From 36c32e4f438a0d73c166ebaec7252648dea69553 Mon Sep 17 00:00:00 2001 From: Markus Humm Date: Tue, 22 Apr 2025 21:46:23 +0200 Subject: [PATCH 01/17] Preliminary fix for crash in GCM encode Attempt to fix bugreport #86 --- Source/DECCipherModes.pas | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Source/DECCipherModes.pas b/Source/DECCipherModes.pas index aa9ef83..a2d5c8b 100644 --- a/Source/DECCipherModes.pas +++ b/Source/DECCipherModes.pas @@ -839,22 +839,31 @@ procedure TDECCipherModes.EncodeCTSx(Source, Dest: PUInt8Array; Size: Integer); end; procedure TDECCipherModes.EncodeGCM(Source, Dest: PUInt8Array; Size: Integer); -var - PlainText, - CipherText : TBytes; +//var +// PlainText, +// CipherText : TBytes; begin if (Size > 0) then begin - PlainText := TBytes(@Source^); - CipherText := TBytes(@Dest^); +// SetLength(PlainText, Size); +// Move(Source^[0], PlainText[0], Size); +// +// SetLength(CipherText, Size); +// Move(Dest^[0], CipherText[0], Size); + +// PlainText := TBytes(@Source^); +// CipherText := TBytes(@Dest^); +// SetLength(CipherText, Size); end else begin - SetLength(PlainText, 0); - SetLength(CipherText, 0); +// SetLength(PlainText, 0); +// SetLength(CipherText, 0); + Size := 0; end; - FGCM.EncodeGCM(PlainText, CipherText, Size); + FGCM.EncodeGCM(Source, Dest, Size); +// FGCM.EncodeGCM(PlainText, CipherText, Size); end; {$IFDEF DEC3_CMCTS} From 6f7dfcc201e2811b4ba1325677410c3ae87b7869 Mon Sep 17 00:00:00 2001 From: Markus Humm Date: Wed, 23 Apr 2025 20:18:54 +0200 Subject: [PATCH 02/17] Fixed GitHub issue #86 Fixed the crash of issue #86 by changing GCM to use poitners instead ot TBytes and added unit test to verify the change --- NOTICE.txt | 3 +- Source/DECCipherModesGCM.pas | 57 +++++++++----- Unit Tests/Tests/TestDECCipherModesGCM.pas | 87 ++++++++++++++++++++++ 3 files changed, 127 insertions(+), 20 deletions(-) diff --git a/NOTICE.txt b/NOTICE.txt index c0041c7..52ab4fc 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -2,7 +2,7 @@ DEC - Delphi Encryption Compendium Version 6.5 Copyright (c) 2018 - 2020 Norman Gallery (ng931884 [at] gmx [dot] de) -Copyright (c) 2016 - 2024 Markus Humm (markus [dot] humm [at] googlemail [dot] com) (main contact) +Copyright (c) 2016 - 2025 Markus Humm (markus [dot] humm [at] googlemail [dot] com) (main contact) Copyright (c) 2008 - 2019 Frederik A. Winkelsdorf (winkelsdorf [at] gmail [dot] com) Copyright (c) 1999 - 2008 Hagen Reddmann (HaReddmann [at] T-Online [dot] de) @@ -30,6 +30,7 @@ alexrayne Stevie danielmarschall Christoph Schneider (Schneider Infosystems AG) +mikerabat Parts of the work loosely based on the works of Wolfgang Erhardt, who is unfortunately dead already. diff --git a/Source/DECCipherModesGCM.pas b/Source/DECCipherModesGCM.pas index 0e81963..033b33a 100644 --- a/Source/DECCipherModesGCM.pas +++ b/Source/DECCipherModesGCM.pas @@ -159,7 +159,7 @@ TGCM = class(TObject) /// /// Result of the XOR operation /// - procedure XOR_ArrayWithT128(const x: TBytes; XIndex, Count: UInt64; y: T128; var Result: TBytes); inline; + procedure XOR_ArrayWithT128(x: PUInt8Array; XIndex, Count: UInt64; y: T128; Result: PUInt8Array); inline; /// /// XORs all elements of the precalculated matrix with the value passed @@ -238,6 +238,9 @@ TGCM = class(TObject) /// Specifys the data for which an authentication value shall be /// calculated. It is allowed to be nil. /// + /// + /// Length of the data to authenticate in byte + /// /// /// Encrypted data used in the calculation /// @@ -247,8 +250,10 @@ TGCM = class(TObject) /// /// Calculated raw hash value which will later get returned as AuthenticatedTag /// - function CalcGaloisHash(AuthenticatedData, Ciphertext : TBytes; CiphertextSize: - Integer): T128; + function CalcGaloisHash(AuthenticatedData : PUInt8Array; + AuthLen : Integer; + Ciphertext : PUInt8Array; + CiphertextSize : Integer): T128; /// /// Encrypts a T128 value using the encryption method specified on init @@ -286,7 +291,7 @@ TGCM = class(TObject) /// Number of bytes to encrypt /// procedure EncodeGCM(Source, - Dest : TBytes; + Dest : PUInt8Array; Size : Integer); /// /// Decodes a block of data using the supplied cipher @@ -359,7 +364,7 @@ function TGCM.XOR_PointerWithT128(const x : Pointer; y : T128): T128; Result[1] := P128(x)^[1] xor y[1]; end; -procedure TGCM.XOR_ArrayWithT128(const x: TBytes; XIndex, Count: UInt64; y: T128; var Result: TBytes); +procedure TGCM.XOR_ArrayWithT128(x: PUInt8Array; XIndex, Count: UInt64; y: T128; Result: PUInt8Array); var i : integer; by : P16ByteArray; @@ -367,7 +372,7 @@ procedure TGCM.XOR_ArrayWithT128(const x: TBytes; XIndex, Count: UInt64; y: T128 by := @y[0]; for i := 0 to Count-1 do begin - Result[XIndex] := x[XIndex] xor by[i]; + Result^[XIndex] := x^[XIndex] xor by[i]; inc(XIndex); end; end; @@ -533,19 +538,19 @@ procedure TGCM.Init(EncryptionMethod : TEncodeDecodeMethod; b^ := 1; end else - FY := CalcGaloisHash(nil, InitVector, length(InitVector)); + FY := CalcGaloisHash(nil, 0, @InitVector[0], length(InitVector)); FEncryptionMethod(@FY[0], @FE_K_Y0[0], 16); end; -function TGCM.CalcGaloisHash(AuthenticatedData, Ciphertext : TBytes; +function TGCM.CalcGaloisHash(AuthenticatedData : PUInt8Array; AuthLen : integer; Ciphertext : PUInt8Array; CiphertextSize: Integer): T128; var AuthCipherLength : T128; x : T128; n : Uint64; - procedure encode(data : TBytes; dataSize: Integer); + procedure encode(data : PUInt8Array; dataSize: Integer); var i, mod_d, div_d, len_d : UInt64; hdata : T128; @@ -559,7 +564,7 @@ function TGCM.CalcGaloisHash(AuthenticatedData, Ciphertext : TBytes; begin for i := 0 to div_d-1 do begin - x := poly_mult_H(XOR_PointerWithT128(@data[n], x )); + x := poly_mult_H(XOR_PointerWithT128(@data^[n], x )); inc(n, 16); end; end; @@ -568,7 +573,7 @@ function TGCM.CalcGaloisHash(AuthenticatedData, Ciphertext : TBytes; if mod_d > 0 then begin hdata := nullbytes; - Move(data[n], hdata[0], mod_d); + Move(data^[n], hdata[0], mod_d); x := poly_mult_H(XOR_T128(hdata, x)); end; end; @@ -576,10 +581,11 @@ function TGCM.CalcGaloisHash(AuthenticatedData, Ciphertext : TBytes; begin x := nullbytes; - encode(AuthenticatedData, length(AuthenticatedData)); - Assert(length(Ciphertext) >= CiphertextSize); + if AuthLen > 0 then + encode(@AuthenticatedData[0], AuthLen); + //Assert(length(Ciphertext) >= CiphertextSize); encode(Ciphertext, CiphertextSize); - SetAuthenticationCipherLength(AuthCipherLength, length(AuthenticatedData) shl 3, CiphertextSize shl 3); + SetAuthenticationCipherLength(AuthCipherLength, AuthLen shl 3, CiphertextSize shl 3); Result := poly_mult_H(XOR_T128(AuthCipherLength, x)); end; @@ -588,6 +594,8 @@ procedure TGCM.DecodeGCM(Source, Dest: TBytes; Size: Integer); var i, j, BlockCount : UInt64; a_tag : T128; + pDataToAuth : PUInt8Array; + pSrc : PUInt8Array; begin i := 0; BlockCount := Size div 16; @@ -602,10 +610,17 @@ procedure TGCM.DecodeGCM(Source, Dest: TBytes; Size: Integer); if i < Size then begin INCR(FY); - XOR_ArrayWithT128(Source, i, UInt64(Size)-i, EncodeT128(FY), Dest); + XOR_ArrayWithT128(@Source[0], i, UInt64(Size)-i, EncodeT128(FY), @Dest[0]); end; - a_tag := XOR_T128(CalcGaloisHash(DataToAuthenticate, Source, Size), FE_K_Y0); + pDataToAuth := nil; + if Length(DataToAuthenticate) > 0 then + pDataToAuth := @DataToAuthenticate[0]; + pSrc := nil; + if Size > 0 then + pSrc := @source[0]; + + a_tag := XOR_T128(CalcGaloisHash(pDataToAuth, Length(DataToAuthenticate), pSrc, Size), FE_K_Y0); Setlength(FCalcAuthenticationTag, FCalcAuthenticationTagLength); if (FCalcAuthenticationTagLength > 0) then @@ -622,10 +637,11 @@ procedure TGCM.DecodeGCM(Source, Dest: TBytes; Size: Integer); // SetLength(plaintext, 0); // NIST FAIL => pt='' end; -procedure TGCM.EncodeGCM(Source, Dest: TBytes; Size: Integer); +procedure TGCM.EncodeGCM(Source, Dest: PUInt8Array; Size: Integer); var i, j, div_len_plain : UInt64; AuthTag : T128; + pDataToAuth : PUInt8Array; begin i := 0; div_len_plain := Size div 16; @@ -634,7 +650,7 @@ procedure TGCM.EncodeGCM(Source, Dest: TBytes; Size: Integer); begin INCR(FY); - P128(@Dest[i])^ := XOR_PointerWithT128(@Source[i], EncodeT128(FY)); + P128(@Dest^[i])^ := XOR_PointerWithT128(@Source^[i], EncodeT128(FY)); inc(i,16); end; @@ -645,7 +661,10 @@ procedure TGCM.EncodeGCM(Source, Dest: TBytes; Size: Integer); XOR_ArrayWithT128(Source, i, UInt64(Size)-i, EncodeT128(FY), Dest); end; - AuthTag := XOR_T128(CalcGaloisHash(DataToAuthenticate, Dest, Size), FE_K_Y0); + pDataToAuth := nil; + if Length(DataToAuthenticate) > 0 then + pDataToAuth := @DataToAuthenticate[0]; + AuthTag := XOR_T128(CalcGaloisHash(pDataToAuth, Length(DataToAuthenticate), @Dest[0], Size), FE_K_Y0); Setlength(FCalcAuthenticationTag, FCalcAuthenticationTagLength); if (FCalcAuthenticationTagLength > 0) then Move(AuthTag[0], FCalcAuthenticationTag[0], FCalcAuthenticationTagLength); diff --git a/Unit Tests/Tests/TestDECCipherModesGCM.pas b/Unit Tests/Tests/TestDECCipherModesGCM.pas index aae0da9..8fc394c 100644 --- a/Unit Tests/Tests/TestDECCipherModesGCM.pas +++ b/Unit Tests/Tests/TestDECCipherModesGCM.pas @@ -241,6 +241,11 @@ TestTDECGCM = class(TTestCase) procedure TestGetStandardAuthenticationTagBitLengths; procedure TestGetExpectedAuthenticationResult; procedure TestSetExpectedAuthenticationResult; + + /// + /// Test for GitHub issue #86 + /// + procedure TestEncodeConstData_86; end; @@ -616,6 +621,88 @@ procedure TestTDECGCM.TestEncode; end; end; +procedure TestTDECGCM.TestEncodeConstData_86; +var cipher : TCipher_AES128; + tag : TBytes; + refPlainText : Array[0..3] of LongWord; + ciphText : Array[0..3] of LongWord; + key : Array[0..3] of LongWord; + iv : Array[0..2] of LongWord; + i : integer; + refCipherText : Array[0..3] of LongWord; + refTag : Array[0..3] of LongWord; + hea : TBytes; + +type + TUINT32Byte = Array[0..3] of Byte; + PUINT32Byte = ^TUINT32Byte; + +// test key/cipher text and IV from an ARM based platform +const cAESKey : Array[0..3] of Longword = ($C939CC13, $397C1D37, $DE6AE0E1, $CB7C423C ); + cAESIV : Array[0..2] of Longword = ($B3D8CC01, $7CBB89B3, $9E0F67E2); + + cPlainText : Array[0..3] of LongWord = ($c3b3c41f, $113a31b7, $3d9a5cd4, $32103069 ); + cCipherText : Array[0..3] of LongWord = ($93FE7D9E, $9BFD1034, $8A5606E5, $CAFA7354 ); + + cExpectedTag : Array[0..3] of LongWord = ($0032A1DC, $85F1C978, $6925A2E7, $1D8272DD); + cAESHea : Array[0..3] of LongWord = ( $24825602, $bd12a984, $e0092d3e, $448eda5f ); + +function InvUINT32( value : UINT32 ) : UINT32; + +var v1, v2 : PUINT32Byte; +begin + v1 := @value; + v2 := @Result; + + v2^[3] := v1^[0]; + v2^[2] := v1^[1]; + v2^[1] := v1^[2]; + v2^[0] := v1^[3]; +end; + +procedure InitKey; +var i : integer; +begin + for i := 0 to High(cAESKey) do + key[i] := InvUINT32(cAESKey[i]); + + for i := 0 to High(cAESIV) do + iv[i] := InvUINT32(cAESIV[i]); + + for i := 0 to High(refPlainText) do + refPlainText[i] := InvUINT32(cPlainText[i]); + + for i := 0 to High(refCipherText) do + refCipherText[i] := InvUINT32(cCipherText[i]); + + for i := 0 to High(cExpectedTag) do + refTag[i] := InvUINT32(cExpectedTag[i]); + + SetLength(hea, sizeof(cAESHea)); + for i := 0 to High(cExpectedTag) do + PLongWord(@hea[i*4])^ := InvUINT32(cAESHea[i]); +end; + +begin + InitKey; + cipher := TCipher_AES128.Create; + try + cipher.Mode := cmGCM; + cipher.Init( key, sizeof(key), iv, sizeof(iv), 0 ); + cipher.AuthenticationResultBitLength := 128; + cipher.DataToAuthenticate := hea; + + cipher.Encode(refPlainText, ciphText, sizeof(refPlainText)); + tag := cipher.CalculatedAuthenticationResult; + finally + cipher.Free; + end; + + Check( CompareMem(@refCipherText[0], @ciphText[0], sizeof(ciphText)), 'Cipher failed'); + Check( CompareMem(@refTag[0], @tag[0], sizeof(refTag)), 'Tag failed'); +end; + + procedure TestTDECGCM.TestGetExpectedAuthenticationResult; var Exp, Act: TBytes; From d1af4edd2da72f8632d9f419afd2c806731a3fcf Mon Sep 17 00:00:00 2001 From: Markus Humm Date: Wed, 23 Apr 2025 20:56:46 +0200 Subject: [PATCH 03/17] Changed DecodeGCM to using pointers instead of TBytes Same kind of change as already done to EncodeGCM --- Source/DECCipherModes.pas | 53 ++++++++-------------- Source/DECCipherModesGCM.pas | 11 +++-- Unit Tests/Tests/TestDECCipherModesGCM.pas | 1 - 3 files changed, 24 insertions(+), 41 deletions(-) diff --git a/Source/DECCipherModes.pas b/Source/DECCipherModes.pas index a2d5c8b..4e3b4ae 100644 --- a/Source/DECCipherModes.pas +++ b/Source/DECCipherModes.pas @@ -839,31 +839,11 @@ procedure TDECCipherModes.EncodeCTSx(Source, Dest: PUInt8Array; Size: Integer); end; procedure TDECCipherModes.EncodeGCM(Source, Dest: PUInt8Array; Size: Integer); -//var -// PlainText, -// CipherText : TBytes; begin - if (Size > 0) then - begin -// SetLength(PlainText, Size); -// Move(Source^[0], PlainText[0], Size); -// -// SetLength(CipherText, Size); -// Move(Dest^[0], CipherText[0], Size); - -// PlainText := TBytes(@Source^); -// CipherText := TBytes(@Dest^); -// SetLength(CipherText, Size); - end - else - begin -// SetLength(PlainText, 0); -// SetLength(CipherText, 0); + if (Size < 0) then Size := 0; - end; FGCM.EncodeGCM(Source, Dest, Size); -// FGCM.EncodeGCM(PlainText, CipherText, Size); end; {$IFDEF DEC3_CMCTS} @@ -948,22 +928,25 @@ procedure TDECCipherModes.DecodeECBx(Source, Dest: PUInt8Array; Size: Integer); end; procedure TDECCipherModes.DecodeGCM(Source, Dest: PUInt8Array; Size: Integer); -var - PlainText, - CipherText : TBytes; +//var +// PlainText, +// CipherText : TBytes; begin - if (Size > 0) then - begin - PlainText := TBytes(@Source^); - CipherText := TBytes(@Dest^); - end - else - begin - SetLength(PlainText, 0); - SetLength(CipherText, 0); - end; +// if (Size > 0) then +// begin +// PlainText := TBytes(@Source^); +// CipherText := TBytes(@Dest^); +// end +// else +// begin +// SetLength(PlainText, 0); +// SetLength(CipherText, 0); +// end; + + if (Size < 0) then + Size := 0; - FGCM.DecodeGCM(PlainText, CipherText, Size); + FGCM.DecodeGCM(Source, Dest, Size); end; procedure TDECCipherModes.DecodeCFB8(Source, Dest: PUInt8Array; Size: Integer); diff --git a/Source/DECCipherModesGCM.pas b/Source/DECCipherModesGCM.pas index 033b33a..9d8cde1 100644 --- a/Source/DECCipherModesGCM.pas +++ b/Source/DECCipherModesGCM.pas @@ -306,7 +306,7 @@ TGCM = class(TObject) /// Number of bytes to decrypt /// procedure DecodeGCM(Source, - Dest : TBytes; + Dest : PUInt8Array; Size : Integer); /// @@ -590,7 +590,7 @@ function TGCM.CalcGaloisHash(AuthenticatedData : PUInt8Array; AuthLen : integer; Result := poly_mult_H(XOR_T128(AuthCipherLength, x)); end; -procedure TGCM.DecodeGCM(Source, Dest: TBytes; Size: Integer); +procedure TGCM.DecodeGCM(Source, Dest: PUInt8Array; Size: Integer); var i, j, BlockCount : UInt64; a_tag : T128; @@ -603,14 +603,14 @@ procedure TGCM.DecodeGCM(Source, Dest: TBytes; Size: Integer); for j := 1 to BlockCount do begin INCR(FY); - P128(@Dest[i])^ := XOR_PointerWithT128(@Source[i], EncodeT128(FY)); + P128(@Dest^[i])^ := XOR_PointerWithT128(@Source^[i], EncodeT128(FY)); inc(i, 16); end; if i < Size then begin INCR(FY); - XOR_ArrayWithT128(@Source[0], i, UInt64(Size)-i, EncodeT128(FY), @Dest[0]); + XOR_ArrayWithT128(@Source^[0], i, UInt64(Size)-i, EncodeT128(FY), @Dest^[0]); end; pDataToAuth := nil; @@ -620,7 +620,8 @@ procedure TGCM.DecodeGCM(Source, Dest: TBytes; Size: Integer); if Size > 0 then pSrc := @source[0]; - a_tag := XOR_T128(CalcGaloisHash(pDataToAuth, Length(DataToAuthenticate), pSrc, Size), FE_K_Y0); + a_tag := XOR_T128(CalcGaloisHash(pDataToAuth, Length(DataToAuthenticate), + pSrc, Size), FE_K_Y0); Setlength(FCalcAuthenticationTag, FCalcAuthenticationTagLength); if (FCalcAuthenticationTagLength > 0) then diff --git a/Unit Tests/Tests/TestDECCipherModesGCM.pas b/Unit Tests/Tests/TestDECCipherModesGCM.pas index 8fc394c..0164956 100644 --- a/Unit Tests/Tests/TestDECCipherModesGCM.pas +++ b/Unit Tests/Tests/TestDECCipherModesGCM.pas @@ -628,7 +628,6 @@ procedure TestTDECGCM.TestEncodeConstData_86; ciphText : Array[0..3] of LongWord; key : Array[0..3] of LongWord; iv : Array[0..2] of LongWord; - i : integer; refCipherText : Array[0..3] of LongWord; refTag : Array[0..3] of LongWord; hea : TBytes; From 7952de6187a797f5e7bbb07e79a5ee556ce2f020 Mon Sep 17 00:00:00 2001 From: Markus Humm Date: Wed, 23 Apr 2025 21:19:56 +0200 Subject: [PATCH 04/17] Fixed crash in unit test setup for Keccak224 Changed GCM part of EncodeBytes as well --- Source/DECCipherFormats.pas | 2 +- Unit Tests/Tests/TestDECHashSHA3.pas | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/DECCipherFormats.pas b/Source/DECCipherFormats.pas index 5a754d7..8495112 100644 --- a/Source/DECCipherFormats.pas +++ b/Source/DECCipherFormats.pas @@ -733,7 +733,7 @@ function TDECFormattedCipher.EncodeBytes(const Source: TBytes): TBytes; Encode(Source[0], Result[0], Length(Source)) else if (FMode = cmGCM) then - EncodeGCM(@Source, @Result, 0); + EncodeGCM(@Source[0], @Result[0], 0); end; begin diff --git a/Unit Tests/Tests/TestDECHashSHA3.pas b/Unit Tests/Tests/TestDECHashSHA3.pas index a427b9a..2f74b56 100644 --- a/Unit Tests/Tests/TestDECHashSHA3.pas +++ b/Unit Tests/Tests/TestDECHashSHA3.pas @@ -1,4 +1,4 @@ -{***************************************************************************** +{***************************************************************************** The DEC team (see file NOTICE.txt) licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance @@ -1755,14 +1755,14 @@ procedure TestTHash_SHA3_Base.AddLastByteForCodeTest(var lDataRow : IHashTest SHA3InputVector : RawByteString; LastByteLength : UInt8); var - LastByteLen : UInt8; +// LastByteLen : UInt8; MsgWithFixup : RawByteString; begin - MsgWithFixup := AddLastByteForKeccakTest(SHA3InputVector, LastByteLen); + MsgWithFixup := AddLastByteForKeccakTest(SHA3InputVector, LastByteLength); //LastByteLen); lDataRow.AddInputVector(MsgWithFixup); - lDataRow.FinalBitLength := LastByteLen; + lDataRow.FinalBitLength := LastByteLength; //LastByteLen; - THash_SHA3Base(FHash).FinalByteLength := LastByteLen; + THash_SHA3Base(FHash).FinalByteLength := LastByteLength; //LastByteLen; lDataRow.ExpectedOutputUTFStrTest := CalcUnicodeHash(string(TFormat_HexL.Encode(MsgWithFixup)), FHash); From 584ff70555c57efeb345c667fea25102dc0cdd1c Mon Sep 17 00:00:00 2001 From: Markus Humm Date: Fri, 2 May 2025 19:51:34 +0200 Subject: [PATCH 05/17] Added options include file --- Demos/Cipher_FMX/MainFormCipherFMX.fmx | 12 ++++++------ Source/DECCipherInterface.pas | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Demos/Cipher_FMX/MainFormCipherFMX.fmx b/Demos/Cipher_FMX/MainFormCipherFMX.fmx index c25082e..c026bfc 100644 --- a/Demos/Cipher_FMX/MainFormCipherFMX.fmx +++ b/Demos/Cipher_FMX/MainFormCipherFMX.fmx @@ -2,8 +2,8 @@ object FormMain: TFormMain Left = 0 Top = 0 Caption = 'FMX Cipher Demo' - ClientHeight = 910 - ClientWidth = 402 + ClientHeight = 908 + ClientWidth = 404 FormFactor.Width = 320 FormFactor.Height = 480 FormFactor.Devices = [Desktop] @@ -13,13 +13,13 @@ object FormMain: TFormMain DesignerMasterStyle = 0 object VertScrollBox1: TVertScrollBox Align = Client - Size.Width = 402.00000000000000000 - Size.Height = 910.00000000000000000 + Size.Width = 404.00000000000000000 + Size.Height = 908.00000000000000000 Size.PlatformDefault = False StyleLookup = 'scrollboxstyle' TabOrder = 6 - Viewport.Width = 386.00000000000000000 - Viewport.Height = 910.00000000000000000 + Viewport.Width = 388.00000000000000000 + Viewport.Height = 908.00000000000000000 object LayoutTop: TLayout Size.Width = 400.00000000000000000 Size.Height = 313.00000000000000000 diff --git a/Source/DECCipherInterface.pas b/Source/DECCipherInterface.pas index 90389bf..1e11c6f 100644 --- a/Source/DECCipherInterface.pas +++ b/Source/DECCipherInterface.pas @@ -26,6 +26,8 @@ interface {$ENDIF} DECTypes, DECCipherBase, DECFormatBase; +{$I DECOptions.inc} + type /// /// Common interface for all ciphers. Some ciphers may have additional From c7267cb9fc5bca1826828c4f178565516222b0fb Mon Sep 17 00:00:00 2001 From: Markus Humm Date: Sun, 25 May 2025 11:38:17 +0200 Subject: [PATCH 06/17] Added CCM block chaining mode and unit tests for it --- Docs/DEC65.pdf | Bin 1211962 -> 1222019 bytes Docs/VersionHistory.pdf | Bin 340781 -> 326801 bytes Source/DEC60.dpr | 4 +- Source/DEC60.dproj | 55 +- Source/DECAuthenticatedCipherModesBase.pas | 230 +++++ Source/DECCipherBase.pas | 9 +- Source/DECCipherFormats.pas | 6 +- Source/DECCipherModes.pas | 173 ++-- Source/DECCipherModesCCM.pas | 399 ++++++++ Source/DECCipherModesGCM.pas | 140 +-- Source/DECOptions.inc | 2 +- Source/DECTypes.pas | 5 + Source/_DECCipherModesGCM.pas | 857 ++++++++++++++++++ Unit Tests/DECDUnitTestSuite.dpr | 5 +- Unit Tests/DECDUnitTestSuite.dproj | 27 +- Unit Tests/DECDUnitXTestSuite.dproj | 17 +- .../AuthenticatedCiphersCommonTestData.pas | 153 ++++ Unit Tests/Tests/TestDECCRC.pas | 2 +- Unit Tests/Tests/TestDECCipherFormats.pas | 2 +- Unit Tests/Tests/TestDECCipherModes.pas | 24 +- Unit Tests/Tests/TestDECCipherModesCCM.pas | 805 ++++++++++++++++ Unit Tests/Tests/TestDECCipherModesGCM.pas | 281 ++---- Unit Tests/Tests/TestDECHashSHA3.pas | 5 +- readme.md | 3 +- 24 files changed, 2817 insertions(+), 387 deletions(-) create mode 100644 Source/DECAuthenticatedCipherModesBase.pas create mode 100644 Source/DECCipherModesCCM.pas create mode 100644 Source/_DECCipherModesGCM.pas create mode 100644 Unit Tests/Tests/AuthenticatedCiphersCommonTestData.pas create mode 100644 Unit Tests/Tests/TestDECCipherModesCCM.pas diff --git a/Docs/DEC65.pdf b/Docs/DEC65.pdf index 1a54b73b51d2950f71424720a09cd4c1eec50832..ba3900399d3d4673d252bcb11ab8e1c98846a18d 100644 GIT binary patch delta 362023 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�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

H5*V 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 WcyeWC3NbPNJ&a4A*G~*0^cC| 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 `i3769bAGE*4!gT4#JDrxxNNv>BX%& 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

FDataSize : Integer; - private + + /// + /// Sets the desired padding mode, means how block ciphers treat + /// incompletely filled last blocks. + /// procedure SetPaddingMode(const Value: TPaddingMode); strict protected /// diff --git a/Source/DECCipherFormats.pas b/Source/DECCipherFormats.pas index 8495112..20e5c14 100644 --- a/Source/DECCipherFormats.pas +++ b/Source/DECCipherFormats.pas @@ -733,7 +733,7 @@ function TDECFormattedCipher.EncodeBytes(const Source: TBytes): TBytes; Encode(Source[0], Result[0], Length(Source)) else if (FMode = cmGCM) then - EncodeGCM(@Source[0], @Result[0], 0); + EncodeGCM(nil, nil, 0); end; begin @@ -756,7 +756,7 @@ function TDECFormattedCipher.DecodeBytes(const Source: TBytes): TBytes; end else if (FMode = cmGCM) then - DecodeGCM(@Source, @Result, 0); + DecodeGCM(nil, nil, 0); if not (FPaddingClass = nil) then Result := FPaddingClass.RemovePadding(Result, Context.BlockSize); @@ -784,7 +784,7 @@ procedure TDECFormattedCipher.DoEncodeDecodeStream(const Source, Dest: TStream; StartPos := Pos; doPadding := false; doStartOnlyPadding := (DataSize = 0) and IsEncode and - (FPaddingMode <> pmNone); + (FPaddingMode <> pmNone); if (DataSize > 0) or doStartOnlyPadding then begin diff --git a/Source/DECCipherModes.pas b/Source/DECCipherModes.pas index 4e3b4ae..e4673bd 100644 --- a/Source/DECCipherModes.pas +++ b/Source/DECCipherModes.pas @@ -26,7 +26,8 @@ interface {$ELSE} System.SysUtils, {$ENDIF} - DECTypes, DECCipherBase, DECCipherModesGCM, DECCipherInterface; + DECTypes, DECCipherBase, DECCipherModesGCM, DECCipherModesCCM, + DECCipherInterface; type /// @@ -143,6 +144,11 @@ TDECCipherModes = class(TDECCipher, IDECAuthenticatedCipher) /// FGCM : TGCM; /// + /// Implementation of the Counter with CBC-MAC mode. Only created when + /// gmCCM is set as mode. + /// + FCCM : TCCM; + /// /// Raises an EDECCipherException exception and provides the correct value /// for block size in that message /// @@ -239,6 +245,12 @@ TDECCipherModes = class(TDECCipher, IDECAuthenticatedCipher) /// source length is 0. /// procedure EncodeGCM(Source, Dest: PUInt8Array; Size: Integer); virtual; + /// + /// Counter with CBC-MAC Mode: encryption with addtional optional authentication. + /// Implemented in its own unit, but needed here to be callable even if + /// source length is 0. + /// + procedure EncodeCCM(Source, Dest: PUInt8Array; Size: Integer); virtual; {$IFDEF DEC3_CMCTS} /// /// double CBC, with @@ -326,6 +338,10 @@ TDECCipherModes = class(TDECCipher, IDECAuthenticatedCipher) /// Galois Counter Mode, details are implemented in DECCipherModesGCM /// procedure DecodeGCM(Source, Dest: PUInt8Array; Size: Integer); virtual; + /// + /// Counter with CBC-MAC Mode, details are implemented in DECCipherModesCCM + /// + procedure DecodeCCM(Source, Dest: PUInt8Array; Size: Integer); virtual; {$IFDEF DEC3_CMCTS} /// /// double CBC @@ -400,7 +416,7 @@ TDECCipherModes = class(TDECCipher, IDECAuthenticatedCipher) /// in addition to encrypting it. This property contains the data which /// shall be authenticated in parallel to the encryption. Some authenticated /// modes still generate an authentication result even if no additional - /// data is supplied via this property, e.g. cmGCM is one of those. + /// data is supplied via this property, e.g. cmGCM or cmCCM are such ones. /// property DataToAuthenticate : TBytes read GetDataToAuthenticate @@ -410,10 +426,8 @@ TDECCipherModes = class(TDECCipher, IDECAuthenticatedCipher) /// Some block chaining modes have the ability to authenticate the message /// in addition to encrypting it. /// Represents the length of CalculatedAuthenticationResult in bit, values - /// as per specification are: 128, 120, 112, 104, or 96 bit. For certain - /// applications, they may be 64 or 32 as well, but the use of these two - /// tag lengths constrains the length of the input data and the lifetime - /// of the key. + /// as per specification can be retrieved via + /// GetStandardAuthenticationTagBitLengths method ///
property AuthenticationResultBitLength : Integer read GetAuthenticationResultBitLength @@ -477,27 +491,33 @@ procedure TDECCipherModes.ReportInvalidMessageLength(Cipher: TDECCipher); procedure TDECCipherModes.SetDataToAuthenticate(const Value: TBytes); begin - if (FMode = cmGCM) then - FGCM.DataToAuthenticate := Value - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM']); + case FMode of + cmGCM: FGCM.DataToAuthenticate := Value; + cmCCM: FCCM.DataToAuthenticate := Value; + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); + end; end; procedure TDECCipherModes.SetExpectedAuthenticationResult(const Value: TBytes); begin - if (FMode = cmGCM) then - FGCM.ExpectedAuthenticationTag := Value - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM']); + case FMode of + cmGCM: FGCM.ExpectedAuthenticationTag := Value; + cmCCM: FCCM.ExpectedAuthenticationTag := Value; + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); + end; end; procedure TDECCipherModes.SetAuthenticationResultBitLength( const Value: Integer); begin - if (FMode = cmGCM) then - FGCM.AuthenticationTagBitLength := Value - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM']); + case FMode of + cmGCM: FGCM.AuthenticationTagBitLength := Value; + cmCCM: FCCM.AuthenticationTagBitLength := Value; + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); + end; end; procedure TDECCipherModes.Encode(const Source; var Dest; DataSize: Integer); @@ -518,6 +538,7 @@ procedure TDECCipherModes.Encode(const Source; var Dest; DataSize: Integer); cmCFS8: EncodeCFS8(@Source, @Dest, DataSize); cmCFSx: EncodeCFSx(@Source, @Dest, DataSize); cmGCM : EncodeGCM(@Source, @Dest, DataSize); + cmCCM : EncodeCCM(@Source, @Dest, DataSize); end; end; @@ -689,24 +710,29 @@ procedure TDECCipherModes.EncodeOFBx(Source, Dest: PUInt8Array; Size: Integer); function TDECCipherModes.GetDataToAuthenticate: TBytes; begin - if (FMode = cmGCM) then - Result := FGCM.DataToAuthenticate - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM']); + case FMode of + cmGCM: Result := FGCM.DataToAuthenticate; + cmCCM: Result := FCCM.DataToAuthenticate; + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cCCM']); + end; end; function TDECCipherModes.GetExpectedAuthenticationResult: TBytes; begin - if (FMode = cmGCM) then - Result := FGCM.ExpectedAuthenticationTag - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM']); + case FMode of + cmGCM: Result := FGCM.ExpectedAuthenticationTag; + cmCCM: Result := FCCM.ExpectedAuthenticationTag; + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); + end; end; function TDECCipherModes.GetStandardAuthenticationTagBitLengths: TStandardBitLengths; begin case FMode of cmGCM: Result := FGCM.GetStandardAuthenticationTagBitLengths; + cmCCM: Result := FCCM.GetStandardAuthenticationTagBitLengths; else begin SetLength(Result, 1); @@ -717,35 +743,49 @@ function TDECCipherModes.GetStandardAuthenticationTagBitLengths: TStandardBitLen function TDECCipherModes.GetAuthenticationResultBitLength: Integer; begin - if (FMode = cmGCM) then - Result := FGCM.AuthenticationTagBitLength - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM']); + case FMode of + cmGCM: Result := FGCM.AuthenticationTagBitLength; + cmCCM: Result := FCCM.AuthenticationTagBitLength; + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); + end; end; function TDECCipherModes.GetCalcAuthenticatonResult: TBytes; begin - if (FMode = cmGCM) then - Result := FGCM.CalculatedAuthenticationTag - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM']); + case FMode of + cmGCM: Result := FGCM.CalculatedAuthenticationTag; + cmCCM: Result := FCCM.CalculatedAuthenticationTag; + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); + end; end; procedure TDECCipherModes.InitMode; begin - if FMode = TCipherMode.cmGCM then + if FMode in [TCipherMode.cmGCM, TCipherMode.cmCCM] then begin - if Context.BlockSize = 16 then - FGCM := TGCM.Create + if (Context.BlockSize = 16) then + begin + case FMode of + cmGCM: FGCM := TGCM.Create; + cmCCM: FCCM := TCCM.Create; + end; + end else - // GCM requires a cipher with 128 bit block size + // GCM and CCM require a cipher with 128 bit block size raise EDECCipherException.CreateResFmt(@sInvalidBlockSize, [128, GetEnumName(TypeInfo(TCipherMode), Integer(FMode))]); end else + begin if Assigned(FGCM) then FreeAndNil(FGCM); + + if Assigned(FCCM) then + FreeAndNil(FCCM); + end; end; procedure TDECCipherModes.EncodeCFSx(Source, Dest: PUInt8Array; Size: Integer); @@ -843,7 +883,15 @@ procedure TDECCipherModes.EncodeGCM(Source, Dest: PUInt8Array; Size: Integer); if (Size < 0) then Size := 0; - FGCM.EncodeGCM(Source, Dest, Size); + FGCM.Encode(Source, Dest, Size); +end; + +procedure TDECCipherModes.EncodeCCM(Source, Dest: PUInt8Array; Size: Integer); +begin + if (Size < 0) then + Size := 0; + + FCCM.Encode(Source, Dest, Size); end; {$IFDEF DEC3_CMCTS} @@ -889,6 +937,7 @@ procedure TDECCipherModes.Decode(const Source; var Dest; DataSize: Integer); cmCFS8: DecodeCFS8(@Source, @Dest, DataSize); cmCFSx: DecodeCFSx(@Source, @Dest, DataSize); cmGCM : DecodeGCM(@Source, @Dest, DataSize); + cmCCM : DecodeCCM(@Source, @Dest, DataSize); end; end; @@ -928,25 +977,19 @@ procedure TDECCipherModes.DecodeECBx(Source, Dest: PUInt8Array; Size: Integer); end; procedure TDECCipherModes.DecodeGCM(Source, Dest: PUInt8Array; Size: Integer); -//var -// PlainText, -// CipherText : TBytes; begin -// if (Size > 0) then -// begin -// PlainText := TBytes(@Source^); -// CipherText := TBytes(@Dest^); -// end -// else -// begin -// SetLength(PlainText, 0); -// SetLength(CipherText, 0); -// end; + if (Size < 0) then + Size := 0; + + FGCM.Decode(Source, Dest, Size); +end; +procedure TDECCipherModes.DecodeCCM(Source, Dest: PUInt8Array; Size: Integer); +begin if (Size < 0) then Size := 0; - FGCM.DecodeGCM(Source, Dest, Size); + FCCM.Decode(Source, Dest, Size); end; procedure TDECCipherModes.DecodeCFB8(Source, Dest: PUInt8Array; Size: Integer); @@ -1095,6 +1138,7 @@ procedure TDECCipherModes.DecodeOFBx(Source, Dest: PUInt8Array; Size: Integer); destructor TDECCipherModes.Destroy; begin FGCM.Free; + FCCM.Free; inherited; end; @@ -1103,11 +1147,18 @@ procedure TDECCipherModes.Done; begin inherited; - if (FMode = cmGCM) then - begin - if (length(FGCM.ExpectedAuthenticationTag) > 0) and - (not IsEqual(FGCM.ExpectedAuthenticationTag, FGCM.CalculatedAuthenticationTag)) then - raise EDECCipherAuthenticationException.CreateRes(@sInvalidAuthenticationValue); + case FMode of + cmGCM : begin + if (length(FGCM.ExpectedAuthenticationTag) > 0) and + (not IsEqual(FGCM.ExpectedAuthenticationTag, FGCM.CalculatedAuthenticationTag)) then + raise EDECCipherAuthenticationException.CreateRes(@sInvalidAuthenticationValue); + end; + + cmCCM : begin + if (length(FCCM.ExpectedAuthenticationTag) > 0) and + (not IsEqual(FCCM.ExpectedAuthenticationTag, FCCM.CalculatedAuthenticationTag)) then + raise EDECCipherAuthenticationException.CreateRes(@sInvalidAuthenticationValue); + end; end; end; @@ -1115,8 +1166,10 @@ procedure TDECCipherModes.OnAfterInitVectorInitialization(const OriginalInitVect begin inherited; - if (FMode = cmGCM) then - FGCM.Init(self.DoEncode, OriginalInitVector); + case FMode of + cmGCM: FGCM.Init(self.DoEncode, OriginalInitVector); + cmCCM: FCCM.Init(self.DoEncode, OriginalInitVector); + end; end; procedure TDECCipherModes.DecodeCFSx(Source, Dest: PUInt8Array; Size: Integer); diff --git a/Source/DECCipherModesCCM.pas b/Source/DECCipherModesCCM.pas new file mode 100644 index 0000000..3f835f6 --- /dev/null +++ b/Source/DECCipherModesCCM.pas @@ -0,0 +1,399 @@ +{***************************************************************************** + The DEC team (see file NOTICE.txt) licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. A copy of this licence is found in the root directory + of this project in the file LICENCE.txt or alternatively at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*****************************************************************************} + +// Based on: aes_ccm.pas from Wolfgang Erhard +unit DECCipherModesCCM; + +interface + +{$INCLUDE DECOptions.inc} + +uses + {$IFDEF FPC} + SysUtils, + {$ELSE} + System.SysUtils, + {$ENDIF} + DECTypes, + DECAuthenticatedCipherModesBase; + +resourcestring + sWrongNonceLength = 'Wrong nonce/IV length. Must be between 7 and 13'; + sWrongNonceLengthDetailed = 'Nonce longer than 15 byte. Act. length: %0:d'; + sWrongCCMAuthLength = 'CCM authentication tag needs to have a length of ' + + '4, 6, 8, 10, 12, 14 or 16 byte'; + +type + /// + /// Counter with CBC-MAC Mode specific methods + /// + TCCM = class(TAuthenticatedCipherModesBase) + strict private + /// + /// Unmodified initialization vector + /// + FOrigInitVector : TBytes; + /// + /// Init vector which is modified during processing + /// + FInitVector : TBlock16Byte; + + /// + /// Encodes or decodes a block of data using the supplied cipher + /// + /// + /// Plain text to encrypt or decrypt, depending on encode parameter + /// + /// + /// Ciphertext or plaintext after encryption or decryption, depending on + /// encode parameter + /// + /// + /// Number of bytes to encrypt or decrypt + /// + /// + /// When true it is encrypting data, else it is descrypting data + /// + procedure EncodeDecode(Source, Dest: PUInt8Array; Size: Integer; Encode: Boolean); + strict protected + /// + /// Defines the length of the resulting authentication value in bit. + /// + /// + /// Sets the length of Authenticaton_tag in bit, values as per specification + /// are: 32, 48, 64, 80, 96, 112, 128 + /// + procedure SetAuthenticationTagLength(const Value: UInt32); override; + public + /// + /// Savely clear any buffers + /// + destructor Destroy; override; + /// + /// Should be called when starting encryption/decryption in order to + /// initialize internal tables etc. + /// + /// + /// Encryption method of the cypher used + /// + /// + /// Initialization vector + /// + procedure Init(EncryptionMethod : TEncodeDecodeMethod; + InitVector : TBytes); override; + + /// + /// Encodes a block of data using the supplied cipher + /// + /// + /// Plain text to encrypt + /// + /// + /// Ciphertext after encryption + /// + /// + /// Number of bytes to encrypt + /// + procedure Encode(Source, + Dest : PUInt8Array; + Size : Integer); override; + /// + /// Decodes a block of data using the supplied cipher + /// + /// + /// Encrypted ciphertext to decrypt + /// + /// + /// Plaintext after decryption + /// + /// + /// Number of bytes to decrypt + /// + procedure Decode(Source, + Dest : PUInt8Array; + Size : Integer); override; + + /// + /// Returns a list of authentication tag lengths explicitely specified by + /// the official specification of the standard. + /// + /// + /// List of bit lengths + /// + function GetStandardAuthenticationTagBitLengths:TStandardBitLengths; override; + end; + +implementation + +uses + DECUtil; + +const + /// + /// Size of one block to be processed in byte + /// + cBlockSize = SizeOf(TBlock16Byte); + +resourcestring + /// + /// Exception raised when a size but no source data pointer was passed + /// + sInvalidSourcePointer = 'No source data pointer passed'; + +procedure TCCM.Decode(Source, Dest: PUInt8Array; Size: Integer); +begin + EncodeDecode(Source, Dest, Size, false); +end; + +destructor TCCM.Destroy; +begin + if (Length(FOrigInitVector) > 0) then + ProtectBytes(FOrigInitVector); + + ProtectBuffer(FInitVector, SizeOf(FInitVector)); + ProtectBytes(FCalcAuthenticationTag); + ProtectBytes(FExpectedAuthenticationTag); + + inherited; +end; + +procedure TCCM.Encode(Source, Dest: PUInt8Array; Size: Integer); +begin + EncodeDecode(Source, Dest, Size, true); +end; + +procedure TCCM.EncodeDecode(Source, Dest: PUInt8Array; + Size: Integer; + Encode: Boolean); +var + ecc : TBlock16Byte; // encrypted counter + FixedTagBuf : TBlock16Byte; // during calculation buffer of authentication tag + // might need to be bigger than then one specified + // by the user + len : Int32; + k, L : UInt16; + b : UInt8; + pb : PByte; + AuthDataLen : Integer; // Length of data to authenticate in bytes + InitVectLen : Integer; // Length of the init vector in bytes + + Buf : TBlock16Byte; + + // Increment CTR[15]..CTR[16-L] + procedure IncCTR(var CTR: TBlock16Byte); + var + j: integer; + begin + for j := 15 downto 16-L do + begin + if (CTR[j] = $FF) then + CTR[j] := 0 + else + begin + inc(CTR[j]); + exit; + end; + end; + end; + +begin + if (Size > 0) and + ((not Assigned(Source)) or (not Assigned(Dest))) then + raise EDECCipherException.Create(sInvalidSourcePointer); + + AuthDataLen := Length(FDataToAuthenticate); + InitVectLen := Length(FOrigInitVector); + + // calculate L value = max(number of bytes needed for sLen, 15-nLen) + len := Size; + L := 0; + while (len > 0) do + begin + inc(L); + len := len shr 8; + end; + + // Length of nonce (= init vector) is InitVectLen + if (InitVectLen + L > 15) then + raise EDECNonceLengthException.CreateFmt(sWrongNonceLengthDetailed, + [InitVectLen + L]); + + // Force Length(FInitVector) + L = 15. Since nLen <= 13, L is at least 2 + L := 15 - InitVectLen; + + // compose B_0 = Flags | Nonce N | l(m) + // octet 0: Flags = 64*HdrPresent | 8*((tLen-2) div 2 | (L-1) + + if (AuthDataLen > 0) then + b := 64 + else + b := 0; + + // Typecast for L-1 possible, since L is at least 2, see comment above + Buf[0] := b or ((FCalcAuthenticationTagLength-2) shl 2) or UInt16(L-1); + // octets 1..15-L is nonce + pb := @FOrigInitvector[0]; + for k := 1 to 15-L do + begin + Buf[k] := pb^; + inc(pb); + end; + + // octets 16-L .. 15: l(m) + len := Size; + for k := 1 to L do + begin + Buf[16-k] := len and $FF; + len := len shr 8; + end; + + FEncryptionMethod(@Buf[0], @Buf[0], Length(Buf)); + + // process header + if (AuthDataLen > 0) then + begin + // octets 0..1: encoding of hLen. Note: since we allow max $FEFF bytes + // only these two octets are used. Generally up to 10 octets are needed. + Buf[0] := Buf[0] xor (AuthDataLen shr 8); + Buf[1] := Buf[1] xor (AuthDataLen and $FF); + // now append the hdr data + len := 2; + pb := @FDataToAuthenticate[0]; + for k:= 1 to AuthDataLen do + begin + if (len = 16) then + begin + FEncryptionMethod(@Buf[0], @Buf[0], Length(Buf)); + len := 0; + end; + Buf[len] := Buf[len] xor pb^; + inc(len); + inc(pb); + end; + + if (len <> 0) then + FEncryptionMethod(@Buf[0], @Buf[0], Length(Buf)); + end; + + // setup the counter for source text processing + pb := @FOrigInitVector[0]; + FInitVector[0] := (L-1) and $FF; + for k := 1 to 15 do + begin + if (k < 16-L) then + begin + FInitVector[k] := pb^; + inc(pb); + end + else + FInitVector[k] := 0; + end; + + // process full source text blocks + while (Size >= 16) do + begin + IncCTR(FInitVector); + FEncryptionMethod(@FInitVector[0], @ecc[0], Length(FInitVector)); + + if Encode then + begin + XORBuffers(Source[0], Buf[0], 16, Buf[0]); + XORBuffers(Source[0], ecc[0], 16, Dest[0]); + end + else + begin + XORBuffers(Source[0], ecc[0], 16, Dest[0]); + XORBuffers(Dest[0], Buf[0], 16, Buf[0]); + end; + + FEncryptionMethod(@Buf[0], @Buf[0], Length(Buf)); + + inc(PByte(Source), cBlockSize); + inc(PByte(Dest), cBlockSize); + dec(Size, cBlockSize); + end; + + if (Size > 0) then + begin + // handle remaining bytes of source text + IncCTR(FInitVector); + + FEncryptionMethod(@FInitVector[0], @ecc[0], Length(ecc)); + + for k := 0 to UInt16(Size - 1) do + begin + if Encode then + begin + b := PByte(Source)^; + PByte(Dest)^ := b xor ecc[k]; + end + else + begin + b := PByte(Source)^ xor ecc[k]; + PByte(Dest)^ := b; + end; + Buf[k] := Buf[k] xor b; + inc(PByte(Source)); + inc(PByte(Dest)); + end; + + FEncryptionMethod(@Buf[0], @Buf[0], Length(Buf)); + end; + + // setup counter for the tag (zero the count) + for k := 15 downto 16-L do + FInitVector[k] := 0; + + FEncryptionMethod(@FInitVector[0], @ecc[0], Length(ecc)); + + // store the TAG/Authentication result value + XORBuffers(Buf[0], ecc[0], 16, FixedTagBuf); + Move(FixedTagBuf[0], FCalcAuthenticationTag[0], length(FCalcAuthenticationTag)); + + ProtectBuffer(Buf, SizeOf(Buf)); +end; + +function TCCM.GetStandardAuthenticationTagBitLengths: TStandardBitLengths; +begin + SetLength(Result, 7); + Result := [32, 48, 64, 80, 96, 112, 128]; +end; + +procedure TCCM.SetAuthenticationTagLength(const Value: UInt32); +begin + if not (Value in [32, 48, 64, 80, 96, 112, 128]) then + raise EDECAuthLengthException.Create(sWrongCCMAuthLength); + + FCalcAuthenticationTagLength := Value shr 3; + + SetLength(FCalcAuthenticationTag, FCalcAuthenticationTagLength); +end; + +procedure TCCM.Init(EncryptionMethod : TEncodeDecodeMethod; + InitVector : TBytes); +begin + Assert(Length(InitVector) > 0, 'No init vector specified'); + + if (Length(InitVector) < 7) or (Length(InitVector) > 13) then + raise EDECNonceLengthException.Create(sWrongNonceLength); + + inherited; + + FOrigInitVector := InitVector; +end; + +end. diff --git a/Source/DECCipherModesGCM.pas b/Source/DECCipherModesGCM.pas index 9d8cde1..ed2cde5 100644 --- a/Source/DECCipherModesGCM.pas +++ b/Source/DECCipherModesGCM.pas @@ -26,7 +26,8 @@ interface {$ELSE} System.SysUtils, {$ENDIF} - DECTypes; + DECTypes, + DECAuthenticatedCipherModesBase; type /// @@ -47,28 +48,11 @@ interface /// P16ByteArray = ^T16ByteArray; - /// - /// A methopd of this type needs to be supplied for encrypting or decrypting - /// a block via this GCM algorithm. The method is implemented as a parameter, - /// to avoid the need to bring TGCM in the inheritance chain. TGCM thus can - /// be used for composition instead of inheritance. - /// - /// - /// Data to be encrypted - /// - /// - /// In this memory the encrypted result will be written - /// - /// - /// Size of source in byte - /// - TEncodeDecodeMethod = procedure(Source, Dest: Pointer; Size: Integer) of Object; - /// /// Galois Counter Mode specific methods /// - TGCM = class(TObject) - private + TGCM = class(TAuthenticatedCipherModesBase) + strict private /// /// Empty value? /// @@ -91,29 +75,6 @@ TGCM = class(TObject) ///
FE_K_Y0 : T128; - /// - /// The data which shall be authenticated in parallel to the encryption - /// - FDataToAuthenticate : TBytes; - /// - /// Length of the authentication tag to generate in byte - /// - FCalcAuthenticationTagLength : UInt32; - /// - /// Generated authentication tag - /// - FCalcAuthenticationTag : TBytes; - /// - /// Expected authentication tag value, will be compared with actual value - /// when decryption finished. - /// - FExpectedAuthenticationTag : TBytes; - - /// - /// Reference to the encode method of the actual cipher used - /// - FEncryptionMethod : TEncodeDecodeMethod; - /// /// XOR implementation for unsigned 128 bit numbers /// @@ -213,24 +174,6 @@ TGCM = class(TObject) /// procedure INCR(var Y : T128); - /// - /// Defines the length of the resulting authentication value in bit. - /// - /// - /// Sets the length of Authenticaton_tag in bit, values as per specification - /// are: 128, 120, 112, 104, or 96 bit. For certain applications, they - /// may be 64 or 32 as well, but the use of these two tag lengths - /// constrains the length of the input data and the lifetime of the key. - /// - procedure SetAuthenticationTagLength(const Value: UInt32); - /// - /// Returns the length of the calculated authehtication value in bit - /// - /// - /// Length of the calculated authentication value in bit - /// - function GetAuthenticationTagBitLength: UInt32; - /// /// Calculates the hash value /// @@ -265,6 +208,17 @@ TGCM = class(TObject) /// Encrypted value /// function EncodeT128(Value: T128): T128; + strict protected + /// + /// Defines the length of the resulting authentication value in bit. + /// + /// + /// Sets the length of Authenticaton_tag in bit, values as per specification + /// are: 128, 120, 112, 104, or 96 bit. For certain applications, they + /// may be 64 or 32 as well, but the use of these two tag lengths + /// constrains the length of the input data and the lifetime of the key. + /// + procedure SetAuthenticationTagLength(const Value: UInt32); override; public /// /// Should be called when starting encryption/decryption in order to @@ -277,7 +231,7 @@ TGCM = class(TObject) /// Initialization vector /// procedure Init(EncryptionMethod : TEncodeDecodeMethod; - InitVector : TBytes); + InitVector : TBytes); override; /// /// Encodes a block of data using the supplied cipher /// @@ -290,9 +244,9 @@ TGCM = class(TObject) /// /// Number of bytes to encrypt /// - procedure EncodeGCM(Source, - Dest : PUInt8Array; - Size : Integer); + procedure Encode(Source, + Dest : PUInt8Array; + Size : Integer); override; /// /// Decodes a block of data using the supplied cipher /// @@ -305,9 +259,9 @@ TGCM = class(TObject) /// /// Number of bytes to decrypt /// - procedure DecodeGCM(Source, - Dest : PUInt8Array; - Size : Integer); + procedure Decode(Source, + Dest : PUInt8Array; + Size : Integer); override; /// /// Returns a list of authentication tag lengths explicitely specified by @@ -316,38 +270,7 @@ TGCM = class(TObject) /// /// List of bit lengths /// - function GetStandardAuthenticationTagBitLengths:TStandardBitLengths; - - /// - /// The data which shall be authenticated in parallel to the encryption - /// - property DataToAuthenticate : TBytes - read FDataToAuthenticate - write FDataToAuthenticate; - /// - /// Sets the length of AuthenticatonTag in bit, values as per official - /// specification are: 128, 120, 112, 104, or 96 bit. For certain - /// applications, they may be 64 or 32 as well, but the use of these two - /// tag lengths constrains the length of the input data and the lifetime - /// of the key. - /// - property AuthenticationTagBitLength : UInt32 - read GetAuthenticationTagBitLength - write SetAuthenticationTagLength; - /// - /// Calculated authentication value - /// - property CalculatedAuthenticationTag : TBytes - read FCalcAuthenticationTag - write FCalcAuthenticationTag; - - /// - /// Expected authentication tag value, will be compared with actual value - /// when decryption finished. - /// - property ExpectedAuthenticationTag : TBytes - read FExpectedAuthenticationTag - write FExpectedAuthenticationTag; + function GetStandardAuthenticationTagBitLengths:TStandardBitLengths; override; end; implementation @@ -511,13 +434,7 @@ procedure TGCM.Init(EncryptionMethod : TEncodeDecodeMethod; b : ^Byte; OldH : T128; begin - Assert(Assigned(EncryptionMethod), 'No encryption method specified'); - - // Clear calculated authentication value - if (Length(FCalcAuthenticationTag) > 0) then - FillChar(FCalcAuthenticationTag[0], Length(FCalcAuthenticationTag), #0); - - FEncryptionMethod := EncryptionMethod; + inherited; Nullbytes[0] := 0; Nullbytes[1] := 0; @@ -590,7 +507,7 @@ function TGCM.CalcGaloisHash(AuthenticatedData : PUInt8Array; AuthLen : integer; Result := poly_mult_H(XOR_T128(AuthCipherLength, x)); end; -procedure TGCM.DecodeGCM(Source, Dest: PUInt8Array; Size: Integer); +procedure TGCM.Decode(Source, Dest: PUInt8Array; Size: Integer); var i, j, BlockCount : UInt64; a_tag : T128; @@ -638,7 +555,7 @@ procedure TGCM.DecodeGCM(Source, Dest: PUInt8Array; Size: Integer); // SetLength(plaintext, 0); // NIST FAIL => pt='' end; -procedure TGCM.EncodeGCM(Source, Dest: PUInt8Array; Size: Integer); +procedure TGCM.Encode(Source, Dest: PUInt8Array; Size: Integer); var i, j, div_len_plain : UInt64; AuthTag : T128; @@ -676,11 +593,6 @@ function TGCM.EncodeT128(Value: T128): T128; FEncryptionMethod(@Value[0], @Result[0], 16); end; -function TGCM.GetAuthenticationTagBitLength: UInt32; -begin - Result := FCalcAuthenticationTagLength shl 3; -end; - function TGCM.GetStandardAuthenticationTagBitLengths: TStandardBitLengths; begin SetLength(Result, 5); diff --git a/Source/DECOptions.inc b/Source/DECOptions.inc index da05d4b..eceec52 100644 --- a/Source/DECOptions.inc +++ b/Source/DECOptions.inc @@ -119,7 +119,7 @@ /// {$IF DECLARED(FireMonkeyVersion)} {$DEFINE FMXTranslateableExceptions} -{$IFEND} { TODO: convert to $ENDIF when raising minimum supported version to XE4+} +{$ENDIF} //------------------------------------------------------------------------------ // Do NOT change anything below! //------------------------------------------------------------------------------ diff --git a/Source/DECTypes.pas b/Source/DECTypes.pas index 9c7ec0b..9fe62d5 100644 --- a/Source/DECTypes.pas +++ b/Source/DECTypes.pas @@ -53,6 +53,11 @@ interface PUInt64Array = ^TUInt64Array; TUInt64Array = array[0..1023] of UInt64; + /// + /// AES works on 16 byte blocks, some others too + /// + TBlock16Byte = Array[0..15] of UInt8; + /// /// Declared here because it is used by Blowfish cipher and BCrypt hash /// diff --git a/Source/_DECCipherModesGCM.pas b/Source/_DECCipherModesGCM.pas new file mode 100644 index 0000000..d378e89 --- /dev/null +++ b/Source/_DECCipherModesGCM.pas @@ -0,0 +1,857 @@ +{***************************************************************************** + The DEC team (see file NOTICE.txt) licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. A copy of this licence is found in the root directory + of this project in the file LICENCE.txt or alternatively at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*****************************************************************************} +unit DECCipherModesGCM; + +interface + +{$INCLUDE DECOptions.inc} + +uses + {$IFDEF FPC} + SysUtils, + {$ELSE} + System.SysUtils, + {$ENDIF} + DECTypes; + +type + /// + /// 128 bit unsigned integer + /// + T128 = array[0..1] of UInt64; + /// + /// pointer to an 128 bit unsigned integer + /// + P128 = ^T128; + + /// + /// Array of 16 bytes + /// + T16ByteArray = array[0..15] of Byte; + /// + /// Pointer to an array of 16 bytes + /// + P16ByteArray = ^T16ByteArray; + + /// + /// A methopd of this type needs to be supplied for encrypting or decrypting + /// a block via this GCM algorithm. The method is implemented as a parameter, + /// to avoid the need to bring TGCM in the inheritance chain. TGCM thus can + /// be used for composition instead of inheritance. + /// + /// + /// Data to be encrypted + /// + /// + /// In this memory the encrypted result will be written + /// + /// + /// Size of source in byte + /// + TEncodeDecodeMethod = procedure(Source, Dest: Pointer; Size: Integer) of Object; + + /// + /// Galois Counter Mode specific methods + /// + TGCM = class(TObject) + private + /// + /// Empty value? + /// + nullbytes : T128; + /// + /// Table with precalculated values + /// + FM : array[0..15,0..255] of T128; + + /// + /// Required for creating the table and encryption at least + /// + FH : T128; + /// + /// Calculated in initialization + /// + FY : T128; + /// + /// Calculated in initialization + /// + FE_K_Y0 : T128; + + /// + /// The data which shall be authenticated in parallel to the encryption + /// + FDataToAuthenticate : TBytes; + /// + /// Length of the authentication tag to generate in byte + /// + FCalcAuthenticationTagLength : UInt32; + /// + /// Generated authentication tag + /// + FCalcAuthenticationTag : TBytes; + /// + /// Expected authentication tag value, will be compared with actual value + /// when decryption finished. + /// + FExpectedAuthenticationTag : TBytes; + + /// + /// Reference to the encode method of the actual cipher used + /// + FEncryptionMethod : TEncodeDecodeMethod; + + /// + /// XOR implementation for unsigned 128 bit numbers + /// + /// + /// First number to xor + /// + /// + /// Second number to xor the first with + /// + /// + /// x xor y + /// + function XOR_T128(const x, y: T128): T128; inline; + /// + /// XOR implementation for a pointer and an unsigned 128 bit number + /// + /// + /// Pointer on a T128 typed number to xor with y + /// + /// + /// Second number to xor the first with + /// + /// + /// x xor y + /// + function XOR_PointerWithT128(const x: Pointer; y: T128 ): T128; inline; + /// + /// XORs the bytes given in a byte array with a T128 number given + /// + /// + /// Bytes which shall be XORed with the T128 number + /// + /// + /// Starting index within x from which onwards to XOR + /// + /// + /// Number of bytes from x beginning at XIndex to XOR + /// + /// + /// Value to XOR the bytes from y with. XOR is done bytewise for each + /// byte of y + /// + /// + /// Result of the XOR operation + /// + procedure XOR_ArrayWithT128(const x: TBytes; XIndex, Count: UInt64; y: T128; var Result: TBytes); overload; //inline; + procedure XOR_ArrayWithT128(const x: PUInt8Array; XIndex, Count: UInt64; y: T128; Result: PUInt8Array); overload; + + /// + /// XORs all elements of the precalculated matrix with the value passed + /// + /// + /// Value who's two parts shall be XORed with the two parts of the + /// matrix each. + /// + /// + /// result of the XOR Operation + /// + function poly_mult_H(const hx: T128) : T128; inline; + + /// + /// Encodes the 64 bit lengths of DataToAuthenticate and of the cipher + /// text into a T128 value, swapping the bytes in the process. + /// + /// + /// Result of the operation + /// + /// + /// Length of the data to authenticate in byte + /// + /// + /// Length of the ciphertext in byte + /// + procedure SetAuthenticationCipherLength(var x : T128; + AuthDataLength, CipherTextLength : UInt64); inline; + + /// + /// Calculates a table with precalculated values which speeds up + /// operations later. The initialized table is the FM field. + /// + /// + /// Start value for the precalculation + /// + procedure GenerateTableM8Bit(const H: T128); //inline; + /// + /// Performs a right shift of 1 of all bytes in an 128 bit variable + /// + /// + /// Variable on which the right shift is being performed + /// + procedure ShiftRight(var rx: T128); //inline; + + /// + /// Incremepts the last 4 bytes of the index 0 part + /// + /// + /// Value to increment, this is the return value as well. + /// + procedure INCR(var Y : T128); + + /// + /// Defines the length of the resulting authentication value in bit. + /// + /// + /// Sets the length of Authenticaton_tag in bit, values as per specification + /// are: 128, 120, 112, 104, or 96 bit. For certain applications, they + /// may be 64 or 32 as well, but the use of these two tag lengths + /// constrains the length of the input data and the lifetime of the key. + /// + procedure SetAuthenticationTagLength(const Value: UInt32); + /// + /// Returns the length of the calculated authehtication value in bit + /// + /// + /// Length of the calculated authentication value in bit + /// + function GetAuthenticationTagBitLength: UInt32; + + /// + /// Calculates the hash value + /// + /// + /// Specifys the data for which an authentication value shall be + /// calculated. It is allowed to be nil. + /// + /// + /// Encrypted data used in the calculation + /// + /// + /// Length of the ciphertext in bytes. Use when reading part of array. + /// + /// + /// Calculated raw hash value which will later get returned as AuthenticatedTag + /// + function CalcGaloisHash(AuthenticatedData, Ciphertext : TBytes; CiphertextSize: + Integer): T128; overload; + + /// + /// Calculates the hash value + /// + /// + /// Specifys the data for which an authentication value shall be + /// calculated. It is allowed to be nil. + /// + /// + /// Encrypted data used in the calculation + /// + /// + /// Length of the ciphertext in bytes. Use when reading part of array. + /// + /// + /// Calculated raw hash value which will later get returned as AuthenticatedTag + /// + function CalcGaloisHash(AuthenticatedData: TBytes; Ciphertext : PUInt8Array; CiphertextSize: + Integer): T128; overload; + + /// + /// Encrypts a T128 value using the encryption method specified on init + /// + /// + /// Value to be encrypted + /// + /// + /// Encrypted value + /// + function EncodeT128(Value: T128): T128; + public + /// + /// Should be called when starting encryption/decryption in order to + /// initialize internal tables etc. + /// + /// + /// Encryption method of the cypher used + /// + /// + /// Initialization vector + /// + procedure Init(EncryptionMethod : TEncodeDecodeMethod; + InitVector : TBytes); +// /// +// /// Encodes a block of data using the supplied cipher +// /// +// /// +// /// Plain text to encrypt +// /// +// /// +// /// Ciphertext after encryption +// /// +// /// +// /// Number of bytes to encrypt +// /// +// procedure EncodeGCM(Source, +// Dest : TBytes; +// Size : Integer); + /// + /// Encodes a block of data using the supplied cipher + /// + /// + /// Plain text to encrypt + /// + /// + /// Ciphertext after encryption + /// + /// + /// Number of bytes to encrypt + /// + procedure EncodeGCM(Source, + Dest : PUInt8Array; + Size : Integer); + /// + /// Decodes a block of data using the supplied cipher + /// + /// + /// Encrypted ciphertext to decrypt + /// + /// + /// Plaintext after decryption + /// + /// + /// Number of bytes to decrypt + /// + procedure DecodeGCM(Source, + Dest : TBytes; + Size : Integer); + + /// + /// Returns a list of authentication tag lengths explicitely specified by + /// the official specification of the standard. + /// + /// + /// List of bit lengths + /// + function GetStandardAuthenticationTagBitLengths:TStandardBitLengths; + + /// + /// The data which shall be authenticated in parallel to the encryption + /// + property DataToAuthenticate : TBytes + read FDataToAuthenticate + write FDataToAuthenticate; + /// + /// Sets the length of AuthenticatonTag in bit, values as per official + /// specification are: 128, 120, 112, 104, or 96 bit. For certain + /// applications, they may be 64 or 32 as well, but the use of these two + /// tag lengths constrains the length of the input data and the lifetime + /// of the key. + /// + property AuthenticationTagBitLength : UInt32 + read GetAuthenticationTagBitLength + write SetAuthenticationTagLength; + /// + /// Calculated authentication value + /// + property CalculatedAuthenticationTag : TBytes + read FCalcAuthenticationTag + write FCalcAuthenticationTag; + + /// + /// Expected authentication tag value, will be compared with actual value + /// when decryption finished. + /// + property ExpectedAuthenticationTag : TBytes + read FExpectedAuthenticationTag + write FExpectedAuthenticationTag; + end; + +implementation + +function TGCM.XOR_T128(const x, y : T128): T128; +begin + Result[0] := x[0] xor y[0]; + Result[1] := x[1] xor y[1]; +end; + +function TGCM.XOR_PointerWithT128(const x : Pointer; y : T128): T128; +begin + Result[0] := P128(x)^[0] xor y[0]; + Result[1] := P128(x)^[1] xor y[1]; +end; + +procedure TGCM.XOR_ArrayWithT128(const x: TBytes; XIndex, Count: UInt64; y: T128; var Result: TBytes); +var + i : integer; + by : P16ByteArray; +begin + by := @y[0]; + for i := 0 to Count-1 do + begin + Result[XIndex] := x[XIndex] xor by[i]; + inc(XIndex); + end; +end; + +procedure TGCM.XOR_ArrayWithT128(const x: PUInt8Array; XIndex, Count: UInt64; y: T128; Result: PUInt8Array); +var + i : integer; + by : P16ByteArray; +begin + by := @y[0]; + for i := 0 to Count-1 do + begin + Result^[XIndex] := x^[XIndex] xor by[i]; + inc(XIndex); + end; +end; + +function TGCM.poly_mult_H(const hx : T128): T128; +var + i : integer; + x : P16ByteArray; +begin + x := @hx[0]; + Result := FM[0, x[0]]; + + for i := 1 to 15 do + begin + Result[0] := Result[0] xor FM[i, x[i]][0]; + Result[1] := Result[1] xor FM[i, x[i]][1]; + end; +end; + +procedure TGCM.SetAuthenticationCipherLength(var x : T128; + AuthDataLength, CipherTextLength : UInt64); +var + i : integer; + hx : P16ByteArray; +begin + hx := @x[0]; + // al: + x := nullbytes; + i := 7; + + repeat + hx[i] := AuthDataLength mod 256; + AuthDataLength := AuthDataLength shr 8; + dec(i); + until AuthDataLength = 0; + + // cl: + i := 15; + + repeat + hx[i] := CipherTextLength mod 256; + CipherTextLength := CipherTextLength shr 8; + dec(i); + until CipherTextLength = 0; +end; + +procedure TGCM.GenerateTableM8Bit(const H : T128); +var + hbit, hbyte, i, j : integer; + HP : T128; + bHP : P16ByteArray; + mask : byte; +begin + HP := H; + bHP := @HP[0]; + for hbyte := 0 to 15 do + begin + mask := 128; + for hbit := 0 to 7 do + begin + FM[hbyte, mask] := HP; + + if (bHP[15] and 1 = 0) then + ShiftRight(HP) + else + begin + ShiftRight(HP); + bHP[0] := bHP[0] xor $e1; + end; + + mask := mask shr 1; + end; + end; + + for hbyte := 0 to 15 do + begin + i := 2; + + while i <= 128 do + begin + for j := 1 to i-1 do + FM[hbyte, i+j] := XOR_T128(FM[hbyte, i], FM[hbyte, j]); + i := i*2; + end; + + FM[hbyte, 0] := nullbytes; + end; +end; + +procedure TGCM.ShiftRight(var rx : T128); +var + x : P16ByteArray; + i : integer; +begin + x := @rx[0]; + + for i := 15 downto 1 do + x[i] := (x[i] shr 1) or ((x[i-1] and 1) shl 7); + + x[0] := x[0] shr 1; +end; + +procedure TGCM.SetAuthenticationTagLength(const Value: UInt32); +begin + FCalcAuthenticationTagLength := Value shr 3; + SetLength(FCalcAuthenticationTag, FCalcAuthenticationTagLength); +end; + +procedure TGCM.INCR(var Y : T128); +var + bY : P16ByteArray; +begin + bY := @Y[0]; + + {$IFOPT Q+}{$DEFINE RESTORE_OVERFLOWCHECKS}{$Q-}{$ENDIF} + {$Q-} + inc(bY[15]); + if bY[15] = 0 then + begin + inc(bY[14]); + + if bY[14] = 0 then + begin + inc(bY[13]); + + if bY[13] = 0 then + inc(bY[12]); + end; + end; + {$IFDEF RESTORE_OVERFLOWCHECKS}{$Q+}{$ENDIF} +end; + +procedure TGCM.Init(EncryptionMethod : TEncodeDecodeMethod; + InitVector : TBytes); +var + b : ^Byte; + OldH : T128; +begin + Assert(Assigned(EncryptionMethod), 'No encryption method specified'); + + // Clear calculated authentication value + if (Length(FCalcAuthenticationTag) > 0) then + FillChar(FCalcAuthenticationTag[0], Length(FCalcAuthenticationTag), #0); + + FEncryptionMethod := EncryptionMethod; + + Nullbytes[0] := 0; + Nullbytes[1] := 0; + + OldH := FH; + EncryptionMethod(@Nullbytes[0], @FH[0], 16); + + // Only generate the table when not already generated + if (OldH[0] <> FH[0]) or (OldH[1] <> FH[1]) then + GenerateTableM8Bit(FH); + + if length(InitVector) = 12 then + begin + FY[1] := 0; + Move(InitVector[0], FY[0], 12); + b := @FY[0]; + inc(b, 15); + b^ := 1; + end + else + FY := CalcGaloisHash(nil, InitVector, length(InitVector)); + + FEncryptionMethod(@FY[0], @FE_K_Y0[0], 16); +end; + +function TGCM.CalcGaloisHash(AuthenticatedData, Ciphertext : TBytes; + CiphertextSize: Integer): T128; +var + AuthCipherLength : T128; + x : T128; + n : Uint64; + + procedure encode(data : TBytes; dataSize: Integer); + var + i, mod_d, div_d, len_d : UInt64; + hdata : T128; + begin + len_d := dataSize; + if (len_d > 0) then + begin + n := 0; + div_d := len_d div 16; + if div_d > 0 then + begin + for i := 0 to div_d-1 do + begin + x := poly_mult_H(XOR_PointerWithT128(@data[n], x )); + inc(n, 16); + end; + end; + + mod_d := len_d mod 16; + if mod_d > 0 then + begin + hdata := nullbytes; + Move(data[n], hdata[0], mod_d); + x := poly_mult_H(XOR_T128(hdata, x)); + end; + end; + end; + +begin + x := nullbytes; + encode(AuthenticatedData, length(AuthenticatedData)); + Assert(length(Ciphertext) >= CiphertextSize); + encode(Ciphertext, CiphertextSize); + SetAuthenticationCipherLength(AuthCipherLength, length(AuthenticatedData) shl 3, CiphertextSize shl 3); + + Result := poly_mult_H(XOR_T128(AuthCipherLength, x)); +end; + +function TGCM.CalcGaloisHash(AuthenticatedData: TBytes; Ciphertext : PUInt8Array; + CiphertextSize: Integer): T128; +var + AuthCipherLength : T128; + x : T128; + n : Uint64; + + procedure encode(data : PUInt8Array; dataSize: Integer); + var + i, mod_d, div_d, len_d : UInt64; + hdata : T128; + begin + len_d := dataSize; + if (len_d > 0) then + begin + n := 0; + div_d := len_d div 16; + if div_d > 0 then + begin + for i := 0 to div_d-1 do + begin + x := poly_mult_H(XOR_PointerWithT128(@data^[n], x )); + inc(n, 16); + end; + end; + + mod_d := len_d mod 16; + if mod_d > 0 then + begin + hdata := nullbytes; + Move(data^[n], hdata[0], mod_d); + x := poly_mult_H(XOR_T128(hdata, x)); + end; + end; + end; + +begin + x := nullbytes; + + if (length(AuthenticatedData) > 0) then + encode(@AuthenticatedData[0], length(AuthenticatedData)); +// Assert(length(Ciphertext) >= CiphertextSize); + encode(Ciphertext, CiphertextSize); + SetAuthenticationCipherLength(AuthCipherLength, length(AuthenticatedData) shl 3, CiphertextSize shl 3); + + Result := poly_mult_H(XOR_T128(AuthCipherLength, x)); +end; + +procedure TGCM.DecodeGCM(Source, Dest: TBytes; Size: Integer); +var + i, j, BlockCount : UInt64; + a_tag : T128; +begin + i := 0; + BlockCount := Size div 16; + + for j := 1 to BlockCount do + begin + INCR(FY); + P128(@Dest[i])^ := XOR_PointerWithT128(@Source[i], EncodeT128(FY)); + inc(i, 16); + end; + + if i < Size then + begin + INCR(FY); + XOR_ArrayWithT128(Source, i, UInt64(Size)-i, EncodeT128(FY), Dest); + end; + + a_tag := XOR_T128(CalcGaloisHash(DataToAuthenticate, Source, Size), FE_K_Y0); + + Setlength(FCalcAuthenticationTag, FCalcAuthenticationTagLength); + if (FCalcAuthenticationTagLength > 0) then + Move(a_tag[0], FCalcAuthenticationTag[0], FCalcAuthenticationTagLength); + + // Check for correct authentication result is in Done of DECCipherModes + // if not IsEqual(FExpectedAuthenticationTag, FCalcAuthenticationTag) then + // raise EDECCipherAuthenticationException.CreateRes(@sInvalidAuthenticationValue); + + // In difference to the NIST recommendation we do not discard plaintext if + // authentication failed to make data recovery possible. But since we throw + // an exception the user will get notified that there's something wrong + // if not IsEqual(authenticaton_tag, ba_tag) then + // SetLength(plaintext, 0); // NIST FAIL => pt='' +end; + +//procedure TGCM.EncodeGCM(Source, Dest: TBytes; Size: Integer); +//var +// i, j, div_len_plain : UInt64; +// AuthTag : T128; +//begin +// i := 0; +// div_len_plain := Size div 16; +// +// for j := 1 to div_len_plain do +// begin +// INCR(FY); +// +// P128(@Dest[i])^ := XOR_PointerWithT128(@Source[i], EncodeT128(FY)); +// +// inc(i,16); +// end; +// +// if i < Size then +// begin +// INCR(FY); +// XOR_ArrayWithT128(Source, i, UInt64(Size)-i, EncodeT128(FY), Dest); +// end; +// +// AuthTag := XOR_T128(CalcGaloisHash(DataToAuthenticate, Dest, Size), FE_K_Y0); +// Setlength(FCalcAuthenticationTag, FCalcAuthenticationTagLength); +// if (FCalcAuthenticationTagLength > 0) then +// Move(AuthTag[0], FCalcAuthenticationTag[0], FCalcAuthenticationTagLength); +//end; + +procedure TGCM.EncodeGCM(Source, Dest: PUInt8Array; Size: Integer); +var + i, j, div_len_plain : UInt64; + AuthTag : T128; +begin + i := 0; + div_len_plain := Size div 16; + + for j := 1 to div_len_plain do + begin + INCR(FY); + + P128(@Dest[i])^ := XOR_PointerWithT128(@Source[i], EncodeT128(FY)); + + inc(i,16); + end; + + if i < Size then + begin + INCR(FY); + XOR_ArrayWithT128(Source, i, UInt64(Size)-i, EncodeT128(FY), Dest); + end; + + AuthTag := XOR_T128(CalcGaloisHash(DataToAuthenticate, Dest, Size), FE_K_Y0); + Setlength(FCalcAuthenticationTag, FCalcAuthenticationTagLength); + if (FCalcAuthenticationTagLength > 0) then + Move(AuthTag[0], FCalcAuthenticationTag[0], FCalcAuthenticationTagLength); +end; + +function TGCM.EncodeT128(Value: T128): T128; +begin + FEncryptionMethod(@Value[0], @Result[0], 16); +end; + +function TGCM.GetAuthenticationTagBitLength: UInt32; +begin + Result := FCalcAuthenticationTagLength shl 3; +end; + +function TGCM.GetStandardAuthenticationTagBitLengths: TStandardBitLengths; +begin + SetLength(Result, 5); + Result := [96, 104, 112, 120, 128]; +end; + +// +//function decrypt( const key, IV : TBytes; out plaintext : TBytes; const authenticated_data, +//ciphertext : TBytes; len_auth_tag : integer; const authenticaton_tag : TBytes ) : boolean; +//var +// i, j, div_len_ciph, len_ciph : Uint64; +// a_tag, E_K_Y0, Y, H : T128; +// bY : array[0..15] of byte absolute Y[0]; +// ba_Tag : TBytes; +// +// function equal( const a, b : TBytes ):boolean; +// begin +// if length(a) <> length(b) then Result := false +// else +// Result := CompareMem( @a[0], @b[0], length(a) ); +// end; +// +//begin +// len_auth_tag := len_auth_tag shr 3; +// +// E_Init( key ); +// H := E_Cipher( nullbytes ); +// Table_M_8Bit(H); +// +// len_ciph := length( ciphertext ); +// SetLength( plaintext, len_ciph ); +// +// if length(IV) = 12 then +// begin +// Y[1] := 0; +// Move( IV[0], Y[0], 12 ); +// bY[15] := 1; +// end +// else +// Y := CalcGaloisHash( H, nil, IV ); +// +// E_K_Y0 := E_Cipher( y ); +// +// i := 0; +// div_len_ciph := len_ciph div 16; +// for j := 1 to div_len_ciph do +// begin +// INCR( Y ); +// P128(@plaintext[i])^ := XOR_128_n( @ciphertext[i], E_cipher( Y ) ); +// inc(i,16); +// end; +// +// if i < len_ciph then +// begin +// INCR( Y ); +// XOR_128_n_l( ciphertext, i, len_ciph-i, E_cipher( Y ), plaintext ); +// end; +// +// a_tag := XOR_128( CalcGaloisHash( H, authenticated_data, ciphertext ), E_K_Y0 ); +// +// Setlength( ba_tag, len_auth_tag ); +// Move( a_tag[0], ba_tag[0], len_auth_tag ); +// +// Result := equal( authenticaton_tag, ba_tag ); +// if not Result then SetLength( plaintext, 0 ); // NIST FAIL => pt='' +//end; +// + +end. diff --git a/Unit Tests/DECDUnitTestSuite.dpr b/Unit Tests/DECDUnitTestSuite.dpr index 44ec045..224f30c 100644 --- a/Unit Tests/DECDUnitTestSuite.dpr +++ b/Unit Tests/DECDUnitTestSuite.dpr @@ -16,6 +16,7 @@ program DECDUnitTestSuite; {$ENDIF} uses +// FastMM4, Vcl.Forms, {$IFDEF TESTINSIGHT} TestInsight.Client, @@ -40,7 +41,9 @@ uses TestDECHashSHA3 in 'Tests\TestDECHashSHA3.pas', TestDECCipherModesGCM in 'Tests\TestDECCipherModesGCM.pas', TestDECZIPHelper in 'Tests\TestDECZIPHelper.pas', - TestDECCipherPaddings in 'Tests\TestDECCipherPaddings.pas'; + TestDECCipherPaddings in 'Tests\TestDECCipherPaddings.pas', + TestDECCipherModesCCM in 'Tests\TestDECCipherModesCCM.pas', + AuthenticatedCiphersCommonTestData in 'Tests\AuthenticatedCiphersCommonTestData.pas'; {$R *.RES} diff --git a/Unit Tests/DECDUnitTestSuite.dproj b/Unit Tests/DECDUnitTestSuite.dproj index 48958ad..1670f30 100644 --- a/Unit Tests/DECDUnitTestSuite.dproj +++ b/Unit Tests/DECDUnitTestSuite.dproj @@ -1,7 +1,7 @@  {4117BDAD-9849-4F6E-8968-BB6B92C8AE7B} - 20.2 + 20.3 DECDUnitTestSuite.dpr True Debug @@ -162,6 +162,9 @@ + + + Base @@ -223,7 +226,19 @@ + + + DECDUnitTestSuite.exe + true + + + + + .\ + true + + 1 @@ -338,6 +353,16 @@ 1 + + + res\values-v35 + 1 + + + res\values-v35 + 1 + + res\drawable-anydpi-v26 diff --git a/Unit Tests/DECDUnitXTestSuite.dproj b/Unit Tests/DECDUnitXTestSuite.dproj index 6563539..461a00b 100644 --- a/Unit Tests/DECDUnitXTestSuite.dproj +++ b/Unit Tests/DECDUnitXTestSuite.dproj @@ -1,7 +1,7 @@  {FEE36113-01B3-4DE2-B2BC-B4858EAD04A3} - 20.2 + 20.3 DECDUnitXTestSuite.dpr True Debug @@ -105,7 +105,7 @@ $(BDS)\bin\delphi_PROJECTICON.ico - package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=;minSdkVersion=23;targetSdkVersion=34 + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=;minSdkVersion=23;targetSdkVersion=35 Debug android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar DBXSqliteDriver;RESTComponents;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;FmxTeeUI;FireDACIBDriver;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FMXTee;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) @@ -117,7 +117,7 @@ $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png - package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=;minSdkVersion=23;targetSdkVersion=34 + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=;minSdkVersion=23;targetSdkVersion=35 Debug true Base @@ -194,6 +194,7 @@ + Base @@ -361,6 +362,16 @@ 1 + + + res\values-v35 + 1 + + + res\values-v35 + 1 + + res\drawable-anydpi-v26 diff --git a/Unit Tests/Tests/AuthenticatedCiphersCommonTestData.pas b/Unit Tests/Tests/AuthenticatedCiphersCommonTestData.pas new file mode 100644 index 0000000..a76938a --- /dev/null +++ b/Unit Tests/Tests/AuthenticatedCiphersCommonTestData.pas @@ -0,0 +1,153 @@ +{***************************************************************************** + The DEC team (see file NOTICE.txt) licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. A copy of this licence is found in the root directory of + this project in the file LICENCE.txt or alternatively at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*****************************************************************************} + +/// +/// Common definitions used by all authenticated cipher modes tests, if possible. +/// The file loading for the individual algorithms has to be implemented in the +/// individual test unit for the algorith, because NIST doesn't use a 100% +/// identical file format for CCM and GCM. +/// +unit AuthenticatedCiphersCommonTestData; + +interface + +uses + Generics.Collections; + +type + /// + /// Test data for one single authenticated cipher test, all in HexL + /// + TSingleAuthenticatedTestData = record + /// + /// Encryption/decryption key + /// + CryptKey : RawByteString; + /// + /// Initialization vecotr + /// + InitVector : RawByteString; + /// + /// Plain Text: text to be encrypted, given in HexL + /// + PT : RawByteString; + /// + /// Additional Authenticated Data: the data which shall be authenticated + /// but not encrypted. + /// + AAD : RawByteString; + /// + /// Cipher Text: encrypted text, given in HexL + /// + CT : RawByteString; + /// + /// Calculated authenticated "tag" value + /// + TagResult : RawByteString; + /// + /// Used additional authenticated data for testing authentication failures. + /// Only filled when present in test data file. + /// + ModifiedAAD: RawByteString; + /// + /// Used ciphertext data for testing authentication failures. + /// Only filled when present in test data file. + /// + ModifiedCT: RawByteString; + + /// + /// Sets all fields and array entries to default values + /// + procedure Clear; + end; + + /// + /// Test data for one single GCM test + /// + TAuthenticatedCipherTestSetEntry = record + /// + /// Length of the encryption/decryption key in bit, determines the + /// algorithm used in case of AES (AES128, AES192, AES256) + /// + Keylen : UInt16; + /// + /// Length of the initialization vector in bit + /// + IVlen : UInt16; + /// + /// Length of the ? in bit + /// + PTlen : UInt16; + /// + /// Length of the ? in bit + /// + AADlen : UInt16; + /// + /// Length of the "tag" resulting from the authentication part in bit + /// + Taglen : UInt16; + + /// + /// The test data files provided contains one test for the meta data + /// specified above. This array holds the test data. + /// + TestData : array of TSingleAuthenticatedTestData; + + /// + /// Sets all fields and array entries to default values + /// + procedure Clear; + end; + + /// + /// List of loaded authentication cipher test vectors + /// + TAuthenticatedTestDataList = TList; + +implementation + +{ TSingleAuthenticatedTestData } + +procedure TSingleAuthenticatedTestData.Clear; +begin + CryptKey := ''; + InitVector := ''; + PT := ''; + AAD := ''; + CT := ''; + TagResult := ''; + ModifiedAAD := ''; + ModifiedCT := ''; +end; + +{ TAuthenticatedCipherTestSetEntry } + +procedure TAuthenticatedCipherTestSetEntry.Clear; +var + i : Integer; +begin + Keylen := 0; + IVlen := 0; + PTlen := 0; + AADlen := 0; + Taglen := 0; + + for i := Low(TestData) to High(TestData) do + TestData[i].Clear; +end; + +end. diff --git a/Unit Tests/Tests/TestDECCRC.pas b/Unit Tests/Tests/TestDECCRC.pas index 94c8da0..5a6c9d4 100644 --- a/Unit Tests/Tests/TestDECCRC.pas +++ b/Unit Tests/Tests/TestDECCRC.pas @@ -1027,7 +1027,7 @@ initialization {$IFDEF DUnitX} TDUnitX.RegisterTestFixture(TestCRC); {$ELSE} - RegisterTest(TestCRC.Suite); + RegisterTest('DEC CRC', TestCRC.Suite); {$ENDIF} end. diff --git a/Unit Tests/Tests/TestDECCipherFormats.pas b/Unit Tests/Tests/TestDECCipherFormats.pas index adcd9a7..978ba98 100644 --- a/Unit Tests/Tests/TestDECCipherFormats.pas +++ b/Unit Tests/Tests/TestDECCipherFormats.pas @@ -709,7 +709,7 @@ procedure TestTDECCipherFormats.TestEncodeWideStringToString; initialization // Register any test cases with the test runner {$IFNDEF DUnitX} - RegisterTest(TestTDECCipherFormats.Suite); + RegisterTest('DEC cipher formats', TestTDECCipherFormats.Suite); {$ELSE} TDUnitX.RegisterTestFixture(TestTDECCipherFormats); {$ENDIF} diff --git a/Unit Tests/Tests/TestDECCipherModes.pas b/Unit Tests/Tests/TestDECCipherModes.pas index 2437c44..21d5d70 100644 --- a/Unit Tests/Tests/TestDECCipherModes.pas +++ b/Unit Tests/Tests/TestDECCipherModes.pas @@ -352,6 +352,12 @@ TestTDECCipherModes = class(TTestCase) /// /// Method needed because CheckException only allows procedure methods and /// not functions as parameter. + /// Simply sets FCipher.Mode to CCM. + /// + procedure TestFailureSetCCMMode; + /// + /// Method needed because CheckException only allows procedure methods and + /// not functions as parameter. /// Tries to encrypt data using ECB-mode but data is not a multiple of /// the block size (length data > 1 block) /// @@ -420,6 +426,7 @@ TestTDECCipherModes = class(TTestCase) procedure TestFailureSetExpectedAuthenticationTag; procedure TestFailureGetExpectedAuthenticationTag; procedure InitGCMBlocksizeNot128Failure; + procedure InitCCMBlocksizeNot128Failure; procedure InitGCMStreamCipherFailure; procedure TestEncodeECBDataDoesNotMatchBlockSizeFailureSmall; procedure TestEncodeECBDataDoesNotMatchBlockSizeFailure; @@ -777,6 +784,16 @@ procedure TestTDECCipherModes.InitGCMBlocksizeNot128Failure; end; end; +procedure TestTDECCipherModes.InitCCMBlocksizeNot128Failure; +begin + FCipher := TCipher_Blowfish.Create; + try + CheckException(TestFailureSetCCMMode, EDECCipherException); + finally + FCipher.Free; + end; +end; + procedure TestTDECCipherModes.InitGCMStreamCipherFailure; begin FCipher := TCipher_RC4.Create; @@ -814,6 +831,11 @@ procedure TestTDECCipherModes.TestFailureSetGCMMode; FCipher.Mode := TCipherMode.cmGCM; end; +procedure TestTDECCipherModes.TestFailureSetCCMMode; +begin + FCipher.Mode := TCipherMode.cmCCM; +end; + procedure TestTDECCipherModes.TestGetStandardAuthenticationTagBitLengths; var i, n : Integer; @@ -1009,6 +1031,6 @@ initialization {$IFDEF DUnitX} TDUnitX.RegisterTestFixture(TestTDECCipherModes); {$ELSE} - RegisterTest(TestTDECCipherModes.Suite); + RegisterTest('DEC cipher modes', TestTDECCipherModes.Suite); {$ENDIF} end. diff --git a/Unit Tests/Tests/TestDECCipherModesCCM.pas b/Unit Tests/Tests/TestDECCipherModesCCM.pas new file mode 100644 index 0000000..26fb507 --- /dev/null +++ b/Unit Tests/Tests/TestDECCipherModesCCM.pas @@ -0,0 +1,805 @@ +{***************************************************************************** + The DEC team (see file NOTICE.txt) licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. A copy of this licence is found in the root directory of + this project in the file LICENCE.txt or alternatively at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*****************************************************************************} + +{$M+} // DUnitX would add it anyway +unit TestDECCipherModesCCM; + +interface + +// Needs to be included before any other statements +{$INCLUDE TestDefines.inc} + +uses + {$IFDEF DUnitX} + DUnitX.TestFramework,DUnitX.DUnitCompatibility, + {$ELSE} + TestFramework, + {$ENDIF} + System.SysUtils, + Generics.Collections, + System.Math, + DECBaseClass, + DECCipherBase, + DECCipherModes, + DECCipherFormats, + DECCiphers, + AuthenticatedCiphersCommonTestData; + +type + // Testmethods for class TDECCipher + {$IFDEF DUnitX} [TestFixture] {$ENDIF} + TestTDECCCM = class(TTestCase) + strict private + FTestDataList : TAuthenticatedTestDataList; + FCipherAES : TCipher_AES; + FTestBitLength : Integer; // AuthenticationBitLength for test for wring lengths + private + function IsEqual(const a, b: TBytes): Boolean; + procedure DoTestEncodeStream_LoadAndTestCAVSData(const aMaxChunkSize: Int64); + procedure DoTestEncodeStream_TestSingleSet(const aSetIndex, aDataIndex: + Integer; const aMaxChunkSize: Int64 = -1); + procedure DoTestInitFailureIVTooLong; + procedure DoTestInitFailureIVTooShort; + procedure DoTestRFC3610(EncodeTest: Boolean); + procedure DoTestAuthenticationBitLengthWrong; + public + procedure SetUp; override; + procedure TearDown; override; + published + procedure TestEncode; + /// + /// Uses testvectors from RFC 3610 but in a generated way + /// as they are specified like this. + /// + procedure TestEncodeRFC3610; + /// + /// Uses testvectors from RFC 3610 but in a generated way + /// as they are specified like this. + /// + procedure TestDecodeRFC3610; + procedure TestDecode; + procedure TestDecodeStream; + procedure TestInitIV; + procedure TestInitFailureIVTooLong; + procedure TestInitFailureIVTooShort; + procedure TestEncodeStream; +// procedure TestEncodeLargeStream; +// procedure TestEncodeStreamChunked; + procedure TestGetDataToAuthenticate; + procedure TestSetDataToAuthenticate; + procedure TestSetAuthenticationBitLengths; + procedure TestSetWrongAuthenticationBitLengths; + procedure TestGetStandardAuthenticationTagBitLengths; + procedure TestGetExpectedAuthenticationResult; + procedure TestSetExpectedAuthenticationResult; + end; + + +implementation + +uses + System.Classes, + DECTypes, + DECFormat, + DECUtil, + DECAuthenticatedCipherModesBase; + +{ TestTDECCCM } + +procedure TestTDECCCM.SetUp; +var + TestDataSet : TAuthenticatedCipherTestSetEntry; +begin + inherited; + + FTestDataList := TAuthenticatedTestDataList.Create; + + FCipherAES := TCipher_AES.Create; + FCipherAES.Mode := TCipherMode.cmCCM; + + TestDataSet.Keylen := 256; + TestDataSet.IVlen := 92; + TestDataSet.PTlen := 23 * 8; + TestDataSet.AADlen := 64; // hdr1 length in initial source + TestDataSet.Taglen := 64; // authentication tag length in bit + SetLength(TestDataSet.TestData, 2); + TestDataSet.TestData[0].CryptKey := 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf'; + TestDataSet.TestData[0].InitVector := '00000003020100a0a1a2a3a4a5'; + TestDataSet.TestData[0].PT := '08090a0b0c0d0e0f101112131415161718191a1b1c1d1e'; + TestDataSet.TestData[0].AAD := '0001020304050607'; // hdr1 in initial source + TestDataSet.TestData[0].CT := '588c979a61c663d2f066d0c2c0f989806d5f6b61dac384'; + TestDataSet.TestData[0].TagResult := '17e8d12cfdf926e0'; + TestDataSet.TestData[0].ModifiedAAD := ''; + TestDataSet.TestData[0].ModifiedCT := ''; + + TestDataSet.TestData[1].CryptKey := 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf'; + TestDataSet.TestData[1].InitVector := '00000006050403a0a1a2a3a4a5'; + TestDataSet.TestData[1].PT := '0c0d0e0f101112131415161718191a1b1c1d1e'; + TestDataSet.TestData[1].AAD := '000102030405060708090a0b'; // hdr1 in initial source + TestDataSet.TestData[1].CT := 'a28c6865939a9a79faaa5c4c2a9d4a91cdac8c'; + TestDataSet.TestData[1].TagResult := '96c861b9c9e61ef1'; + TestDataSet.TestData[1].ModifiedAAD := ''; + TestDataSet.TestData[1].ModifiedCT := ''; + + FTestDataList.Add(TestDataSet); + + TestDataSet.Taglen := 32; // authentication tag length in bit + SetLength(TestDataSet.TestData, 1); + TestDataSet.TestData[0].CryptKey := '404142434445464748494a4b4c4d4e4f'; + TestDataSet.TestData[0].InitVector := '10111213141516'; + TestDataSet.TestData[0].PT := '20212223'; + TestDataSet.TestData[0].AAD := '0001020304050607'; // hdr1 in initial source + TestDataSet.TestData[0].CT := '7162015b'; + TestDataSet.TestData[0].TagResult := '4dac255d'; + TestDataSet.TestData[0].ModifiedAAD := ''; + TestDataSet.TestData[0].ModifiedCT := ''; + + FTestDataList.Add(TestDataSet); +end; + +procedure TestTDECCCM.TearDown; +begin + inherited; + + FCipherAES.Free; +// FTestDataLoader.Free; + FTestDataList.Free; +end; + +procedure TestTDECCCM.TestDecode; +var + TestDataSet : TAuthenticatedCipherTestSetEntry; + TestData : TSingleAuthenticatedTestData; + DecryptData : TBytes; +begin + for TestDataSet in FTestDataList do + begin + try + for TestData in TestDataSet.TestData do + begin + FCipherAES.Init(BytesOf(TFormat_HexL.Decode(TestData.CryptKey)), + BytesOf(TFormat_HexL.Decode(TestData.InitVector)), + $FF); + + FCipherAES.AuthenticationResultBitLength := TestDataSet.Taglen; + FCipherAES.DataToAuthenticate := TFormat_HexL.Decode( + BytesOf(TestData.AAD)); + + FCipherAES.ExpectedAuthenticationResult := + TFormat_HexL.Decode(BytesOf(TestData.TagResult)); + + DecryptData := FCipherAES.DecodeBytes( + TFormat_HexL.Decode( + BytesOf(TestData.CT))); + FCipherAES.Done; + + CheckEquals(string(TestData.PT), + StringOf(TFormat_HexL.Encode(DecryptData)), + 'Plaintext wrong for key ' + + string(TestData.CryptKey) + ' IV ' + + string(TestData.InitVector) + ' PT ' + + string(TestData.PT) + ' AAD ' + + string(TestData.AAD) + ' Exp. PT: ' + + string(TestData.CT) + ' Act. PT: ' + + StringOf(TFormat_HexL.Encode(DecryptData))); + + // Additional Authentication Data prüfen + CheckEquals(string(TestData.TagResult), + StringOf(TFormat_HexL.Encode(FCipherAES.CalculatedAuthenticationResult)), + 'Authentication tag wrong for key ' + + string(TestData.CryptKey) + ' IV ' + + string(TestData.InitVector) + ' PT ' + + string(TestData.PT) + ' AAD ' + + string(TestData.AAD) + ' Exp. AuthTag: ' + + string(TestData.TagResult) + ' Act. AuthTag: ' + + StringOf(TFormat_HexL.Encode(FCipherAES.DataToAuthenticate))); + end; + except + on E: Exception do + Status('CryptKey ' + string(TestData.CryptKey) + + ' ' + E.ClassName + ': ' + E.Message); + end; + end; +end; + +procedure TestTDECCCM.TestInitFailureIVTooLong; +begin + CheckException(DoTestInitFailureIVTooLong, + EDECNonceLengthException, + 'Init vector too long not detected'); +end; + +procedure TestTDECCCM.DoTestInitFailureIVTooLong; +var + CipherAES : TCipher_AES; +begin + CipherAES := TCipher_AES.Create; + + try + CipherAES.Mode := cmCCM; + CipherAES.Init(BytesOf(TFormat_HexL.Decode('000102030405060708')), + BytesOf(TFormat_HexL.Decode('0a0b0c0d0e0f0a0b0c0d0e0f0a0b0c0d')), + $FF); + finally + CipherAES.Free; + end; +end; + +procedure TestTDECCCM.TestInitFailureIVTooShort; +begin + CheckException(DoTestInitFailureIVTooShort, + EDECNonceLengthException, + 'Init vector too long not detected'); +end; + +procedure TestTDECCCM.TestInitIV; +var + Key : TBytes; + IV : TBytes; + i : Integer; +begin + Key := [1, 2, 3, 4, 5, 6, 7, 8]; + + for i := 7 to 13 do + begin + SetLength(IV, i); + FillChar(IV[0], length(IV), $FF); + + FCipherAES.Init(Key, IV, $FF, pmNone); + Check(true); + end; +end; + +procedure TestTDECCCM.DoTestInitFailureIVTooShort; +var + CipherAES : TCipher_AES; +begin + CipherAES := TCipher_AES.Create; + + try + CipherAES.Mode := cmCCM; + CipherAES.Init(BytesOf(TFormat_HexL.Decode('000102030405060708')), + BytesOf(TFormat_HexL.Decode('0a0b0c0d0e0f')), + $FF); + finally + CipherAES.Free; + end; +end; + +procedure TestTDECCCM.TestEncode; +var + TestDataSet : TAuthenticatedCipherTestSetEntry; + TestData : TSingleAuthenticatedTestData; + EncryptData : TBytes; + EncrDataStr : string; +begin + for TestDataSet in FTestDataList do + begin + for TestData in TestDataSet.TestData do + begin + FCipherAES.Init(BytesOf(TFormat_HexL.Decode(TestData.CryptKey)), + BytesOf(TFormat_HexL.Decode(TestData.InitVector)), + $FF); + + FCipherAES.AuthenticationResultBitLength := TestDataSet.Taglen; + FCipherAES.DataToAuthenticate := TFormat_HexL.Decode( + BytesOf(TestData.AAD)); + + EncryptData := FCipherAES.EncodeBytes( + TFormat_HexL.Decode( + BytesOf(TestData.PT))); + FCipherAES.Done; + + EncrDataStr := StringOf(TFormat_HexL.Encode(EncryptData)); + CheckEquals(string(TestData.CT), + EncrDataStr, + 'Cipher text wrong for Key ' + + string(TestData.CryptKey) + ' IV ' + + string(TestData.InitVector) + ' PT ' + + string(TestData.PT) + ' AAD ' + + string(TestData.AAD) + ' Exp.: ' + + string(TestData.CT) + ' Act.: ' + + EncrDataStr); + + // Additional Authentication Data prüfen + CheckEquals(string(TestData.TagResult), + StringOf(TFormat_HexL.Encode(FCipherAES.CalculatedAuthenticationResult)), + 'Authentication tag wrong for Key ' + + string(TestData.CryptKey) + ' IV ' + + string(TestData.InitVector) + ' PT ' + + string(TestData.PT) + ' AAD ' + + string(TestData.AAD) + ' Exp.: ' + + string(TestData.TagResult) + ' Act.: ' + + StringOf(TFormat_HexL.Encode(FCipherAES.DataToAuthenticate))); + end; + end; +end; + +procedure TestTDECCCM.TestGetExpectedAuthenticationResult; +var + Exp, Act: TBytes; +begin + SetLength(Exp, 4); + Exp := [1, 2, 3, 4]; + FCipherAES.ExpectedAuthenticationResult := Exp; + Act := FCipherAES.ExpectedAuthenticationResult; + + CheckEquals(true, IsEqual(Exp, Act), 'Data length = 4'); + + SetLength(Exp, 0); + FCipherAES.ExpectedAuthenticationResult := Exp; + Act := FCipherAES.ExpectedAuthenticationResult; + + CheckEquals(true, IsEqual(Exp, Act), 'Data length = 0'); +end; + +//procedure TestTDECCCM.DoTestDecodeFailure; +//begin +// FDecryptedData := FCipherAES.DecodeBytes(FCipherText); +// FCipherAES.Done; +//end; + +function TestTDECCCM.IsEqual(const a, b : TBytes):Boolean; +begin + if (length(a) <> length(b)) then + Result := false + else + if (Length(a) > 0) then + Result := CompareMem(@a[0], @b[0], length(a)) + else + Result := true; +end; + +procedure TestTDECCCM.TestDecodeStream; +var + ctbStream: TBytesStream; + ctBytes: TBytes; + TestDataSet : TAuthenticatedCipherTestSetEntry; + TestData : TSingleAuthenticatedTestData; + DecryptData : TBytes; + ptbStream: TBytesStream; +begin + for TestDataSet in FTestDataList do + begin + for TestData in TestDataSet.TestData do + begin + ctBytes := TFormat_HexL.Decode(BytesOf(TestData.CT)); + + try + FCipherAES.Init(BytesOf(TFormat_HexL.Decode(TestData.CryptKey)), + BytesOf(TFormat_HexL.Decode(TestData.InitVector)), + $FF); + + FCipherAES.AuthenticationResultBitLength := TestDataSet.Taglen; + FCipherAES.DataToAuthenticate := TFormat_HexL.Decode( + BytesOf(TestData.AAD)); + + FCipherAES.ExpectedAuthenticationResult := + TFormat_HexL.Decode(BytesOf(TestData.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(TestData.CryptKey) + + ' ' + E.ClassName + ': ' + E.Message); + end; + FreeAndNil(ptbStream); + FreeAndNil(ctbStream); + + CheckEquals(string(TestData.PT), + StringOf(TFormat_HexL.Encode(DecryptData)), + 'Plaintext wrong for key ' + + string(TestData.CryptKey) + ' IV ' + + string(TestData.InitVector) + ' PT ' + + string(TestData.PT) + ' AAD ' + + string(TestData.AAD) + ' Exp.: ' + + string(TestData.CT) + ' Act.: ' + + StringOf(TFormat_HexL.Encode(DecryptData))); + + // Additional Authentication Data prüfen + CheckEquals(string(TestData.TagResult), + StringOf(TFormat_HexL.Encode(FCipherAES.CalculatedAuthenticationResult)), + 'Authentication tag wrong for key ' + + string(TestData.CryptKey) + ' IV ' + + string(TestData.InitVector) + ' PT ' + + string(TestData.PT) + ' AAD ' + + string(TestData.AAD) + ' Exp.: ' + + string(TestData.TagResult) + ' Act.: ' + + StringOf(TFormat_HexL.Encode(FCipherAES.DataToAuthenticate))); + end; + end; +end; + +procedure TestTDECCCM.TestEncodeStream; +begin + // -1 to disable chunking + DoTestEncodeStream_LoadAndTestCAVSData(-1); +end; + +procedure TestTDECCCM.DoTestAuthenticationBitLengthWrong; +begin + FCipherAES.AuthenticationResultBitLength := FTestBitLength; +end; + +//procedure TestTDECCCM.TestEncodeStreamChunked; +//begin +// // Use cipher block size as max chunk size +// DoTestEncodeStream_LoadAndTestCAVSData( +// Max(FCipherAES.Context.BlockSize, FCipherAES.Context.BufferSize)); +//end; +// +procedure TestTDECCCM.DoTestEncodeStream_LoadAndTestCAVSData(const + aMaxChunkSize: Int64); +var + TestDataSet : TAuthenticatedCipherTestSetEntry; + curSetIndex: Integer; +begin + for curSetIndex := 0 to FTestDataList.Count - 1 do + begin + TestDataSet := FTestDataList[curSetIndex]; + DoTestEncodeStream_TestSingleSet(curSetIndex, 0, aMaxChunkSize); + end; +end; +// +//procedure TestTDECCCM.TestEncodeLargeStream; +//begin +// // There is only one record in test data set atm, so need to allow +// // incomplete load +// FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV256_large.rsp', +// FTestDataList, True); +// Status('Encode large stream using chunking'); +// CheckEquals(8192, StreamBufferSize, 'Might need to update data set to have enough data!'); +//{ TODO : Auskommentierten Code entfernen } +//// Assert(StreamBufferSize = 8192, 'Might need to update data set to have enough data!'); +// DoTestEncodeStream_TestSingleSet(0, 0, StreamBufferSize); +// Status('Encode large stream without chunking'); +// DoTestEncodeStream_TestSingleSet(0, 0, -1); +//end; + +procedure TestTDECCCM.DoTestEncodeStream_TestSingleSet(const aSetIndex, + aDataIndex: Integer; const aMaxChunkSize: Int64 = -1); +var + ctbStream: TBytesStream; + curChunkSize: Int64; + dataLeftToEncode: Int64; + ptBytes: TBytes; + TestDataSet : TAuthenticatedCipherTestSetEntry; + TestData : TSingleAuthenticatedTestData; + EncryptData : TBytes; + ptbStream: TBytesStream; +begin + TestDataSet := FTestDataList[aSetIndex]; + + for TestData in TestDataSet.TestData do + begin + ptBytes := TFormat_HexL.Decode(BytesOf(TestData.PT)); + + FCipherAES.Init(BytesOf(TFormat_HexL.Decode(TestData.CryptKey)), + BytesOf(TFormat_HexL.Decode(TestData.InitVector)), + $FF); + + FCipherAES.AuthenticationResultBitLength := TestDataSet.Taglen; + FCipherAES.DataToAuthenticate := TFormat_HexL.Decode( + BytesOf(TestData.AAD)); + + ptbStream := TBytesStream.Create(ptBytes); + ctbStream := TBytesStream.Create; + try + dataLeftToEncode := ptbStream.Size; + curChunkSize := dataLeftToEncode; + repeat + // Apply chunking if needed + if aMaxChunkSize > 0 then + curChunkSize := Min(dataLeftToEncode, aMaxChunkSize); + FCipherAES.EncodeStream(ptbStream, ctbStream, curChunkSize); + Dec(dataLeftToEncode, curChunkSize); + until (dataLeftToEncode = 0); + + FCipherAES.Done; + + EncryptData := ctbStream.Bytes; + SetLength(EncryptData, ctbStream.Size); + except + on E: Exception do + Status('CryptKey ' + string(TestData.CryptKey) + + ' ' + E.ClassName + ': ' + E.Message); + end; + + FreeAndNil(ptbStream); + FreeAndNil(ctbStream); + + CheckEquals(string(TestData.CT), + StringOf(TFormat_HexL.Encode(EncryptData)), + 'Cipher text wrong for Set ' + aSetIndex.ToString + ' and Data ' + aDataIndex.ToString + + ' and Key ' + string(TestData.CryptKey) + ' IV ' + + string(TestData.InitVector) + ' PT ' + + string(TestData.PT) + ' AAD Exp.: ' + + string(TestData.AAD) + ' Act.: ' + + StringOf(TFormat_HexL.Encode(FCipherAES.DataToAuthenticate))); + + // Additional Authentication Data prüfen + CheckEquals(string(TestData.TagResult), + StringOf(TFormat_HexL.Encode(FCipherAES.CalculatedAuthenticationResult)), + 'Authentication tag wrong for Set ' + aSetIndex.ToString + ' and Data ' + aDataIndex.ToString + + ' and Key ' + string(TestData.CryptKey) + ' IV ' + + string(TestData.InitVector) + ' PT ' + + string(TestData.PT) + ' AAD Exp.: ' + + string(TestData.AAD) + ' Act.: ' + + StringOf(TFormat_HexL.Encode(FCipherAES.DataToAuthenticate))); + end; +end; + +procedure TestTDECCCM.TestGetStandardAuthenticationTagBitLengths; +var + BitLengths: TStandardBitLengths; +begin + BitLengths := FCipherAES.GetStandardAuthenticationTagBitLengths; + + CheckEquals( 32, BitLengths[0]); + CheckEquals( 48, BitLengths[1]); + CheckEquals( 64, BitLengths[2]); + CheckEquals( 80, BitLengths[3]); + CheckEquals( 96, BitLengths[4]); + CheckEquals(112, BitLengths[5]); + CheckEquals(128, BitLengths[6]); +end; + +procedure TestTDECCCM.TestSetExpectedAuthenticationResult; +var + Exp, Act: TBytes; +begin + SetLength(Exp, 4); + Exp := [1, 2, 3, 4]; + FCipherAES.ExpectedAuthenticationResult := Exp; + Act := FCipherAES.ExpectedAuthenticationResult; + + CheckEquals(true, IsEqual(Exp, Act), 'Data length = 4'); + + SetLength(Exp, 8); + Exp := [1, 2, 3, 4, 5, 6, 7, 8]; + FCipherAES.ExpectedAuthenticationResult := Exp; + Act := FCipherAES.ExpectedAuthenticationResult; + + CheckEquals(true, IsEqual(Exp, Act), 'Data length = 8'); + + SetLength(Exp, 12); + Exp := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + FCipherAES.ExpectedAuthenticationResult := Exp; + Act := FCipherAES.ExpectedAuthenticationResult; + + CheckEquals(true, IsEqual(Exp, Act), 'Data length = 12'); + + SetLength(Exp, 0); + FCipherAES.ExpectedAuthenticationResult := Exp; + Act := FCipherAES.ExpectedAuthenticationResult; + + CheckEquals(true, IsEqual(Exp, Act), 'Data length = 0'); +end; + +procedure TestTDECCCM.TestSetWrongAuthenticationBitLengths; +var + bl : Integer; +begin + bl := 31; + while (bl < 128) do + begin + FTestBitLength := bl; + CheckException(DoTestAuthenticationBitLengthWrong, EDECAuthLengthException); + inc(bl, 16); + end; + + bl := 33; + while (bl <= 129) do + begin + FTestBitLength := bl; + CheckException(DoTestAuthenticationBitLengthWrong, EDECAuthLengthException); + inc(bl, 16); + end; +end; + +procedure TestTDECCCM.TestSetAuthenticationBitLengths; +var + bl : Integer; +begin + bl := 32; + + while (bl <= 128) do + begin + FCipherAES.AuthenticationResultBitLength := bl; + CheckEquals(bl, FCipherAES.AuthenticationResultBitLength); + + inc(bl, 16); + end; +end; + +procedure TestTDECCCM.TestGetDataToAuthenticate; +var + inp, outp : TBytes; +begin + inp := BytesOf(RawByteString('Hello')); + FCipherAES.DataToAuthenticate := inp; + outp := FCipherAES.DataToAuthenticate; + + CheckEquals(true, CompareMem(@inp[0], @outp[0], length(inp)), + 'Data to authenticate not properly set. Expected: ' + + string(BytesToRawString(inp)) + ' Result: ' + + string(BytesToRawString(outp))); +end; + +procedure TestTDECCCM.TestSetDataToAuthenticate; +begin + FCipherAES.DataToAuthenticate := BytesOf(RawByteString('Hello')); + CheckEquals(RawByteString('Hello'), + RawByteString(StringOf(FCipherAES.DataToAuthenticate))); + + FCipherAES.DataToAuthenticate := BytesOf(RawByteString('The quick brown fox jumped over the lazy dog')); + CheckEquals(RawByteString('The quick brown fox jumped over the lazy dog'), + RawByteString(StringOf(FCipherAES.DataToAuthenticate))); +end; + +procedure TestTDECCCM.TestEncodeRFC3610; +begin + DoTestRFC3610(true); +end; + +procedure TestTDECCCM.TestDecodeRFC3610; +begin + DoTestRFC3610(false); +end; + +procedure TestTDECCCM.DoTestRFC3610(EncodeTest: Boolean); +type + ta25 = array[0..24] of UInt8; + ta10 = array[0..09] of UInt8; +const + ctest: array[1..12] of ta25 = ( + ($58,$8C,$97,$9A,$61,$C6,$63,$D2,$F0,$66,$D0,$C2,$C0,$F9,$89,$80,$6D,$5F,$6B,$61,$DA,$C3,$84,$00,$00), + ($72,$C9,$1A,$36,$E1,$35,$F8,$CF,$29,$1C,$A8,$94,$08,$5C,$87,$E3,$CC,$15,$C4,$39,$C9,$E4,$3A,$3B,$00), + ($51,$B1,$E5,$F4,$4A,$19,$7D,$1D,$A4,$6B,$0F,$8E,$2D,$28,$2A,$E8,$71,$E8,$38,$BB,$64,$DA,$85,$96,$57), + ($A2,$8C,$68,$65,$93,$9A,$9A,$79,$FA,$AA,$5C,$4C,$2A,$9D,$4A,$91,$CD,$AC,$8C,$00,$00,$00,$00,$00,$00), + ($DC,$F1,$FB,$7B,$5D,$9E,$23,$FB,$9D,$4E,$13,$12,$53,$65,$8A,$D8,$6E,$BD,$CA,$3E,$00,$00,$00,$00,$00), + ($6F,$C1,$B0,$11,$F0,$06,$56,$8B,$51,$71,$A4,$2D,$95,$3D,$46,$9B,$25,$70,$A4,$BD,$87,$00,$00,$00,$00), + ($01,$35,$D1,$B2,$C9,$5F,$41,$D5,$D1,$D4,$FE,$C1,$85,$D1,$66,$B8,$09,$4E,$99,$9D,$FE,$D9,$6C,$00,$00), + ($7B,$75,$39,$9A,$C0,$83,$1D,$D2,$F0,$BB,$D7,$58,$79,$A2,$FD,$8F,$6C,$AE,$6B,$6C,$D9,$B7,$DB,$24,$00), + ($82,$53,$1A,$60,$CC,$24,$94,$5A,$4B,$82,$79,$18,$1A,$B5,$C8,$4D,$F2,$1C,$E7,$F9,$B7,$3F,$42,$E1,$97), + ($07,$34,$25,$94,$15,$77,$85,$15,$2B,$07,$40,$98,$33,$0A,$BB,$14,$1B,$94,$7B,$00,$00,$00,$00,$00,$00), + ($67,$6B,$B2,$03,$80,$B0,$E3,$01,$E8,$AB,$79,$59,$0A,$39,$6D,$A7,$8B,$83,$49,$34,$00,$00,$00,$00,$00), + ($C0,$FF,$A0,$D6,$F0,$5B,$DB,$67,$F2,$4D,$43,$A4,$33,$8D,$2A,$A4,$BE,$D7,$B2,$0E,$43,$00,$00,$00,$00)); + ttest: array[1..12] of ta10 = ( + ($17,$E8,$D1,$2C,$FD,$F9,$26,$E0,$00,$00), + ($A0,$91,$D5,$6E,$10,$40,$09,$16,$00,$00), + ($4A,$DA,$A7,$6F,$BD,$9F,$B0,$C5,$00,$00), + ($96,$C8,$61,$B9,$C9,$E6,$1E,$F1,$00,$00), + ($51,$E8,$3F,$07,$7D,$9C,$2D,$93,$00,$00), + ($40,$5A,$04,$43,$AC,$91,$CB,$94,$00,$00), + ($04,$8C,$56,$60,$2C,$97,$AC,$BB,$74,$90), + ($C1,$7B,$44,$33,$F4,$34,$96,$3F,$34,$B4), + ($EA,$9C,$07,$E5,$6B,$5E,$B1,$7E,$5F,$4E), + ($56,$6A,$A9,$40,$6B,$4D,$99,$99,$88,$DD), + ($F5,$3A,$A2,$E9,$10,$7A,$8B,$6C,$02,$2C), + ($CD,$1A,$A3,$16,$62,$E7,$AD,$65,$D6,$DB)); +var + buf, DecodeBuf : array[0..63] of UInt8; + pn : Integer; + key, nonce : TBlock16Byte; + i, ih, it, k : Integer; + plen, tlen, hlen : UInt16; + x : UInt32; + b : UInt8; + CipherAES : TCipher_AES; + DataToAuth : TBytes; + TagResult : TBytes; +begin + nonce[00] := 0; // = init vector + nonce[01] := 0; + nonce[02] := 0; + nonce[07] := $A0; + nonce[08] := $A1; + nonce[09] := $A2; + nonce[10] := $A3; + nonce[11] := $A4; + nonce[12] := $A5; + + pn := 0; + + // key setup, all tests use the same one + for i:= 0 to 15 do + key[i] := $C0 + i; + + for it := 0 to 1 do + begin + tlen := 8 + 2*it; // authentication tag length + + for ih := 0 to 1 do + begin + hlen := 8 + 4*ih; // length of the data to authenticate + SetLength(DataToAuth, hlen); + + for k := 31 to 33 do + begin + pLen := k-hlen; // plain text length? + + x := pn * $01010101+$03020100; + inc(pn); + + // some positions of the nonce/init vector are different for each test + nonce[03] := (x shr 24) and $ff; + nonce[04] := (x shr 16) and $ff; + nonce[05] := (x shr 08) and $ff; + nonce[06] := x and $ff; + + b := 0; + for i := 0 to hlen - 1 do + begin + DataToAuth[i] := b; + inc(b); + end; + + for i := 0 to pLen - 1 do + begin + buf[i] := b; + inc(b); + end; + + CipherAES := TCipher_AES256.Create; + try + CipherAES.Mode := TCipherMode.cmCCM; + CipherAES.DataToAuthenticate := DataToAuth; + CipherAES.AuthenticationResultBitLength := tlen * 8; + + CipherAES.Init(Key[0], Length(Key), Nonce[0], 13, 0, pmNone); + + if EncodeTest then + begin + CipherAES.Encode(buf[0], buf[0], plen); + CheckEquals(true, CompareMem(@buf,@ctest[pn],plen), 'Ciphertext wrong'); + end + else + begin + CipherAES.Decode(ctest[pn], DecodeBuf[0], plen); + CheckEquals(true, CompareMem(@buf,@DecodeBuf,plen), 'Plaintext wrong'); + end; + + TagResult := CipherAES.CalculatedAuthenticationResult; + + // Test the generated tag + CheckEquals(true, CompareMem(@TagResult[0], @ttest[pn],tlen), 'Tag wrong'); + finally + CipherAES.Free; + end; + end; + end; + end; +end; + +initialization + // Register all test cases to be run + {$IFDEF DUnitX} + TDUnitX.RegisterTestFixture(TestTDECCCM); + {$ELSE} + RegisterTest('DEC authenticated cipher modes', TestTDECCCM.Suite); + {$ENDIF} +end. diff --git a/Unit Tests/Tests/TestDECCipherModesGCM.pas b/Unit Tests/Tests/TestDECCipherModesGCM.pas index 0164956..b7f5f6c 100644 --- a/Unit Tests/Tests/TestDECCipherModesGCM.pas +++ b/Unit Tests/Tests/TestDECCipherModesGCM.pas @@ -29,100 +29,17 @@ interface {$ELSE} TestFramework, {$ENDIF} - System.SysUtils, Generics.Collections, System.Math, + System.SysUtils, + Generics.Collections, + System.Math, DECBaseClass, - DECCipherBase, DECCipherModes, DECCipherFormats, DECCiphers; + DECCipherBase, + DECCipherModes, + DECCipherFormats, + DECCiphers, + AuthenticatedCiphersCommonTestData; type - /// - /// Test data for one single GCM test, all in HexL - /// - TGCMSingleTestData = record - /// - /// Encryption/decryption key - /// - CryptKey : RawByteString; - /// - /// Initialization vecotr - /// - InitVector : RawByteString; - /// - /// Plain Text: text to be encrypted, given in HexL - /// - PT : RawByteString; - /// - /// Additional Authenticated Data: the data which shall be authenticated - /// but not encrypted. - /// - AAD : RawByteString; - /// - /// Cipher Text: encrypted text, given in HexL - /// - CT : RawByteString; - /// - /// Calculated authenticated "tag" value - /// - TagResult : RawByteString; - /// - /// Used additional authenticated data for testing authentication failures. - /// Only filled when present in test data file. - /// - ModifiedAAD: RawByteString; - /// - /// Used ciphertext data for testing authentication failures. - /// Only filled when present in test data file. - /// - ModifiedCT: RawByteString; - - /// - /// Sets all fields and array entries to default values - /// - procedure Clear; - end; - - /// - /// Test data for one single GCM test - /// - TGCMTestSetEntry = record - /// - /// Length of the encryption/decryption key in bit, determines the - /// algorithm used in case of AES (AES128, AES192, AES256) - /// - Keylen : UInt16; - /// - /// Length of the initialization vector in bit - /// - IVlen : UInt16; - /// - /// Length of the ? in bit - /// - PTlen : UInt16; - /// - /// Length of the ? in bit - /// - AADlen : UInt16; - /// - /// Length of the "tag" resulting from the authentication part in bit - /// - Taglen : UInt16; - - /// - /// The test data files provided contain 14 tests for the meta data - /// specified above. This array holds the test data. - /// - TestData : array[0..14] of TGCMSingleTestData; - - /// - /// Sets all fields and array entries to default values - /// - procedure Clear; - end; - - /// - /// List of loaded GCM test vectors - /// - TGCMTestDataList = TList; - /// /// Class for loading a GCM style test data file /// @@ -164,7 +81,7 @@ TGCMTestDataLoader = class(TObject) /// the start of a new block has been detected in the method. /// procedure ReadBlockMetaDataLine(const Line : string; - var Entry : TGCMTestSetEntry; + var Entry : TAuthenticatedCipherTestSetEntry; var Index : Byte); /// @@ -187,8 +104,8 @@ TGCMTestDataLoader = class(TObject) /// line will be ignored. /// procedure ReadDataLine(const Line : string; - var Entry : TGCMTestSetEntry; - TestData : TGCMTestDataList; + var Entry : TAuthenticatedCipherTestSetEntry; + TestData : TAuthenticatedTestDataList; var Index : Byte); public /// @@ -204,7 +121,7 @@ TGCMTestDataLoader = class(TObject) /// /// Use when loading data set with incomplete entries. /// - procedure LoadFile(const FileName: string; TestData : TGCMTestDataList; + procedure LoadFile(const FileName: string; TestData : TAuthenticatedTestDataList; AllowIncompleteEntries: Boolean = False); end; @@ -213,7 +130,7 @@ TGCMTestDataLoader = class(TObject) TestTDECGCM = class(TTestCase) strict private FTestDataLoader : TGCMTestDataLoader; - FTestDataList : TGCMTestDataList; + FTestDataList : TAuthenticatedTestDataList; FCipherAES : TCipher_AES; // Needed for passing data to and from DoTestDecodeFailure @@ -256,36 +173,6 @@ implementation DECTypes, DECFormat; -{ TGCMTestSetEntry } - -procedure TGCMTestSetEntry.Clear; -var - i : Integer; -begin - Keylen := 0; - IVlen := 0; - PTlen := 0; - AADlen := 0; - Taglen := 0; - - for i := Low(TestData) to High(TestData) do - TestData[i].Clear; -end; - -{ TGCMSingleTestData } - -procedure TGCMSingleTestData.Clear; -begin - CryptKey := ''; - InitVector := ''; - PT := ''; - AAD := ''; - CT := ''; - TagResult := ''; - ModifiedAAD := ''; - ModifiedCT := ''; -end; - { TGCMTestDataLoader } function TGCMTestDataLoader.ExtractHexString(const Line: string): RawByteString; @@ -318,17 +205,19 @@ function TGCMTestDataLoader.ExtractNumber(const Line: string): UInt16; end; procedure TGCMTestDataLoader.LoadFile(const FileName: string; - TestData : TGCMTestDataList; AllowIncompleteEntries: Boolean = False); + TestData : TAuthenticatedTestDataList; AllowIncompleteEntries: Boolean = False); var Reader : TStreamReader; Line : string; - Entry : TGCMTestSetEntry; + Entry : TAuthenticatedCipherTestSetEntry; Index : Byte; begin System.Assert(FileName <> '', 'No file to load specified'); System.Assert(Assigned(TestData), 'Unassigned test data list given'); + SetLength(Entry.TestData, 15); Entry.Clear; + Index := 0; Reader := TStreamReader.Create(FileName, TEncoding.UTF8); @@ -372,13 +261,14 @@ procedure TGCMTestDataLoader.LoadFile(const FileName: string; end; procedure TGCMTestDataLoader.ReadBlockMetaDataLine(const Line : string; - var Entry : TGCMTestSetEntry; + var Entry : TAuthenticatedCipherTestSetEntry; var Index : Byte); begin // Loading of the block metadata // Does a new block start? if (Pos('[keylen', Line) > 0) then begin + SetLength(Entry.TestData, 15); Entry.Clear; Index := 0; @@ -395,8 +285,8 @@ procedure TGCMTestDataLoader.ReadBlockMetaDataLine(const Line : string; end; procedure TGCMTestDataLoader.ReadDataLine(const Line : string; - var Entry : TGCMTestSetEntry; - TestData : TGCMTestDataList; + var Entry : TAuthenticatedCipherTestSetEntry; + TestData : TAuthenticatedTestDataList; var Index : Byte); begin // Data entries do not contain [ @@ -446,7 +336,7 @@ procedure TestTDECGCM.SetUp; inherited; FTestDataLoader := TGCMTestDataLoader.Create; - FTestDataList := TGCMTestDataList.Create; + FTestDataList := TAuthenticatedTestDataList.Create; FCipherAES := TCipher_AES.Create; FCipherAES.Mode := TCipherMode.cmGCM; @@ -463,7 +353,7 @@ procedure TestTDECGCM.TearDown; procedure TestTDECGCM.TestDecode; var - TestDataSet : TGCMTestSetEntry; + TestDataSet : TAuthenticatedCipherTestSetEntry; i : Integer; DecryptData : TBytes; begin @@ -525,7 +415,7 @@ procedure TestTDECGCM.TestDecode; procedure TestTDECGCM.TestDecodeAuthenticationFailure; var - TestDataSet : TGCMTestSetEntry; + TestDataSet : TAuthenticatedCipherTestSetEntry; i : Integer; begin FTestDataLoader.LoadFile('..\..\Unit Tests\Data\GCM128AuthenticationFailures.rsp', FTestDataList); @@ -569,7 +459,7 @@ procedure TestTDECGCM.TestDecodeAuthenticationFailure; procedure TestTDECGCM.TestEncode; var - TestDataSet : TGCMTestSetEntry; + TestDataSet : TAuthenticatedCipherTestSetEntry; i : Integer; EncryptData : TBytes; EncrDataStr : string; @@ -622,15 +512,16 @@ procedure TestTDECGCM.TestEncode; end; procedure TestTDECGCM.TestEncodeConstData_86; -var cipher : TCipher_AES128; - tag : TBytes; - refPlainText : Array[0..3] of LongWord; - ciphText : Array[0..3] of LongWord; - key : Array[0..3] of LongWord; - iv : Array[0..2] of LongWord; - refCipherText : Array[0..3] of LongWord; - refTag : Array[0..3] of LongWord; - hea : TBytes; +var + cipher : TCipher_AES128; + tag : TBytes; + refPlainText : Array[0..3] of LongWord; + ciphText : Array[0..3] of LongWord; + key : Array[0..3] of LongWord; + iv : Array[0..2] of LongWord; + refCipherText : Array[0..3] of LongWord; + refTag : Array[0..3] of LongWord; + hea : TBytes; type TUINT32Byte = Array[0..3] of Byte; @@ -646,59 +537,60 @@ procedure TestTDECGCM.TestEncodeConstData_86; cExpectedTag : Array[0..3] of LongWord = ($0032A1DC, $85F1C978, $6925A2E7, $1D8272DD); cAESHea : Array[0..3] of LongWord = ( $24825602, $bd12a984, $e0092d3e, $448eda5f ); -function InvUINT32( value : UINT32 ) : UINT32; - -var v1, v2 : PUINT32Byte; -begin - v1 := @value; - v2 := @Result; + function InvUINT32( value : UINT32 ) : UINT32; + var + v1, v2 : PUINT32Byte; + begin + v1 := @value; + v2 := @Result; - v2^[3] := v1^[0]; - v2^[2] := v1^[1]; - v2^[1] := v1^[2]; - v2^[0] := v1^[3]; -end; + v2^[3] := v1^[0]; + v2^[2] := v1^[1]; + v2^[1] := v1^[2]; + v2^[0] := v1^[3]; + end; -procedure InitKey; -var i : integer; -begin - for i := 0 to High(cAESKey) do - key[i] := InvUINT32(cAESKey[i]); + procedure InitKey; + var + i : Integer; + begin + for i := 0 to High(cAESKey) do + key[i] := InvUINT32(cAESKey[i]); - for i := 0 to High(cAESIV) do - iv[i] := InvUINT32(cAESIV[i]); + for i := 0 to High(cAESIV) do + iv[i] := InvUINT32(cAESIV[i]); - for i := 0 to High(refPlainText) do - refPlainText[i] := InvUINT32(cPlainText[i]); + for i := 0 to High(refPlainText) do + refPlainText[i] := InvUINT32(cPlainText[i]); - for i := 0 to High(refCipherText) do - refCipherText[i] := InvUINT32(cCipherText[i]); + for i := 0 to High(refCipherText) do + refCipherText[i] := InvUINT32(cCipherText[i]); - for i := 0 to High(cExpectedTag) do - refTag[i] := InvUINT32(cExpectedTag[i]); + for i := 0 to High(cExpectedTag) do + refTag[i] := InvUINT32(cExpectedTag[i]); - SetLength(hea, sizeof(cAESHea)); - for i := 0 to High(cExpectedTag) do - PLongWord(@hea[i*4])^ := InvUINT32(cAESHea[i]); -end; + SetLength(hea, sizeof(cAESHea)); + for i := 0 to High(cExpectedTag) do + PLongWord(@hea[i*4])^ := InvUINT32(cAESHea[i]); + end; begin - InitKey; - cipher := TCipher_AES128.Create; - try - cipher.Mode := cmGCM; - cipher.Init( key, sizeof(key), iv, sizeof(iv), 0 ); - cipher.AuthenticationResultBitLength := 128; - cipher.DataToAuthenticate := hea; - - cipher.Encode(refPlainText, ciphText, sizeof(refPlainText)); - tag := cipher.CalculatedAuthenticationResult; - finally - cipher.Free; - end; + InitKey; + cipher := TCipher_AES128.Create; + try + cipher.Mode := cmGCM; + cipher.Init( key, sizeof(key), iv, sizeof(iv), 0 ); + cipher.AuthenticationResultBitLength := 128; + cipher.DataToAuthenticate := hea; + + cipher.Encode(refPlainText, ciphText, sizeof(refPlainText)); + tag := cipher.CalculatedAuthenticationResult; + finally + cipher.Free; + end; - Check( CompareMem(@refCipherText[0], @ciphText[0], sizeof(ciphText)), 'Cipher failed'); - Check( CompareMem(@refTag[0], @tag[0], sizeof(refTag)), 'Tag failed'); + Check( CompareMem(@refCipherText[0], @ciphText[0], sizeof(ciphText)), 'Cipher failed'); + Check( CompareMem(@refTag[0], @tag[0], sizeof(refTag)), 'Tag failed'); end; @@ -741,7 +633,7 @@ procedure TestTDECGCM.TestDecodeStream; var ctbStream: TBytesStream; ctBytes: TBytes; - TestDataSet : TGCMTestSetEntry; + TestDataSet : TAuthenticatedCipherTestSetEntry; i : Integer; DecryptData : TBytes; ptbStream: TBytesStream; @@ -831,7 +723,7 @@ procedure TestTDECGCM.DoTestEncodeStream_LoadAndTestCAVSData(const aMaxChunkSize: Int64); var i : Integer; - TestDataSet : TGCMTestSetEntry; + TestDataSet : TAuthenticatedCipherTestSetEntry; curSetIndex: Integer; begin FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV128.rsp', FTestDataList); @@ -870,7 +762,7 @@ procedure TestTDECGCM.DoTestEncodeStream_TestSingleSet(const aSetIndex, curChunkSize: Int64; dataLeftToEncode: Int64; ptBytes: TBytes; - TestDataSet : TGCMTestSetEntry; + TestDataSet : TAuthenticatedCipherTestSetEntry; EncryptData : TBytes; ptbStream: TBytesStream; begin @@ -896,6 +788,13 @@ procedure TestTDECGCM.DoTestEncodeStream_TestSingleSet(const aSetIndex, // Apply chunking if needed if aMaxChunkSize > 0 then curChunkSize := Min(dataLeftToEncode, aMaxChunkSize); +// Darf vermutlich so nicht sein, es darf vermutlich nur einen EncodeStream Aufruf +// geben. Möglicherwiese ist das Padding wie es jetzt umgesetzt ist nicht ganz richtig, +// da man sonst keinen dynamischen Stream haben kann. Gehört vermutlich ins Done, +// aber das hat noch keinen Stream, braucht also eine überladene Variante mit +// Outputstream als Parameter... +// Zuerst test mal ohne Schleife testen. EncodeStream darf nicht anhand der Size +// das "globale" Ende des Streams ermitteln, sonst nichts nachschiebbar. FCipherAES.EncodeStream(ptbStream, ctbStream, curChunkSize); Dec(dataLeftToEncode, curChunkSize); until (dataLeftToEncode = 0); @@ -1003,7 +902,7 @@ initialization {$IFDEF DUnitX} TDUnitX.RegisterTestFixture(TestTDECGCM); {$ELSE} - RegisterTest(TestTDECGCM.Suite); + RegisterTest('DEC authenticated cipher modes', TestTDECGCM.Suite); {$ENDIF} end. diff --git a/Unit Tests/Tests/TestDECHashSHA3.pas b/Unit Tests/Tests/TestDECHashSHA3.pas index 2f74b56..e545fa1 100644 --- a/Unit Tests/Tests/TestDECHashSHA3.pas +++ b/Unit Tests/Tests/TestDECHashSHA3.pas @@ -1755,14 +1755,13 @@ procedure TestTHash_SHA3_Base.AddLastByteForCodeTest(var lDataRow : IHashTest SHA3InputVector : RawByteString; LastByteLength : UInt8); var -// LastByteLen : UInt8; MsgWithFixup : RawByteString; begin MsgWithFixup := AddLastByteForKeccakTest(SHA3InputVector, LastByteLength); //LastByteLen); lDataRow.AddInputVector(MsgWithFixup); - lDataRow.FinalBitLength := LastByteLength; //LastByteLen; + lDataRow.FinalBitLength := LastByteLength; - THash_SHA3Base(FHash).FinalByteLength := LastByteLength; //LastByteLen; + THash_SHA3Base(FHash).FinalByteLength := LastByteLength; lDataRow.ExpectedOutputUTFStrTest := CalcUnicodeHash(string(TFormat_HexL.Encode(MsgWithFixup)), FHash); diff --git a/readme.md b/readme.md index 3b91dda..7fe40e2 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,7 @@ It contains algorithms for these categories: * CRCs: non cryptographic checksums based on CRC algorithms ## Which Delphi versions are compatible? -The current version 6.4.1 is compatible with Delphi XE2 - Delphi 12.3 Athens. +The last release version 6.4.1 is compatible with Delphi XE2 - Delphi 12.3 Athens. When defining the NO_ASM define in DECOptions.inc it is compatible with all platforms supported by Delphi! It might be compatible with FPC, but this has not been focus and is not tested. The development branch contains a more @@ -146,6 +146,7 @@ Modes ending on x have been invented by the original developer of DEC * CFS8 * CFSx * GCM +* CCM ## Contained key derivation algorithms: * KDF1 From 9dfdfd9d9653d715a701ab3a7843b397eb7a8c4f Mon Sep 17 00:00:00 2001 From: Markus Humm Date: Sun, 25 May 2025 16:40:48 +0200 Subject: [PATCH 07/17] Added name of a sponsor to notice.txt Added name of the CCM implementation sponsor --- NOTICE.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/NOTICE.txt b/NOTICE.txt index 52ab4fc..65c9dd4 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -31,6 +31,7 @@ Stevie danielmarschall Christoph Schneider (Schneider Infosystems AG) mikerabat +icTrainer Parts of the work loosely based on the works of Wolfgang Erhardt, who is unfortunately dead already. From 760681ff6f90b645dec964797d1e744e0b12ab9a Mon Sep 17 00:00:00 2001 From: Michael Rabatscher Date: Mon, 16 Jun 2025 23:09:04 +0200 Subject: [PATCH 08/17] Progress: Removed class TCipher_ChaCha_Poly1305 -> now in Mode Still not working. --- Source/DECCipherBase.pas | 3 +- Source/DECCipherModes.pas | 71 +- Source/DECCipherModesPoly1305.pas | 2017 ++++++++++++++++++++ Source/DECCiphers.pas | 1085 ++++++++++- Source/DECOptions.inc | 2 +- Unit Tests/DECDUnitTestSuite.dpr | 31 +- Unit Tests/DECDUnitTestSuite.dproj | 28 + Unit Tests/Tests/TestDECChaChaPoly1305.pas | 353 ++++ 8 files changed, 3578 insertions(+), 12 deletions(-) create mode 100644 Source/DECCipherModesPoly1305.pas create mode 100644 Unit Tests/Tests/TestDECChaChaPoly1305.pas diff --git a/Source/DECCipherBase.pas b/Source/DECCipherBase.pas index f6cecfa..c2efe35 100644 --- a/Source/DECCipherBase.pas +++ b/Source/DECCipherBase.pas @@ -183,7 +183,8 @@ interface cmCFSx, // CFS on Blocksize bytes cmECBx, // Electronic Code Book cmGCM, // Galois Counter Mode - cmCCM // Counter with CBC-MAC Mode + cmCCM, // Counter with CBC-MAC Mode + cmPoly1305 // Poly1305 for ChaCha {$IFDEF DEC3_CMCTS} ,cmCTS3 // double CBC, with less secure padding of truncated final block // for DEC 3.0 compatibility only (see DECOptions.inc) diff --git a/Source/DECCipherModes.pas b/Source/DECCipherModes.pas index e4673bd..7f3e4c0 100644 --- a/Source/DECCipherModes.pas +++ b/Source/DECCipherModes.pas @@ -27,6 +27,7 @@ interface System.SysUtils, {$ENDIF} DECTypes, DECCipherBase, DECCipherModesGCM, DECCipherModesCCM, + DECCipherModesPoly1305, DECCipherInterface; type @@ -155,6 +156,14 @@ TDECCipherModes = class(TDECCipher, IDECAuthenticatedCipher) /// /// Exception raised unconditionally. /// + + + /// + /// Implementation of the Pol1305 polynom. Only created when gmPoly1305 is + /// set as mode. + /// + FPOLY1305 : TPoly1305; + procedure ReportInvalidMessageLength(Cipher: TDECCipher); /// /// Allows to run code after the initialization vector has been initialized @@ -245,7 +254,15 @@ TDECCipherModes = class(TDECCipher, IDECAuthenticatedCipher) /// source length is 0. /// procedure EncodeGCM(Source, Dest: PUInt8Array; Size: Integer); virtual; + /// + /// Poly1305: encryption with addtional optional authentication. + /// Implemented in its own unit and only usable with ChaCha20 (and maybe AES128) - + /// Currently only one class overrides this method. + /// + procedure EncodePoly1305(Source, Dest: PUInt8Array; Size: Integer); virtual; + + /// /// Counter with CBC-MAC Mode: encryption with addtional optional authentication. /// Implemented in its own unit, but needed here to be callable even if /// source length is 0. @@ -338,7 +355,14 @@ TDECCipherModes = class(TDECCipher, IDECAuthenticatedCipher) /// Galois Counter Mode, details are implemented in DECCipherModesGCM /// procedure DecodeGCM(Source, Dest: PUInt8Array; Size: Integer); virtual; + /// + /// Poly1305, details are implemented in DECCipherModesPoly1305. + /// This routine needs to be overridden by successor classes. + /// + procedure DecodePoly1305(Source, Dest: PUInt8Array; Size: Integer); virtual; + + /// /// Counter with CBC-MAC Mode, details are implemented in DECCipherModesCCM /// procedure DecodeCCM(Source, Dest: PUInt8Array; Size: Integer); virtual; @@ -494,6 +518,7 @@ procedure TDECCipherModes.SetDataToAuthenticate(const Value: TBytes); case FMode of cmGCM: FGCM.DataToAuthenticate := Value; cmCCM: FCCM.DataToAuthenticate := Value; + cmPoly1305: FPOLY1305.DataToAuthenticate := Value; else raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); end; @@ -504,6 +529,7 @@ procedure TDECCipherModes.SetExpectedAuthenticationResult(const Value: TBytes); case FMode of cmGCM: FGCM.ExpectedAuthenticationTag := Value; cmCCM: FCCM.ExpectedAuthenticationTag := Value; + cmPoly1305: FPOLY1305.ExpectedAuthenticationTag := Value; else raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); end; @@ -515,6 +541,7 @@ procedure TDECCipherModes.SetAuthenticationResultBitLength( case FMode of cmGCM: FGCM.AuthenticationTagBitLength := Value; cmCCM: FCCM.AuthenticationTagBitLength := Value; + cmPoly1305: FPOLY1305.AuthenticationTagBitLength := Value; else raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); end; @@ -538,6 +565,7 @@ procedure TDECCipherModes.Encode(const Source; var Dest; DataSize: Integer); cmCFS8: EncodeCFS8(@Source, @Dest, DataSize); cmCFSx: EncodeCFSx(@Source, @Dest, DataSize); cmGCM : EncodeGCM(@Source, @Dest, DataSize); + cmPoly1305: EncodePoly1305(@Source, @Dest, DataSize); cmCCM : EncodeCCM(@Source, @Dest, DataSize); end; end; @@ -713,6 +741,7 @@ function TDECCipherModes.GetDataToAuthenticate: TBytes; case FMode of cmGCM: Result := FGCM.DataToAuthenticate; cmCCM: Result := FCCM.DataToAuthenticate; + cmPoly1305: Result := FPOLY1305.DataToAuthenticate; else raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cCCM']); end; @@ -723,6 +752,7 @@ function TDECCipherModes.GetExpectedAuthenticationResult: TBytes; case FMode of cmGCM: Result := FGCM.ExpectedAuthenticationTag; cmCCM: Result := FCCM.ExpectedAuthenticationTag; + cmPoly1305: REsult := FPOLY1305.ExpectedAuthenticationTag; else raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); end; @@ -733,6 +763,7 @@ function TDECCipherModes.GetStandardAuthenticationTagBitLengths: TStandardBitLen case FMode of cmGCM: Result := FGCM.GetStandardAuthenticationTagBitLengths; cmCCM: Result := FCCM.GetStandardAuthenticationTagBitLengths; + cmPoly1305: Result := FPOLY1305.GetStandardAuthenticationTagBitLengths; else begin SetLength(Result, 1); @@ -746,6 +777,7 @@ function TDECCipherModes.GetAuthenticationResultBitLength: Integer; case FMode of cmGCM: Result := FGCM.AuthenticationTagBitLength; cmCCM: Result := FCCM.AuthenticationTagBitLength; + cmPoly1305: Result := FPOLY1305.AuthenticationTagBitLength; else raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); end; @@ -756,6 +788,7 @@ function TDECCipherModes.GetCalcAuthenticatonResult: TBytes; case FMode of cmGCM: Result := FGCM.CalculatedAuthenticationTag; cmCCM: Result := FCCM.CalculatedAuthenticationTag; + cmPoly1305: Result := FPOLY1305.CalculatedAuthenticationTag; else raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); end; @@ -763,15 +796,19 @@ function TDECCipherModes.GetCalcAuthenticatonResult: TBytes; procedure TDECCipherModes.InitMode; begin - if FMode in [TCipherMode.cmGCM, TCipherMode.cmCCM] then + if FMode in [TCipherMode.cmGCM, TCipherMode.cmCCM, TCipherMode.cmPoly1305] then begin if (Context.BlockSize = 16) then begin case FMode of cmGCM: FGCM := TGCM.Create; cmCCM: FCCM := TCCM.Create; + cmPoly1305: FPOLY1305 := TPoly1305.Create; end; end + else if Context.BlockSize < 16 + then + FPOLY1305 := TPoly1305.Create else // GCM and CCM require a cipher with 128 bit block size raise EDECCipherException.CreateResFmt(@sInvalidBlockSize, @@ -785,6 +822,9 @@ procedure TDECCipherModes.InitMode; if Assigned(FCCM) then FreeAndNil(FCCM); + + if Assigned(FPOLY1305) then + FreeAndNil(FPOLY1305); end; end; @@ -886,6 +926,14 @@ procedure TDECCipherModes.EncodeGCM(Source, Dest: PUInt8Array; Size: Integer); FGCM.Encode(Source, Dest, Size); end; +procedure TDECCipherModes.EncodePoly1305(Source, Dest: PUInt8Array; + Size: Integer); +begin + if size < 0 then + Size := 0; + FPOLY1305.Encode(Source, Dest, Size); +end; + procedure TDECCipherModes.EncodeCCM(Source, Dest: PUInt8Array; Size: Integer); begin if (Size < 0) then @@ -937,6 +985,7 @@ procedure TDECCipherModes.Decode(const Source; var Dest; DataSize: Integer); cmCFS8: DecodeCFS8(@Source, @Dest, DataSize); cmCFSx: DecodeCFSx(@Source, @Dest, DataSize); cmGCM : DecodeGCM(@Source, @Dest, DataSize); + cmPoly1305: DecodePoly1305(@Source, @Dest, DataSize); cmCCM : DecodeCCM(@Source, @Dest, DataSize); end; end; @@ -1135,10 +1184,20 @@ procedure TDECCipherModes.DecodeOFBx(Source, Dest: PUInt8Array; Size: Integer); end; end; +procedure TDECCipherModes.DecodePoly1305(Source, Dest: PUInt8Array; + Size: Integer); +begin + if (Size < 0) then + Size := 0; + + FPOLY1305.Decode(Source, Dest, Size); +end; + destructor TDECCipherModes.Destroy; begin FGCM.Free; FCCM.Free; + FPOLY1305.Free; inherited; end; @@ -1159,6 +1218,15 @@ procedure TDECCipherModes.Done; (not IsEqual(FCCM.ExpectedAuthenticationTag, FCCM.CalculatedAuthenticationTag)) then raise EDECCipherAuthenticationException.CreateRes(@sInvalidAuthenticationValue); end; + cmPoly1305: begin + // ########################################### + // #### Now the time is here to create the tag... + FPOLY1305.Finalize; + + if (length(FPOLY1305.ExpectedAuthenticationTag) > 0) and + (not IsEqual(FPOLY1305.ExpectedAuthenticationTag, FPOLY1305.CalculatedAuthenticationTag)) then + raise EDECCipherAuthenticationException.CreateRes(@sInvalidAuthenticationValue); + end; end; end; @@ -1169,6 +1237,7 @@ procedure TDECCipherModes.OnAfterInitVectorInitialization(const OriginalInitVect case FMode of cmGCM: FGCM.Init(self.DoEncode, OriginalInitVector); cmCCM: FCCM.Init(self.DoEncode, OriginalInitVector); + cmPoly1305: FPOLY1305.Init(self.DoEncode, OriginalInitVector); end; end; diff --git a/Source/DECCipherModesPoly1305.pas b/Source/DECCipherModesPoly1305.pas new file mode 100644 index 0000000..5495a6f --- /dev/null +++ b/Source/DECCipherModesPoly1305.pas @@ -0,0 +1,2017 @@ +{***************************************************************************** + The DEC team (see file NOTICE.txt) licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. A copy of this licence is found in the root directory + of this project in the file LICENCE.txt or alternatively at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*****************************************************************************} + +unit DECCipherModesPoly1305; + +interface + +{$INCLUDE DECOptions.inc} + +uses + {$IFDEF FPC} + SysUtils, + {$ELSE} + System.SysUtils, + {$ENDIF} + DECTypes, DECAuthenticatedCipherModesBase; + +// ########################################### +// #### The implementation follows the openssl poly1305 one +// on https://github.com/openssl/openssl/blob/master/crypto/poly1305/poly1305.c +// the avx optimization is based on https://github.com/Sreyosi/Improved-SIMD-Implementation-of-Poly1305/ +type + T32ByteArray = Array[0..31] of Byte; // 0 - 16: key, 17-32 nonce +type + TPoly1305 = class(TAuthenticatedCipherModesBase) + private + const POLY1305_BLOCK_SIZE = 16; + POLY1305_DIGEST_SIZE = 16; + POLY1305_KEY_SIZE = 32; + + POLY1305_SSE2_BLOCK_SIZE = 32; + type + TPoly1305Nonce = Array[0..3] of UInt32; + TPoly1305BlockMethod = procedure ( pData : PByteArray; size : integer ) of Object; + TPoly1305EmitMethod = procedure( var mac: TBlock16Byte ) of Object; + THArr = Array[0..4] of UInt32; + PHArr = ^THArr; + TRArr = Array[0..3] of UInt32; + PRArr = ^TRArr; + TYMM = Array[0..7] of UInt32; + TYMM64 = Array[0..3] of Int64; + TXMM = Array[0..3] of UInt32; + + TPoly1305StateFlag = (poly1305_started = 1, poly1305_final_shift8 = 4, + poly1305_final_shift16 = 8, + poly1305_final_r2_r = 16, // use [r^2,r] for the final block */ + poly1305_final_r_1 = 32 // use [r,1] for the final block */ + ); + + type + TH = packed record + case byte of + 0: (H : Array[0..2] of UInt64); + 1: (HH: Array[0..19] of Uint32); + 2: (H32 : THArr); + end; + + TPoly1305_state_internal_t = packed record + hmac : TH; + R : Array[0..4] of Uint32; + R2 : Array[0..4] of UInt32; + R3 : Array[0..4] of UInt32; + R4 : Array[0..4] of UInt32; + pad : Array[0..1] of UInt64; + flags : Uint64; + end; + PPoly1305_state_internal_t = ^TPoly1305_state_internal_t; + + TPoly1305State = Array[0..191] of Byte; + private + // avx structures + fPoly1305State : TPoly1305State; + fPPoly1305State : PPoly1305_state_internal_t; + + // pas structures + FH : PHArr; + FR : PRArr; + + // + + fIV : T32ByteArray; + FNonce : TPoly1305Nonce; + + FEncryptionMethod : TEncodeDecodeMethod; + + // reminder... + fNum : integer; + fData : Array[0..POLY1305_BLOCK_SIZE - 1] of Byte; + + FAuthDataLen : integer; + FCipherLen : integer; + fPolyInitComplete : boolean; + fPolyBlkFunc : TPoly1305BlockMethod; + fPolyEmitFunc : TPoly1305EmitMethod; + + function U8ToU32( pData : PByteArray ) : UInt32; + procedure U32ToU8(pData : PByteArray; value : UInt32); + + procedure Poly1305Blocks( pData : PByteArray; size : integer; padBit : UInt32 ); + procedure Poly1305Emit( var mac : TBlock16Byte; const nonce : TPoly1305Nonce ); + procedure Poly1305Finalize( var mac : TBlock16Byte ); + + procedure Poly1305Init_SSE( initVec : PByte ); + procedure Poly1305Blocks_SSE( pData : PByteArray; size : integer ); + procedure Poly1305Emit_SSE( var mac : TBlock16Byte ); + + procedure Burn; + protected + // ########################################### + // #### base functionality + + // ########################################### + // #### Authentication block functions + procedure BeginPoly1304Auth; + public + class var UseSSE : boolean; + + constructor Create; + + procedure UpdatePoly( pData : PByteArray; size : integer); + procedure InitInternal(const InitVector : T32ByteArray); + procedure PadAndFinalizeAEAD; + procedure Finalize; + + /// + /// Should be called when starting encryption/decryption in order to + /// initialize internal tables etc. + /// + /// + /// Encryption method of the cypher used + /// + /// + /// Initialization vector + /// + procedure Init(EncryptionMethod : TEncodeDecodeMethod; + InitVector : TBytes); override; + + /// + /// Encodes a block of data using the supplied cipher. + /// The function can be called multiple times. Internally the + /// hash is updated on each call. + /// + procedure Encode(Source, Dest : PUInt8Array; + CiphertextSize : Integer); override; + + /// + /// Decodes a block of data using the supplied cipher. + /// The function can be called multiple times. Internally the + /// hash is updated on each call. + /// + procedure Decode(Source, + Dest : PUInt8Array; + Size : Integer + ); override; + end; + +implementation + +{$R-}{$Q-} + +//CONSTANT_TIME_CARRY(a,b) ( \ +// (a ^ ((a ^ b) | ((a - b) ^ b))) >> (sizeof(a) * 8 - 1) \ +// ) +function ConstTimeCarray32(a, b : UInt32 ) : UInt32; inline; +begin + Result := (a xor ( (a xor b) or (a - b) xor b )) shr 31; +end; + +{ TPoly1305 } + +procedure TPoly1305.BeginPoly1304Auth; +var aLen : integer; + vec : TBytes; +begin + InitInternal(fIV); + + // update the polynom with the unencrypted authentication data + FAuthDataLen := Length(FDataToAuthenticate); + if fAuthDataLen > 0 then + begin + // pad to blocksize if ncessary + aLen := Length(FDataToAuthenticate); + if aLen mod POLY1305_BLOCK_SIZE <> 0 then + begin + aLen := aLen + POLY1305_BLOCK_SIZE - aLen mod POLY1305_BLOCK_SIZE; + SetLength(vec, aLen); + Move(FDataToAuthenticate[0], vec[0], FAuthDataLen); + //vec[FAuthDataLen] := 1; + end + else + vec := FDataToAuthenticate; + fPolyBlkFunc( @vec[0], POLY1305_BLOCK_SIZE); + end; + FCipherLen := 0; +end; + + +procedure TPoly1305.Burn; +begin + FillChar(FH, sizeof(FH), 0); + FillChar(FNonce, sizeof(FNonce), 0); + FillChar(FR, sizeof(FR), 0); + FillChar(fIV, sizeof(fIV), 0); + FillChar(fData, sizeof(fData), 0); + fNum := 0; +end; + +function AlignPtr64( A : Pointer ) : Pointer; +begin + Result := A; + if (NativeUint(A) and $3F) <> 0 then + Result := Pointer( NativeUint(Result) + $40 - NativeUint(Result) and $3F ); +end; + + +constructor TPoly1305.Create; +begin + inherited Create; + + fPPoly1305State := @fPoly1305State[0]; + FH := @fPoly1305State[0]; + FR := @fPoly1305State[sizeof(THarr)]; + + if useSSE then + begin + fPolyBlkFunc := Poly1305Blocks_SSE; // Poly1305Blocks_AVX + fPolyEmitFunc := Poly1305Emit_SSE; + end + else + begin + fPolyBlkFunc := UpdatePoly; + fPolyEmitFunc := Poly1305Finalize; + end; +end; + +procedure TPoly1305.Decode(Source, Dest: PUInt8Array; Size: Integer); +begin + if not fPolyInitComplete then + BeginPoly1304Auth; + + fPolyBlkFunc( PByteArray( Source ), Size ); + inc( FCipherLen, Size ); + + FEncryptionMethod( Source, Dest, Size ); +end; + +procedure TPoly1305.Encode(Source, Dest: PUInt8Array; + CiphertextSize: Integer); +begin + if not fPolyInitComplete then + BeginPoly1304Auth; + + FEncryptionMethod( Source, Dest, CipherTextSize ); + fPolyBlkFunc( PByteArray( dest ), CipherTextSize ); + inc( FCipherLen, CipherTextSize ); +end; + + +procedure TPoly1305.Finalize; +var mac : TBlock16Byte; +begin + if not fPolyInitComplete then + BeginPoly1304Auth; + + fPolyEmitFunc( mac ); + + SetLength(FCalcAuthenticationTag, sizeof(mac)); + Move(mac, FCalcAuthenticationTag[0], sizeof(mac)); + + FillChar(mac, sizeof(mac), 0); + Burn; +end; + +procedure TPoly1305.Init(EncryptionMethod: TEncodeDecodeMethod; + InitVector: TBytes); +begin + inherited; + + // the poly1305 format needs to be initialized correctly from the outside! + // for chacha20 one needs to set initialize the chacha block with the key, + // 96bit nonce and counter to 0 -> the first 32bytes of that chacha scrambled block + // defines the iv + if Length(InitVector) <> ( Length(fIV) ) then + raise Exception.Create('The initVector must be 32Bytes long!'); + + // in this library this only works for chacha since it has an internal counter + // don't know how to deal with aes here... + // build the poly1305 iv from the first 256 bits + FillChar( fIV, sizeof(fIV), 0); + Move( initVector[0], fIV, Length(initVector)); + + FAuthDataLen := 0; + FCipherLen := 0; + FEncryptionMethod := EncryptionMethod; +end; + +procedure TPoly1305.InitInternal(const InitVector: T32ByteArray); +begin + fPolyInitComplete := True; + + if UseSSE then + begin + Poly1305Init_SSE(@initVector[0]); + end + else + begin + FillChar(FH^, sizeof(FH^), 0); + + ///* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ + // st->r[0] = U8TOU32(&key[0]) & 0x0fffffff; + // st->r[1] = U8TOU32(&key[4]) & 0x0ffffffc; + // st->r[2] = U8TOU32(&key[8]) & 0x0ffffffc; + // st->r[3] = U8TOU32(&key[12]) & 0x0ffffffc; + FR^[0] := U8ToU32(@initVector[0]) and $0fffffff; + FR^[1] := U8ToU32(@initVector[4]) and $0ffffffc; + FR^[2] := U8ToU32(@initVector[8]) and $0ffffffc; + FR^[3] := U8ToU32(@initVector[12]) and $0ffffffc; + + + FNonce[0] := U8ToU32(@initVector[16]); + FNonce[1] := U8ToU32(@initVector[20]); + FNonce[2] := U8ToU32(@initVector[24]); + FNonce[3] := U8ToU32(@initVector[28]); + end; + + fNum := 0; + +end; + +procedure TPoly1305.PadAndFinalizeAEAD; +var lens : Array[0..1] of UInt64; +begin + if not fPolyInitComplete then + BeginPoly1304Auth; + + // pad the last block with 0 + if fNum > 0 then + begin + fData[fNum] := 0; + inc(fNum); + while fNum < Length(fData) do + begin + fData[fNum] := 0; + inc(fNum); + end; + + Poly1305Blocks(@fData[0], POLY1305_BLOCK_SIZE, 1); + fNum := 0; + end; + + + // finalize with the tag with the lengths! + lens[0] := FAuthDataLen; + lens[1] := FCipherLen; + Poly1305Blocks(@lens[0], POLY1305_BLOCK_SIZE, 1); + + Finalize; +end; + +procedure TPoly1305.Poly1305Blocks(pData: PByteArray; size: integer; + padBit: UInt32); +var r0, r1, r2, r3 : UInt32; + s1, s2, s3 : UInt32; + h0, h1, h2, h3, h4, c : UInt32; + d0, d1, d2, d3 : UInt64; +begin + //r0 = st->r[0]; +// r1 = st->r[1]; +// r2 = st->r[2]; +// r3 = st->r[3]; + r0 := FR^[0]; + r1 := FR^[1]; + r2 := FR^[2]; + r3 := FR^[3]; + + //s1 = r1 + (r1 >> 2); +// s2 = r2 + (r2 >> 2); +// s3 = r3 + (r3 >> 2); + s1 := r1 + (r1 shr 2); + s2 := r2 + (r2 shr 2); + s3 := r3 + (r3 shr 2); + + //h0 = st->h[0]; +// h1 = st->h[1]; +// h2 = st->h[2]; +// h3 = st->h[3]; +// h4 = st->h[4]; + h0 := FH^[0]; + h1 := FH^[1]; + h2 := FH^[2]; + h3 := FH^[3]; + h4 := FH^[4]; + + while size >= POLY1305_BLOCK_SIZE do + begin + ///* h += m[i] */ +// h0 = (u32)(d0 = (u64)h0 + U8TOU32(inp + 0)); +// h1 = (u32)(d1 = (u64)h1 + (d0 >> 32) + U8TOU32(inp + 4)); +// h2 = (u32)(d2 = (u64)h2 + (d1 >> 32) + U8TOU32(inp + 8)); +// h3 = (u32)(d3 = (u64)h3 + (d2 >> 32) + U8TOU32(inp + 12)); +// h4 += (u32)(d3 >> 32) + padbit; + d0 := UInt64(h0) + U8ToU32(pData); + h0 := UInt32(d0); + d1 := UInt64(h1) + d0 shr 32 + U8ToU32(@pData^[4]); + h1 := UInt32(d1); + d2 := UInt64(h2) + d1 shr 32 + U8ToU32(@pData^[8]); + h2 := UInt32(d2); + d3 := UInt64(h3) + d2 shr 32 + U8ToU32(@pData^[12]); + h3 := UInt32(d3); + h4 := h4 + padbit + UInt32( d3 shr 32); + +// /* h *= r "%" p, where "%" stands for "partial remainder" */ + //d0 = ((u64)h0 * r0) + +// ((u64)h1 * s3) + +// ((u64)h2 * s2) + +// ((u64)h3 * s1); + d0 := UInt64(h0)*r0 + UInt64(h1)*s3 + UInt64(h2)*s2 + UInt64(h3)*s1; + +// d1 = ((u64)h0 * r1) + +// ((u64)h1 * r0) + +// ((u64)h2 * s3) + +// ((u64)h3 * s2) + +// (h4 * s1); + d1 := UInt64(h0)*r1 + UInt64(h1)*r0 + UInt64(h2)*s3 + UInt64(h3)*s2 + h4*s1; + +// d2 = ((u64)h0 * r2) + +// ((u64)h1 * r1) + +// ((u64)h2 * r0) + +// ((u64)h3 * s3) + +// (h4 * s2); + d2 := UInt64(h0)*r2 + UInt64(h1)*r1 + UInt64(h2)*r0 + UInt64(h3)*s3 + h4*s2; + +// d3 = ((u64)h0 * r3) + +// ((u64)h1 * r2) + +// ((u64)h2 * r1) + +// ((u64)h3 * r0) + +// (h4 * s3); +// h4 = (h4 * r0); +// + d3 := UInt64(h0)*r3 + UInt64(h1)*r2 + UInt64(h2)*r1 + UInt64(h3)*r0 + h4*s3; + h4 := h4*r0; + + //* last reduction step: */ + // /* a) h4:h0 = h4<<128 + d3<<96 + d2<<64 + d1<<32 + d0 */ +// h0 = (u32)d0; +// h1 = (u32)(d1 += d0 >> 32); +// h2 = (u32)(d2 += d1 >> 32); +// h3 = (u32)(d3 += d2 >> 32); +// h4 += (u32)(d3 >> 32); + h0 := UInt32(d0); + d1 := d1 + d0 shr 32; + h1 := UInt32(d1); + d2 := d2 + d1 shr 32; + h2 := UInt32(d2); + d3 := d3 + d2 shr 32; + h3 := UInt32(d3); + h4 := h4 + UInt32(d3 shr 32); + + //* b) (h4:h0 += (h4:h0>>130) * 5) %= 2^130 */ +// c = (h4 >> 2) + (h4 & ~3U); +// h4 &= 3; +// h0 += c; +// h1 += (c = CONSTANT_TIME_CARRY(h0,c)); +// h2 += (c = CONSTANT_TIME_CARRY(h1,c)); +// h3 += (c = CONSTANT_TIME_CARRY(h2,c)); +// h4 += CONSTANT_TIME_CARRY(h3,c); + c := (h4 shr 2) + (h4 and (not UInt32(3))); + h4 := h4 and 3; + h0 := h0 + c; + c := ConstTimeCarray32(h0, c); + h1 := h1 + c; + c := ConstTimeCarray32(h1, c); + h2 := h2 + c; + c := ConstTimeCarray32(h2, c); + h3 := h3 + c; + h4 := h4 + ConstTimeCarray32(h3, c); + + inc(PByte(pData), POLY1305_BLOCK_SIZE); + dec(size, POLY1305_BLOCK_SIZE); + end; + + FH^[0] := h0; + FH^[1] := h1; + FH^[2] := h2; + FH^[3] := h3; + FH^[4] := h4; +end; + +procedure TPoly1305.Poly1305Emit(var mac: TBlock16Byte; + const nonce: TPoly1305Nonce); +var h0, h1, h2, h3, h4 : UInt32; + g0, g1, g2, g3, g4 : UInt32; + t: UInt64; + mask : UInt32; +begin + // h0 = st->h[0]; + // h1 = st->h[1]; + //h2 = st->h[2]; +// h3 = st->h[3]; +// h4 = st->h[4]; + h0 := FH^[0]; + h1 := FH^[1]; + h2 := FH^[2]; + h3 := FH^[3]; + h4 := FH^[4]; + + // /* compare to modulus by computing h + -p */ +// g0 = (u32)(t = (u64)h0 + 5); +// g1 = (u32)(t = (u64)h1 + (t >> 32)); +// g2 = (u32)(t = (u64)h2 + (t >> 32)); +// g3 = (u32)(t = (u64)h3 + (t >> 32)); +// g4 = h4 + (u32)(t >> 32); + t := UInt64(h0) + 5; + g0 := UInt32(t); + t := UInt64(h1) + t shr 32; + g1 := UInt32(t); + t := UInt64(h2) + t shr 32; + g2 := UInt32(t); + t := UInt64(h3) + t shr 32; + g3 := UInt32(t); + g4 := h4 + UInt32( t shr 32 ); + + //* if there was carry into 131st bit, h3:h0 = g3:g0 */ + //mask = 0 - (g4 >> 2); +// g0 &= mask; +// g1 &= mask; +// g2 &= mask; +// g3 &= mask; +// mask = ~mask; +// h0 = (h0 & mask) | g0; +// h1 = (h1 & mask) | g1; +// h2 = (h2 & mask) | g2; +// h3 = (h3 & mask) | g3; + mask := UInt32( -(g4 shr 2) ); + g0 := g0 and mask; + g1 := g1 and mask; + g2 := g2 and mask; + g3 := g3 and mask; + mask := not mask; + h0 := (h0 and mask) or g0; + h1 := (h1 and mask) or g1; + h2 := (h2 and mask) or g2; + h3 := (h3 and mask) or g3; + + // /* mac = (h + nonce) % (2^128) */ + //h0 = (u32)(t = (u64)h0 + nonce[0]); +// h1 = (u32)(t = (u64)h1 + (t >> 32) + nonce[1]); +// h2 = (u32)(t = (u64)h2 + (t >> 32) + nonce[2]); +// h3 = (u32)(t = (u64)h3 + (t >> 32) + nonce[3]); + t := UInt64(h0) + nonce[0]; + h0 := UInt32(t); + t := UInt64(h1) + t shr 32 + nonce[1]; + h1 := UInt32(t); + t := UInt64(h2) + t shr 32 + nonce[2]; + h2 := UInt32(t); + t := UInt64(h3) + t shr 32 + nonce[3]; + h3 := UInt32(t); + + U32ToU8(@mac[0], h0); + U32ToU8(@mac[4], h1); + U32ToU8(@mac[8], h2); + U32ToU8(@mac[12], h3); +end; + +type + XMMWORD = Array[0..7] of Word; + +// dump from poly1305-donna-x86-sse2-incremental-source.c +procedure _poly1305_init_ext_sse2(st : TPoly1305.PPoly1305_state_internal_t; key : PByte; bytes : integer); cdecl; +asm + push ebp + pxor xmm0, xmm0 + mov ebp, esp + push edi + push esi + push ebx + and esp, -8 + sub esp, 56 + mov edx, DWORD PTR [ebp+12] + mov eax, DWORD PTR [ebp+8] + movups XMMWORD PTR [eax+32], xmm0 + movups XMMWORD PTR [eax], xmm0 + movups XMMWORD PTR [eax+16], xmm0 + mov ebx, DWORD PTR [edx] + mov eax, DWORD PTR [edx+4] + mov ecx, DWORD PTR [edx+8] + mov edi, DWORD PTR [edx+12] + mov esi, ebx + shr ebx, 26 + and esi, 67108863 + mov DWORD PTR [esp+40], esi + mov esi, eax + shr eax, 20 + sal esi, 6 + or esi, ebx + mov ebx, esi + mov esi, DWORD PTR [esp+40] + and ebx, 67108611 + mov DWORD PTR [esp+36], ebx + mov ebx, ecx + shr ecx, 14 + sal ebx, 12 + or ebx, eax + mov eax, edi + shr edi, 8 + sal eax, 18 + and ebx, 67092735 + and edi, 1048575 + or eax, ecx + mov ecx, eax + mov eax, DWORD PTR [ebp+8] + and ecx, 66076671 + mov DWORD PTR [eax+40], esi + mov esi, eax + mov eax, DWORD PTR [esp+36] + mov DWORD PTR [esi+48], ebx + mov DWORD PTR [esi+52], ecx + mov DWORD PTR [esi+56], edi + mov DWORD PTR [esi+44], eax + mov eax, DWORD PTR [edx+16] + mov DWORD PTR [esi+100], eax + mov eax, DWORD PTR [edx+20] + mov DWORD PTR [esi+104], eax + mov eax, DWORD PTR [edx+24] + mov DWORD PTR [esi+108], eax + mov eax, DWORD PTR [edx+28] + lea edx, [ecx+ecx*4] + mov DWORD PTR [esi+112], eax + mov esi, edx + lea edx, [edi+edi] + mov DWORD PTR [esp+28], edi + lea edi, [ebx+ebx] + mov eax, edi + mov DWORD PTR [esp+48], edx + mul esi + mov DWORD PTR [esp+12], esi + mov DWORD PTR [esp+32], ebx + lea ebx, [ebx+ebx*4] + mov esi, eax + mov eax, DWORD PTR [esp+36] + mov edi, edx + lea eax, [eax+eax*4] + mul DWORD PTR [esp+48] + add esi, eax + mov eax, DWORD PTR [esp+40] + adc edi, edx + mul eax + add esi, eax + adc edi, edx + mov eax, esi + mov edx, edi + mov edi, DWORD PTR [esp+36] + mov DWORD PTR [esp], eax + and eax, 67108863 + mov DWORD PTR [esp+4], edx + lea esi, [edi+edi] + mov DWORD PTR [esp+8], eax + mov eax, ebx + mov DWORD PTR [esp+20], esi + mul DWORD PTR [esp+48] + mov esi, DWORD PTR [esp+40] + lea edi, [esi+esi] + lea esi, [ecx+ecx] + mov DWORD PTR [esp+24], edi + mov edi, edx + mov DWORD PTR [esp+16], esi + mov esi, eax + mov eax, DWORD PTR [esp+20] + mul DWORD PTR [esp+40] + add esi, eax + mov eax, ecx + mov ecx, DWORD PTR [esp+36] + adc edi, edx + mul DWORD PTR [esp+12] + add eax, esi + mov esi, DWORD PTR [esp] + adc edx, edi + mov edi, DWORD PTR [esp+4] + shrd esi, edi, 26 + shr edi, 26 + add esi, eax + mov eax, esi + adc edi, edx + and eax, 67108863 + mov DWORD PTR [esp], eax + mov eax, ecx + mul ecx + mov ecx, eax + mov ebx, edx + mov eax, DWORD PTR [esp+32] + mul DWORD PTR [esp+24] + add ecx, eax + mov eax, DWORD PTR [esp+12] + adc ebx, edx + mul DWORD PTR [esp+48] + add eax, ecx + adc edx, ebx + shrd esi, edi, 26 + shr edi, 26 + add esi, eax + mov eax, esi + adc edi, edx + and eax, 67108863 + mov DWORD PTR [esp+48], eax + mov eax, DWORD PTR [esp+40] + mul DWORD PTR [esp+16] + mov ecx, eax + mov ebx, edx + mov eax, DWORD PTR [esp+20] + mul DWORD PTR [esp+32] + add ecx, eax + adc ebx, edx + mov edx, DWORD PTR [esp+28] + lea eax, [edx+edx*4] + mul edx + add eax, ecx + adc edx, ebx + shrd esi, edi, 26 + shr edi, 26 + add eax, esi + mov DWORD PTR [esp+40], eax + mov ebx, DWORD PTR [esp+32] + adc edx, edi + mov esi, eax + mov DWORD PTR [esp+44], edx + and esi, 67108863 + mov edi, DWORD PTR [esp+48] + mov eax, ebx + mul ebx + mov ecx, eax + mov ebx, edx + mov eax, DWORD PTR [esp+28] + mul DWORD PTR [esp+24] + add ecx, eax + mov eax, DWORD PTR [esp+36] + adc ebx, edx + mul DWORD PTR [esp+16] + add eax, ecx + mov ecx, DWORD PTR [esp+40] + adc edx, ebx + mov ebx, DWORD PTR [esp+44] + shrd ecx, ebx, 26 + shr ebx, 26 + add eax, ecx + adc edx, ebx + mov ecx, eax + mov ebx, DWORD PTR [esp+8] + shrd eax, edx, 26 + mov edx, DWORD PTR [esp] + and ecx, 67108863 + lea eax, [eax+eax*4] + mov DWORD PTR [esp+32], ecx + add eax, ebx + mov ebx, eax + shr eax, 26 + add edx, eax + mov eax, DWORD PTR [ebp+8] + and ebx, 67108863 + mov DWORD PTR [esp+40], ebx + mov DWORD PTR [eax+60], ebx + mov DWORD PTR [eax+64], edx + mov DWORD PTR [eax+68], edi + mov DWORD PTR [eax+72], esi + mov DWORD PTR [eax+76], ecx + lea eax, [esi+esi*4] + mov ebx, eax + lea eax, [ecx+ecx] + lea ecx, [edi+edi] + mov DWORD PTR [esp+36], edx + mov DWORD PTR [esp+28], eax + mov eax, ecx + lea edi, [edi+edi*4] + mul ebx + mov DWORD PTR [esp+12], ebx + mov ecx, eax + mov eax, DWORD PTR [esp+40] + mov ebx, edx + mul eax + add ecx, eax + adc ebx, edx + mov edx, DWORD PTR [esp+36] + lea eax, [edx+edx*4] + mul DWORD PTR [esp+28] + add ecx, eax + adc ebx, edx + mov edx, DWORD PTR [esp+36] + lea eax, [edx+edx] + lea edx, [esi+esi] + mov DWORD PTR [esp+16], eax + mov eax, DWORD PTR [esp+40] + mov DWORD PTR [esp+24], edx + add eax, eax + mov DWORD PTR [esp+20], eax + mov eax, ecx + and eax, 67108863 + mov DWORD PTR [esp+8], eax + mov eax, edi + mul DWORD PTR [esp+28] + mov DWORD PTR [esp], eax + mov eax, esi + mov DWORD PTR [esp+4], edx + mul DWORD PTR [esp+12] + mov esi, eax + mov edi, edx + add esi, DWORD PTR [esp] + mov eax, DWORD PTR [esp+16] + adc edi, DWORD PTR [esp+4] + mul DWORD PTR [esp+40] + add eax, esi + adc edx, edi + shrd ecx, ebx, 26 + shr ebx, 26 + add ecx, eax + mov eax, DWORD PTR [esp+48] + adc ebx, edx + mul DWORD PTR [esp+20] + mov esi, ecx + and esi, 67108863 + mov DWORD PTR [esp], esi + mov esi, eax + mov eax, DWORD PTR [esp+36] + mov edi, edx + mul eax + add esi, eax + mov eax, DWORD PTR [esp+28] + adc edi, edx + mul DWORD PTR [esp+12] + add eax, esi + adc edx, edi + shrd ecx, ebx, 26 + shr ebx, 26 + add eax, ecx + mov ecx, DWORD PTR [esp+32] + adc edx, ebx + mov esi, eax + lea ebx, [ecx+ecx*4] + mov edi, edx + mov eax, ebx + mov DWORD PTR [esp+44], edi + mul ecx + mov ecx, eax + mov ebx, edx + mov eax, DWORD PTR [esp+40] + mov DWORD PTR [esp+40], esi + mul DWORD PTR [esp+24] + add ecx, eax + mov eax, DWORD PTR [esp+16] + adc ebx, edx + mul DWORD PTR [esp+48] + add eax, ecx + adc edx, ebx + shrd esi, edi, 26 + shr edi, 26 + add esi, eax + mov eax, DWORD PTR [esp+24] + adc edi, edx + mul DWORD PTR [esp+36] + mov ecx, eax + mov ebx, edx + mov eax, DWORD PTR [esp+32] + mul DWORD PTR [esp+20] + add ecx, eax + mov eax, DWORD PTR [esp+48] + adc ebx, edx + mul eax + add eax, ecx + mov ecx, esi + adc edx, ebx + shrd ecx, edi, 26 + mov ebx, edi + shr ebx, 26 + add eax, ecx + adc edx, ebx + mov DWORD PTR [esp+48], eax + mov ebx, DWORD PTR [ebp+8] + shrd eax, edx, 26 + mov DWORD PTR [esp+52], edx + lea edx, [eax+eax*4] + mov eax, DWORD PTR [esp+8] + add edx, eax + mov eax, DWORD PTR [esp] + mov ecx, edx + shr edx, 26 + add edx, eax + and ecx, 67108863 + mov DWORD PTR [ebx+80], ecx + mov DWORD PTR [ebx+84], edx + mov edx, DWORD PTR [esp+40] + mov eax, DWORD PTR [esp+48] + mov DWORD PTR [ebx+116], 0 + and edx, 67108863 + mov DWORD PTR [ebx+88], edx + mov edx, esi + and eax, 67108863 + and edx, 67108863 + mov DWORD PTR [ebx+96], eax + mov DWORD PTR [ebx+92], edx + lea esp, [ebp-12] + pop ebx + pop esi + pop edi + pop ebp +end; + +procedure _Poly1305_Blocks_sse2( st : TPoly1305.PPoly1305_state_internal_t; m : PByte; bytes : integer ); cdecl; +label L6, L7, L8, L10, L12, L13, L14, L15, L16, L18, L19, L28, L29; +const LC3 : TPoly1305.TXMM = ( 1, 0, 0, 0 ); + LC4 : TPoly1305.TXMM = ( 16777216, 0, 16777216, 0); + LC5 : TPoly1305.TXMM = ( 67108863, 0, 67108863, 0); + LC6 : TPoly1305.TXMM = ( 5, 0, 5, 0); +asm + push ebp + mov ebp, esp + push edi + push esi + push ebx + and esp, -16 + sub esp, 1168 + mov esi, DWORD PTR [ebp+8] + mov eax, DWORD PTR [ebp+12] + movdqa xmm0, XMMWORD PTR LC4 + mov ecx, DWORD PTR [ebp+16] + mov edx, DWORD PTR [esi+116] + movaps XMMWORD PTR [esp+1136], xmm0 + test dl, 4 + je L6 + movdqa xmm7, xmm0 + psrldq xmm7, 8 + movaps XMMWORD PTR [esp+1136], xmm7 +L6: + test dl, 8 + je L7 + pxor xmm7, xmm7 + movaps XMMWORD PTR [esp+1136], xmm7 +L7: + test dl, 1 + jne L8 + movq xmm0, QWORD PTR [eax+16] + movq xmm1, QWORD PTR [eax] + or edx, 1 + add eax, 32 + movq xmm2, QWORD PTR [eax-8] + sub ecx, 32 + movdqa xmm7, XMMWORD PTR LC5 + movdqa xmm5, XMMWORD PTR LC5 + punpcklqdq xmm1, xmm0 + movq xmm0, QWORD PTR [eax-24] + mov DWORD PTR [esi+116], edx + pand xmm7, xmm1 + punpcklqdq xmm0, xmm2 + movdqa xmm2, xmm1 + psrlq xmm2, 26 + psrlq xmm1, 52 + pand xmm2, XMMWORD PTR LC5 + movaps XMMWORD PTR [esp+1120], xmm2 + movdqa xmm2, xmm0 + psrlq xmm0, 40 + por xmm0, XMMWORD PTR [esp+1136] + psllq xmm2, 12 + por xmm1, xmm2 + movdqa xmm2, xmm0 + pand xmm5, xmm1 + psrlq xmm1, 26 + pand xmm1, XMMWORD PTR LC5 + test dl, 48 + je L10 +L29: + movd xmm3, DWORD PTR [esi+56] + and edx, 16 + movdqu xmm4, XMMWORD PTR [esi+40] + movaps XMMWORD PTR [esp+320], xmm3 + jne L28 + movdqa xmm6, xmm4 + movdqa xmm0, xmm4 + punpckldq xmm6, XMMWORD PTR LC3 + punpckhdq xmm0, XMMWORD PTR LC3 +L12: + pshufd xmm4, xmm6, 80 + movaps XMMWORD PTR [esp+304], xmm4 + pshufd xmm4, xmm6, 250 + movaps XMMWORD PTR [esp+336], xmm4 + pshufd xmm4, xmm0, 80 + pshufd xmm0, xmm0, 250 + movaps XMMWORD PTR [esp+288], xmm4 + movaps XMMWORD PTR [esp+272], xmm0 + jmp L13 + // .p2align 4,,10 + // .p2align 3 +L8: + movdqu xmm1, XMMWORD PTR [esi] + movdqu xmm0, XMMWORD PTR [esi+16] + movdqu xmm7, XMMWORD PTR [esi] + pshufd xmm1, xmm1, 250 + pshufd xmm5, xmm0, 80 + movaps XMMWORD PTR [esp+1120], xmm1 + pshufd xmm1, xmm0, 250 + movdqu xmm0, XMMWORD PTR [esi+32] + pshufd xmm7, xmm7, 80 + pshufd xmm2, xmm0, 80 + test dl, 48 + jne L29 +L10: + movdqu xmm0, XMMWORD PTR [esi+60] + pshufd xmm0, xmm0, 0 + movaps XMMWORD PTR [esp+304], xmm0 + movdqu xmm0, XMMWORD PTR [esi+60] + pshufd xmm0, xmm0, 85 + movaps XMMWORD PTR [esp+336], xmm0 + movdqu xmm0, XMMWORD PTR [esi+60] + pshufd xmm0, xmm0, 170 + movaps XMMWORD PTR [esp+288], xmm0 + movdqu xmm0, XMMWORD PTR [esi+60] + pshufd xmm0, xmm0, 255 + movaps XMMWORD PTR [esp+272], xmm0 + movd xmm0, DWORD PTR [esi+76] + pshufd xmm0, xmm0, 0 + movaps XMMWORD PTR [esp+320], xmm0 +L13: + movdqa xmm0, XMMWORD PTR [esp+336] + movdqa xmm4, XMMWORD PTR [esp+288] + pmuludq xmm0, XMMWORD PTR LC6 + movaps XMMWORD PTR [esp+48], xmm0 + movdqa xmm6, XMMWORD PTR [esp+320] + movdqa xmm0, XMMWORD PTR [esp+272] + pmuludq xmm6, XMMWORD PTR LC6 + movaps XMMWORD PTR [esp+32], xmm6 + pmuludq xmm4, XMMWORD PTR LC6 + pmuludq xmm0, XMMWORD PTR LC6 + cmp ecx, 63 + jbe L14 + movdqu xmm6, XMMWORD PTR [esi+80] + lea edx, [ecx-64] + movaps XMMWORD PTR [esp], xmm0 + movaps XMMWORD PTR [esp+160], xmm0 + and edx, -64 + movdqa xmm0, XMMWORD PTR [esp+1120] + pshufd xmm6, xmm6, 0 + movaps XMMWORD PTR [esp+112], xmm4 + lea edx, [eax+64+edx] + movaps XMMWORD PTR [esp+208], xmm6 + movdqu xmm6, XMMWORD PTR [esi+80] + movaps XMMWORD PTR [esp+16], xmm4 + pshufd xmm3, xmm6, 85 + pshufd xmm6, xmm6, 170 + movaps XMMWORD PTR [esp+256], xmm6 + movdqu xmm6, XMMWORD PTR [esi+80] + movaps XMMWORD PTR [esp+192], xmm3 + pshufd xmm6, xmm6, 255 + movaps XMMWORD PTR [esp+240], xmm6 + movd xmm6, DWORD PTR [esi+96] + pshufd xmm6, xmm6, 0 + movaps XMMWORD PTR [esp+224], xmm6 + movdqa xmm6, XMMWORD PTR LC5 + movaps XMMWORD PTR [esp+1152], xmm6 + movdqa xmm6, XMMWORD PTR [esp+32] + movaps XMMWORD PTR [esp+176], xmm6 + movdqa xmm6, XMMWORD PTR [esp+240] + pmuludq xmm6, XMMWORD PTR LC6 + movaps XMMWORD PTR [esp+144], xmm6 + movdqa xmm6, XMMWORD PTR [esp+224] + pmuludq xmm6, XMMWORD PTR LC6 + movaps XMMWORD PTR [esp+128], xmm6 + movdqa xmm6, XMMWORD PTR [esp+48] + movaps XMMWORD PTR [esp+96], xmm6 + movdqa xmm6, xmm3 + pmuludq xmm6, XMMWORD PTR LC6 + movaps XMMWORD PTR [esp+80], xmm6 + movdqa xmm6, XMMWORD PTR [esp+256] + pmuludq xmm6, XMMWORD PTR LC6 + movaps XMMWORD PTR [esp+64], xmm6 + movdqa xmm6, xmm2 + movdqa xmm2, xmm1 + movdqa xmm1, xmm7 +// .p2align 4,,10 +// .p2align 3 +L15: + movdqa xmm7, XMMWORD PTR [esp+64] + movdqa xmm4, XMMWORD PTR [esp+80] + add eax, 64 + movdqa xmm3, xmm7 + pmuludq xmm4, xmm6 + pmuludq xmm3, xmm2 + movaps XMMWORD PTR [esp+1120], xmm4 + movaps XMMWORD PTR [esp+1104], xmm3 + movdqa xmm3, xmm7 + movdqa xmm7, XMMWORD PTR [esp+144] + pmuludq xmm3, xmm6 + movdqa xmm4, xmm7 + pmuludq xmm4, xmm2 + movaps XMMWORD PTR [esp+1088], xmm3 + movdqa xmm3, xmm7 + pmuludq xmm3, xmm6 + movaps XMMWORD PTR [esp+1072], xmm4 + movdqa xmm4, xmm7 + movdqa xmm7, XMMWORD PTR [esp+128] + pmuludq xmm4, xmm5 + movaps XMMWORD PTR [esp+1056], xmm3 + movdqa xmm3, xmm7 + pmuludq xmm3, xmm5 + movaps XMMWORD PTR [esp+1040], xmm4 + movdqa xmm4, xmm7 + pmuludq xmm4, xmm6 + movaps XMMWORD PTR [esp+992], xmm3 + movdqa xmm3, XMMWORD PTR [esp+208] + movaps XMMWORD PTR [esp+1024], xmm4 + movdqa xmm4, xmm7 + pmuludq xmm6, xmm3 + pmuludq xmm4, xmm0 + movaps XMMWORD PTR [esp+944], xmm6 + movdqa xmm6, xmm3 + movaps XMMWORD PTR [esp+1008], xmm4 + movdqa xmm4, xmm7 + pmuludq xmm6, xmm1 + pmuludq xmm4, xmm2 + movaps XMMWORD PTR [esp+928], xmm6 + movdqa xmm6, xmm3 + movaps XMMWORD PTR [esp+976], xmm4 + movdqa xmm4, xmm3 + pmuludq xmm6, xmm0 + pmuludq xmm4, xmm2 + movaps XMMWORD PTR [esp+912], xmm6 + movdqa xmm6, xmm3 + movaps XMMWORD PTR [esp+960], xmm4 + pmuludq xmm6, xmm5 + movdqa xmm4, XMMWORD PTR [esp+192] + pmuludq xmm2, xmm4 + movdqa xmm3, xmm4 + pmuludq xmm3, xmm5 + movaps XMMWORD PTR [esp+896], xmm6 + movdqa xmm6, XMMWORD PTR [esp+256] + movaps XMMWORD PTR [esp+864], xmm2 + movdqa xmm2, xmm4 + pmuludq xmm5, xmm6 + pmuludq xmm2, xmm1 + movaps XMMWORD PTR [esp+880], xmm3 + movaps XMMWORD PTR [esp+800], xmm5 + movdqa xmm5, xmm6 + movaps XMMWORD PTR [esp+848], xmm2 + movdqa xmm2, xmm4 + pmuludq xmm5, xmm1 + pmuludq xmm2, xmm0 + movaps XMMWORD PTR [esp+784], xmm5 + movaps XMMWORD PTR [esp+832], xmm2 + movdqa xmm2, xmm6 + pmuludq xmm2, xmm0 + movaps XMMWORD PTR [esp+816], xmm2 + movdqa xmm2, XMMWORD PTR [esp+240] + movdqa xmm5, xmm2 + pmuludq xmm0, xmm2 + pmuludq xmm5, xmm1 + movaps XMMWORD PTR [esp+752], xmm0 + movaps XMMWORD PTR [esp+768], xmm5 + pmuludq xmm1, XMMWORD PTR [esp+224] + movq xmm0, QWORD PTR [eax-64] + movq xmm4, QWORD PTR [eax-56] + movaps XMMWORD PTR [esp+736], xmm1 + movq xmm1, QWORD PTR [eax-48] + movdqa xmm6, XMMWORD PTR [esp+96] + movdqa xmm7, XMMWORD PTR [esp+112] + punpcklqdq xmm0, xmm1 + movq xmm1, QWORD PTR [eax-40] + punpcklqdq xmm4, xmm1 + movdqa xmm1, xmm0 + movdqa xmm2, xmm4 + psrlq xmm1, 26 + movdqa xmm3, xmm4 + psrlq xmm4, 40 + psrlq xmm3, 14 + por xmm4, XMMWORD PTR [esp+1136] + pand xmm3, XMMWORD PTR [esp+1152] + psllq xmm2, 12 + pmuludq xmm6, xmm4 + movdqa xmm5, xmm2 + movdqa xmm2, xmm0 + psrlq xmm2, 52 + por xmm2, xmm5 + pand xmm2, XMMWORD PTR [esp+1152] + movaps XMMWORD PTR [esp+720], xmm6 + movdqa xmm6, xmm7 + pmuludq xmm6, xmm3 + movaps XMMWORD PTR [esp+704], xmm6 + movdqa xmm6, xmm7 + movdqa xmm7, XMMWORD PTR [esp+160] + pmuludq xmm6, xmm4 + movdqa xmm5, xmm7 + pmuludq xmm5, xmm2 + movaps XMMWORD PTR [esp+688], xmm6 + movdqa xmm6, xmm7 + pmuludq xmm6, xmm3 + movaps XMMWORD PTR [esp+640], xmm5 + movaps XMMWORD PTR [esp+672], xmm6 + movdqa xmm6, xmm7 + movdqa xmm7, XMMWORD PTR [esp+1152] + pmuludq xmm6, xmm4 + pand xmm1, xmm7 + pand xmm0, xmm7 + movaps XMMWORD PTR [esp+656], xmm6 + movdqa xmm6, XMMWORD PTR [esp+176] + movdqa xmm5, xmm6 + pmuludq xmm5, xmm4 + movaps XMMWORD PTR [esp+624], xmm5 + movdqa xmm5, xmm6 + pmuludq xmm5, xmm1 + movaps XMMWORD PTR [esp+608], xmm5 + movdqa xmm5, xmm6 + pmuludq xmm5, xmm2 + movaps XMMWORD PTR [esp+592], xmm5 + movdqa xmm5, xmm6 + pmuludq xmm5, xmm3 + movaps XMMWORD PTR [esp+576], xmm5 + movdqa xmm5, XMMWORD PTR [esp+304] + movdqa xmm6, xmm5 + pmuludq xmm4, xmm5 + pmuludq xmm6, xmm3 + movaps XMMWORD PTR [esp+544], xmm4 + movdqa xmm4, xmm5 + movaps XMMWORD PTR [esp+560], xmm6 + movdqa xmm6, xmm5 + pmuludq xmm4, xmm0 + pmuludq xmm6, xmm1 + movaps XMMWORD PTR [esp+528], xmm6 + movdqa xmm6, xmm5 + pmuludq xmm6, xmm2 + movaps XMMWORD PTR [esp+512], xmm6 + movdqa xmm6, XMMWORD PTR [esp+336] + movdqa xmm5, xmm6 + pmuludq xmm3, xmm6 + pmuludq xmm5, xmm2 + movaps XMMWORD PTR [esp+480], xmm3 + movaps XMMWORD PTR [esp+496], xmm5 + movdqa xmm5, xmm6 + pmuludq xmm5, xmm0 + movaps XMMWORD PTR [esp+464], xmm5 + movdqa xmm5, xmm6 + movdqu xmm7, XMMWORD PTR [eax-16] + paddq xmm4, XMMWORD PTR [esp+608] + pmuludq xmm5, xmm1 + movaps XMMWORD PTR [esp+448], xmm5 + movdqa xmm5, XMMWORD PTR [esp+288] + pmuludq xmm2, xmm5 + movdqa xmm3, xmm5 + pmuludq xmm3, xmm1 + movaps XMMWORD PTR [esp+416], xmm2 + movdqa xmm2, xmm5 + pmuludq xmm2, xmm0 + movaps XMMWORD PTR [esp+432], xmm3 + movdqa xmm3, XMMWORD PTR [esp+272] + pmuludq xmm1, xmm3 + movaps XMMWORD PTR [esp+400], xmm2 + movdqa xmm2, xmm3 + pmuludq xmm2, xmm0 + pmuludq xmm0, XMMWORD PTR [esp+320] + movaps XMMWORD PTR [esp+368], xmm0 + movdqu xmm0, XMMWORD PTR [eax-32] + movaps XMMWORD PTR [esp+384], xmm1 + punpckldq xmm0, xmm7 + movdqa xmm1, xmm0 + movdqu xmm0, XMMWORD PTR [eax-32] + movdqa xmm3, xmm1 + punpckhdq xmm0, xmm7 + movdqa xmm5, xmm2 + pxor xmm2, xmm2 + movdqa xmm7, xmm0 + punpckldq xmm3, xmm2 + punpckhdq xmm1, xmm2 + punpckldq xmm7, xmm2 + psllq xmm1, 6 + movdqa xmm6, xmm3 + paddq xmm5, XMMWORD PTR [esp+432] + movdqa xmm3, xmm2 + movdqa xmm2, xmm7 + psllq xmm2, 12 + punpckhdq xmm0, xmm3 + movaps XMMWORD PTR [esp+352], xmm2 + psllq xmm0, 18 + movdqa xmm2, XMMWORD PTR [esp+1008] + paddq xmm2, XMMWORD PTR [esp+1040] + movdqa xmm3, xmm2 + movdqa xmm2, XMMWORD PTR [esp+1120] + paddq xmm2, XMMWORD PTR [esp+1104] + paddq xmm2, xmm3 + movdqa xmm3, XMMWORD PTR [esp+720] + paddq xmm3, XMMWORD PTR [esp+928] + paddq xmm3, xmm2 + movdqa xmm2, XMMWORD PTR [esp+640] + paddq xmm2, XMMWORD PTR [esp+704] + paddq xmm2, xmm3 + paddq xmm2, xmm4 + movdqa xmm4, XMMWORD PTR [esp+816] + paddq xmm4, XMMWORD PTR [esp+880] + paddq xmm2, xmm6 + movdqa xmm3, xmm4 + movdqa xmm7, xmm2 + movdqa xmm4, XMMWORD PTR [esp+1024] + paddq xmm4, XMMWORD PTR [esp+960] + psrlq xmm7, 26 + paddq xmm4, xmm3 + movdqa xmm3, XMMWORD PTR [esp+624] + paddq xmm3, XMMWORD PTR [esp+768] + paddq xmm4, xmm3 + movdqa xmm3, XMMWORD PTR [esp+496] + paddq xmm3, XMMWORD PTR [esp+560] + paddq xmm4, xmm3 + paddq xmm4, xmm5 + movdqa xmm5, xmm7 + paddq xmm4, xmm0 + paddq xmm1, xmm5 + movdqa xmm0, XMMWORD PTR [esp+912] + paddq xmm0, XMMWORD PTR [esp+992] + movdqa xmm7, xmm4 + movdqa xmm6, XMMWORD PTR [esp+1088] + paddq xmm6, XMMWORD PTR [esp+1072] + psrlq xmm7, 26 + movdqa xmm3, xmm7 + paddq xmm3, XMMWORD PTR [esp+1136] + paddq xmm6, xmm0 + movdqa xmm0, XMMWORD PTR [esp+688] + paddq xmm0, XMMWORD PTR [esp+848] + paddq xmm0, xmm6 + movdqa xmm6, XMMWORD PTR [esp+592] + paddq xmm6, XMMWORD PTR [esp+672] + paddq xmm0, xmm6 + movdqa xmm6, XMMWORD PTR [esp+464] + paddq xmm6, XMMWORD PTR [esp+528] + paddq xmm0, xmm6 + movdqa xmm6, XMMWORD PTR [esp+944] + paddq xmm6, XMMWORD PTR [esp+864] + paddq xmm0, xmm1 + movdqa xmm1, XMMWORD PTR [esp+752] + paddq xmm1, XMMWORD PTR [esp+800] + paddq xmm6, xmm1 + movdqa xmm1, XMMWORD PTR [esp+544] + paddq xmm1, XMMWORD PTR [esp+736] + paddq xmm1, xmm6 + movdqa xmm6, XMMWORD PTR [esp+416] + paddq xmm6, XMMWORD PTR [esp+480] + paddq xmm1, xmm6 + movdqa xmm6, XMMWORD PTR [esp+384] + paddq xmm6, XMMWORD PTR [esp+368] + movdqa xmm5, xmm6 + paddq xmm5, xmm1 + movdqa xmm7, xmm5 + movdqa xmm5, XMMWORD PTR [esp+832] + paddq xmm5, XMMWORD PTR [esp+896] + paddq xmm7, xmm3 + movdqa xmm3, XMMWORD PTR [esp+1056] + paddq xmm3, XMMWORD PTR [esp+976] + movdqa xmm6, xmm7 + movdqa xmm7, xmm0 + paddq xmm3, xmm5 + psrlq xmm7, 26 + movdqa xmm1, xmm6 + movdqa xmm5, XMMWORD PTR [esp+656] + paddq xmm5, XMMWORD PTR [esp+784] + psrlq xmm1, 26 + pmuludq xmm1, XMMWORD PTR LC6 + paddq xmm5, xmm3 + movdqa xmm3, XMMWORD PTR [esp+512] + paddq xmm3, XMMWORD PTR [esp+576] + paddq xmm3, xmm5 + movdqa xmm5, XMMWORD PTR [esp+448] + paddq xmm5, XMMWORD PTR [esp+400] + paddq xmm3, xmm5 + movdqa xmm5, XMMWORD PTR [esp+352] + paddq xmm5, xmm7 + movdqa xmm7, XMMWORD PTR [esp+1152] + paddq xmm5, xmm3 + pand xmm2, xmm7 + pand xmm4, xmm7 + pand xmm0, xmm7 + paddq xmm1, xmm2 + movdqa xmm2, xmm5 + pand xmm6, xmm7 + movdqa xmm3, xmm1 + psrlq xmm2, 26 + pand xmm5, xmm7 + psrlq xmm3, 26 + paddq xmm2, xmm4 + pand xmm1, xmm7 + paddq xmm0, xmm3 + movdqa xmm3, xmm2 + pand xmm2, xmm7 + psrlq xmm3, 26 + paddq xmm6, xmm3 + cmp eax, edx + jne L15 + movaps XMMWORD PTR [esp+1120], xmm0 + movdqa xmm4, XMMWORD PTR [esp+16] + movdqa xmm7, xmm1 + and ecx, 63 + movdqa xmm0, XMMWORD PTR [esp] + movdqa xmm1, xmm2 + movdqa xmm2, xmm6 +L14: + cmp ecx, 31 + jbe L16 + movdqa xmm6, XMMWORD PTR [esp+48] + movdqa xmm3, XMMWORD PTR [esp+32] + pmuludq xmm6, xmm2 + movaps XMMWORD PTR [esp+1152], xmm6 + movdqa xmm6, xmm1 + pmuludq xmm6, xmm4 + pmuludq xmm4, xmm2 + movaps XMMWORD PTR [esp+1104], xmm6 + movdqa xmm6, xmm1 + pmuludq xmm6, xmm0 + movaps XMMWORD PTR [esp+1088], xmm4 + movaps XMMWORD PTR [esp+1072], xmm6 + movdqa xmm6, xmm2 + pmuludq xmm6, xmm0 + pmuludq xmm0, xmm5 + movaps XMMWORD PTR [esp+1056], xmm6 + movdqa xmm6, xmm5 + movdqa xmm4, xmm0 + movdqa xmm0, xmm2 + pmuludq xmm6, xmm3 + pmuludq xmm0, xmm3 + movaps XMMWORD PTR [esp+1024], xmm6 + movdqa xmm6, xmm3 + pmuludq xmm6, xmm1 + movaps XMMWORD PTR [esp+1040], xmm0 + movdqa xmm0, XMMWORD PTR [esp+1120] + pmuludq xmm0, xmm3 + movaps XMMWORD PTR [esp+1008], xmm6 + movdqa xmm6, XMMWORD PTR [esp+304] + pmuludq xmm2, xmm6 + movdqa xmm3, xmm6 + paddq xmm0, xmm4 + movdqa xmm4, XMMWORD PTR [esp+1152] + paddq xmm4, XMMWORD PTR [esp+1104] + pmuludq xmm3, xmm1 + paddq xmm0, xmm4 + movdqa xmm4, XMMWORD PTR [esp+1120] + movaps XMMWORD PTR [esp+976], xmm2 + movdqa xmm2, xmm6 + pmuludq xmm2, xmm7 + movaps XMMWORD PTR [esp+992], xmm3 + movdqa xmm3, xmm6 + pmuludq xmm3, XMMWORD PTR [esp+1120] + movdqa xmm6, xmm3 + movdqa xmm3, XMMWORD PTR [esp+304] + paddq xmm6, XMMWORD PTR [esp+1024] + pmuludq xmm3, xmm5 + paddq xmm0, xmm2 + movdqa xmm2, XMMWORD PTR [esp+336] + movaps XMMWORD PTR [esp+1104], xmm0 + movdqa xmm0, xmm2 + pmuludq xmm1, xmm2 + pmuludq xmm0, xmm7 + movaps XMMWORD PTR [esp+960], xmm3 + movdqa xmm3, XMMWORD PTR [esp+336] + pmuludq xmm3, xmm5 + movaps XMMWORD PTR [esp+928], xmm1 + movdqa xmm1, xmm0 + movdqa xmm0, xmm2 + pmuludq xmm0, xmm4 + movdqa xmm4, XMMWORD PTR [esp+1088] + paddq xmm4, XMMWORD PTR [esp+1072] + movaps XMMWORD PTR [esp+944], xmm3 + movdqa xmm3, XMMWORD PTR [esp+288] + paddq xmm6, xmm4 + movdqa xmm4, xmm6 + pmuludq xmm5, xmm3 + movdqa xmm2, xmm3 + movdqa xmm6, XMMWORD PTR [esp+272] + pmuludq xmm2, XMMWORD PTR [esp+1120] + paddq xmm4, xmm1 + movaps XMMWORD PTR [esp+1152], xmm4 + movdqa xmm4, xmm3 + movdqa xmm1, xmm6 + movdqa xmm3, XMMWORD PTR [esp+1120] + pmuludq xmm4, xmm7 + pmuludq xmm1, xmm7 + paddq xmm0, XMMWORD PTR [esp+960] + paddq xmm2, XMMWORD PTR [esp+944] + pmuludq xmm3, xmm6 + movdqa xmm6, XMMWORD PTR [esp+320] + pmuludq xmm6, xmm7 + movdqa xmm7, XMMWORD PTR [esp+1056] + paddq xmm7, XMMWORD PTR [esp+1008] + paddq xmm0, xmm7 + paddq xmm0, xmm4 + paddq xmm3, xmm5 + movdqa xmm4, XMMWORD PTR [esp+1040] + paddq xmm4, XMMWORD PTR [esp+992] + paddq xmm2, xmm4 + paddq xmm2, xmm1 + movdqa xmm1, XMMWORD PTR [esp+976] + paddq xmm1, XMMWORD PTR [esp+928] + paddq xmm3, xmm1 + paddq xmm3, xmm6 + test eax, eax + je L18 + movdqu xmm5, XMMWORD PTR [eax+16] + movdqu xmm1, XMMWORD PTR [eax] + paddq xmm3, XMMWORD PTR [esp+1136] + punpckldq xmm1, xmm5 + movdqa xmm4, xmm1 + movdqu xmm1, XMMWORD PTR [eax] + movdqa xmm6, xmm4 + punpckhdq xmm1, xmm5 + pxor xmm5, xmm5 + punpckldq xmm6, xmm5 + punpckhdq xmm4, xmm5 + movdqa xmm7, xmm6 + psllq xmm4, 6 + movdqa xmm6, xmm1 + punpckldq xmm6, xmm5 + punpckhdq xmm1, xmm5 + movdqa xmm5, XMMWORD PTR [esp+1104] + psllq xmm6, 12 + psllq xmm1, 18 + paddq xmm5, xmm7 + paddq xmm0, xmm6 + movaps XMMWORD PTR [esp+1104], xmm5 + paddq xmm2, xmm1 + movdqa xmm5, XMMWORD PTR [esp+1152] + paddq xmm5, xmm4 + movaps XMMWORD PTR [esp+1152], xmm5 +L18: + movdqa xmm7, XMMWORD PTR [esp+1104] + movdqa xmm4, xmm2 + pand xmm2, XMMWORD PTR LC5 + psrlq xmm4, 26 + movdqa xmm1, xmm7 + paddq xmm4, xmm3 + pand xmm7, XMMWORD PTR LC5 + psrlq xmm1, 26 + movdqa xmm3, xmm4 + paddq xmm1, XMMWORD PTR [esp+1152] + pand xmm4, XMMWORD PTR LC5 + psrlq xmm3, 26 + pmuludq xmm3, XMMWORD PTR LC6 + paddq xmm7, xmm3 + movdqa xmm5, xmm1 + movdqa xmm3, xmm7 + pand xmm1, XMMWORD PTR LC5 + psrlq xmm5, 26 + psrlq xmm3, 26 + pand xmm7, XMMWORD PTR LC5 + paddq xmm0, xmm5 + paddq xmm3, xmm1 + movdqa xmm1, XMMWORD PTR LC5 + movdqa xmm6, xmm0 + movaps XMMWORD PTR [esp+1120], xmm3 + pand xmm0, XMMWORD PTR LC5 + psrlq xmm6, 26 + paddq xmm2, xmm6 + movdqa xmm5, xmm0 + movdqa xmm0, xmm2 + pand xmm1, xmm2 + psrlq xmm0, 26 + paddq xmm4, xmm0 + movdqa xmm2, xmm4 +L16: + test eax, eax + je L19 + pshufd xmm3, xmm5, 8 + pshufd xmm4, xmm7, 8 + pshufd xmm5, xmm1, 8 + pshufd xmm6, XMMWORD PTR [esp+1120], 8 + pshufd xmm0, xmm2, 8 + movdqa xmm1, xmm3 + movdqa xmm2, xmm4 + punpcklqdq xmm2, xmm6 + punpcklqdq xmm1, xmm5 + movq QWORD PTR [esi+32], xmm0 + movups XMMWORD PTR [esi], xmm2 + movups XMMWORD PTR [esi+16], xmm1 + lea esp, [ebp-12] + pop ebx + pop esi + pop edi + pop ebp + ret +// .p2align 4,,10 +// .p2align 3 +L28: + movdqu xmm6, XMMWORD PTR [esi+60] + movdqu xmm0, XMMWORD PTR [esi+60] + punpckldq xmm6, xmm4 + punpckhdq xmm0, xmm4 + movd xmm4, DWORD PTR [esi+76] + punpcklqdq xmm4, xmm3 + movaps XMMWORD PTR [esp+320], xmm4 + jmp L12 +// .p2align 4,,10 +// .p2align 3 +L19: + movdqa xmm3, xmm1 + movdqa xmm6, xmm7 + movdqa xmm0, xmm5 + movdqa xmm4, XMMWORD PTR [esp+1120] + psrldq xmm3, 8 + psrldq xmm6, 8 + psrldq xmm4, 8 + psrldq xmm0, 8 + paddq xmm7, xmm6 + movaps XMMWORD PTR [esp+1136], xmm3 + movdqa xmm3, XMMWORD PTR [esp+1120] + movd edx, xmm7 + movaps XMMWORD PTR [esp+1152], xmm0 + paddq xmm5, XMMWORD PTR [esp+1152] + paddq xmm1, XMMWORD PTR [esp+1136] + mov edi, edx + shr edx, 26 + movdqa xmm0, xmm2 + paddq xmm3, xmm4 + psrldq xmm0, 8 + and edi, 67108863 + movd eax, xmm3 + paddq xmm2, xmm0 + add eax, edx + movd edx, xmm5 + mov ebx, eax + shr eax, 26 + add eax, edx + movd edx, xmm1 + and ebx, 67108863 + mov ecx, eax + shr eax, 26 + add eax, edx + and ecx, 67108863 + movd edx, xmm2 + mov DWORD PTR [esp+1152], ecx + mov ecx, eax + shr eax, 26 + add eax, edx + and ecx, 67108863 + mov edx, eax + shr eax, 26 + lea eax, [eax+eax*4] + and edx, 67108863 + add eax, edi + mov edi, eax + shr eax, 26 + add eax, ebx + and edi, 67108863 + mov ebx, eax + shr eax, 26 + and ebx, 67108863 + mov DWORD PTR [esp+1120], ebx + mov ebx, DWORD PTR [esp+1152] + add eax, ebx + mov ebx, eax + shr eax, 26 + add eax, ecx + and ebx, 67108863 + mov ecx, eax + shr eax, 26 + mov DWORD PTR [esp+1152], ebx + mov ebx, DWORD PTR [esp+1120] + add eax, edx + and ecx, 67108863 + mov DWORD PTR [esp+1136], ecx + mov edx, eax + mov ecx, eax + and edx, 67108863 + shr ecx, 26 + mov DWORD PTR [esp+1104], edx + lea edx, [ecx+ecx*4] + add edx, edi + mov ecx, edx + shr edx, 26 + and ecx, 67108863 + add edx, ebx + or eax, -67108864 + lea ebx, [ecx+5] + mov DWORD PTR [esp+1088], ebx + shr ebx, 26 + lea edi, [ebx+edx] + mov DWORD PTR [esp+1072], edi + shr edi, 26 + mov ebx, edi + mov edi, DWORD PTR [esp+1152] + add ebx, edi + mov edi, DWORD PTR [esp+1136] + mov DWORD PTR [esp+1056], ebx + shr ebx, 26 + add ebx, edi + mov DWORD PTR [esp+1040], ebx + shr ebx, 26 + add eax, ebx + mov edi, eax + shr edi, 31 + mov ebx, edi + mov edi, eax + sar edi, 31 + sub ebx, 1 + and edx, edi + and ecx, edi + and eax, ebx + mov DWORD PTR [esp+1120], edx + mov edx, DWORD PTR [esp+1152] + and edx, edi + mov DWORD PTR [esp+1152], edx + mov edx, DWORD PTR [esp+1136] + and edx, edi + mov DWORD PTR [esp+1136], edx + mov edx, DWORD PTR [esp+1104] + and edx, edi + mov edi, DWORD PTR [esp+1088] + mov DWORD PTR [esp+1104], edx + and edi, ebx + and edi, 67108863 + or edi, ecx + mov ecx, DWORD PTR [esp+1072] + mov DWORD PTR [esi], edi + mov edi, DWORD PTR [esp+1120] + and ecx, ebx + and ecx, 67108863 + or ecx, edi + mov DWORD PTR [esi+4], ecx + mov ecx, DWORD PTR [esp+1056] + mov edi, DWORD PTR [esp+1152] + and ecx, ebx + mov edx, ecx + mov ecx, DWORD PTR [esp+1040] + and edx, 67108863 + or edx, edi + and ecx, ebx + mov edi, DWORD PTR [esp+1136] + mov DWORD PTR [esi+8], edx + mov edx, ecx + and edx, 67108863 + or edx, edi + mov DWORD PTR [esi+12], edx + mov edx, DWORD PTR [esp+1104] + or eax, edx + mov DWORD PTR [esi+16], eax + lea esp, [ebp-12] + pop ebx + pop esi + pop edi + pop ebp +end; + +procedure _Poly1305_finish_SSE2(st : TPoly1305.PPoly1305_state_internal_t; m : PByte; leftover : integer; var mac : TBlock16Byte) cdecl; +label L63, L33, L34, L35, L36, L37, L38, L42, L43, L64, L65, L99; +asm + push ebp + mov ebp, esp + push edi + push esi + push ebx + and esp, -16 + sub esp, 64 + mov ebx, DWORD PTR [ebp+8] + mov edx, DWORD PTR [ebp+16] + mov eax, DWORD PTR [ebx+116] + mov DWORD PTR [esp+28], eax + test edx, edx + je L63 + mov edi, DWORD PTR [ebp+12] + pxor xmm0, xmm0 + lea esi, [esp+32] + movaps XMMWORD PTR [esp+32], xmm0 + mov ecx, esi + movaps XMMWORD PTR [esp+48], xmm0 + sub edi, esi + test BYTE PTR [ebp+16], 16 + je L34 + mov eax, DWORD PTR [ebp+12] + lea ecx, [esp+48] + movdqu xmm4, XMMWORD PTR [eax] + movaps XMMWORD PTR [esp+32], xmm4 +L34: + test BYTE PTR [ebp+16], 8 + je L35 + mov eax, DWORD PTR [ecx+edi] + mov edx, DWORD PTR [ecx+4+edi] + add ecx, 8 + mov DWORD PTR [ecx-8], eax + mov DWORD PTR [ecx-4], edx +L35: + test BYTE PTR [ebp+16], 4 + je L36 + mov eax, DWORD PTR [ecx+edi] + add ecx, 4 + mov DWORD PTR [ecx-4], eax +L36: + test BYTE PTR [ebp+16], 2 + je L37 + movzx eax, WORD PTR [ecx+edi] + add ecx, 2 + mov WORD PTR [ecx-2], ax +L37: + test BYTE PTR [ebp+16], 1 + je L38 + movzx edx, BYTE PTR [ecx+edi] + mov BYTE PTR [ecx], dl +L38: + cmp DWORD PTR [ebp+16], 16 + je L65 + mov eax, DWORD PTR [ebp+16] + mov edx, DWORD PTR [esp+28] + mov BYTE PTR [esp+32+eax], 1 + cmp eax, 15 + jbe L42 + or edx, 4 + mov DWORD PTR [ebx+116], edx + mov DWORD PTR [esp+8], 32 + mov DWORD PTR [esp+4], esi + mov DWORD PTR [esp], ebx + call _Poly1305_Blocks_sse2 + mov eax, DWORD PTR [ebx+116] + mov DWORD PTR [esp+28], eax +L63: + test al, 1 + je L33 + mov edx, DWORD PTR [esp+28] + or edx, 16 +L43: + mov DWORD PTR [ebx+116], edx + mov DWORD PTR [esp+8], 32 + mov DWORD PTR [esp+4], 0 + mov DWORD PTR [esp], ebx + call _Poly1305_Blocks_sse2 +L33: + mov esi, DWORD PTR [ebx+4] + mov edx, DWORD PTR [ebx+8] + mov ecx, DWORD PTR [ebx+12] + mov eax, esi + mov edi, esi + mov esi, edx + shr edi, 6 + sal esi, 20 + or esi, edi + mov edi, edx + mov edx, ecx + shr ecx, 18 + shr edi, 12 + sal edx, 14 + or edx, edi + mov edi, ecx + mov ecx, DWORD PTR [ebx+16] + sal eax, 26 + or eax, DWORD PTR [ebx] + sal ecx, 8 + or ecx, edi + //APP + // # 526 "poly1305-donna-x86-sse2-incremental-source - Kopie.c" 1 + //addl DWORD PTR [ebx+100], eax; +// adcl DWORD PTR [ebx+104], esi; +// adcl DWORD PTR [ebx+108], edx; +// adcl DWORD PTR [ebx+112], ecx; + add DWORD PTR [ebx+100], eax; + adc DWORD PTR [ebx+104], esi; + adc DWORD PTR [ebx+108], edx; + adc DWORD PTR [ebx+112], ecx; + + //# 0 "" 2 + //NO_APP + pxor xmm0, xmm0 + movd xmm3, esi + movd xmm1, edx + movups XMMWORD PTR [ebx], xmm0 + movd xmm2, ecx + movups XMMWORD PTR [ebx+16], xmm0 + punpckldq xmm1, xmm2 + movups XMMWORD PTR [ebx+32], xmm0 + movups XMMWORD PTR [ebx+48], xmm0 + movups XMMWORD PTR [ebx+64], xmm0 + movups XMMWORD PTR [ebx+80], xmm0 + movups XMMWORD PTR [ebx+96], xmm0 + movups XMMWORD PTR [ebx+112], xmm0 + movd xmm0, eax + mov eax, DWORD PTR [ebp+20] + punpckldq xmm0, xmm3 + punpcklqdq xmm0, xmm1 + movups XMMWORD PTR [eax], xmm0 + lea esp, [ebp-12] + pop ebx + pop esi + pop edi + pop ebp + + jmp L99; + +// now that is some kind of epiolog +L65: + mov edx, DWORD PTR [esp+28] + or edx, 4 +L64: + mov DWORD PTR [ebx+116], edx + mov DWORD PTR [esp+8], 32 + mov DWORD PTR [esp+4], esi + mov DWORD PTR [esp], ebx + call _poly1305_blocks_sse2 + mov edx, DWORD PTR [ebx+116] + test dl, 1 + je L33 + or edx, 32 + jmp L43 +// .p2align 4,,10 +// .p2align 3 +L42: + or edx, 8 + jmp L64 + +// return: +L99: +end; + +procedure TPoly1305.Poly1305Emit_SSE(var mac: TBlock16Byte); +begin + FillChar(mac, sizeof(mac), 0); + //nonce is not used - it is already stored + _Poly1305_finish_SSE2( fPPoly1305State, nil, 0, mac) +end; + + +procedure TPoly1305.Poly1305Finalize(var mac: TBlock16Byte); +begin + if fNum > 0 then + begin + fData[fNum] := 1; + inc(fNum); + while fNum < Length(fData) do + begin + fData[fNum] := 0; + inc(fNum); + end; + + Poly1305Blocks(@fData[0], POLY1305_BLOCK_SIZE, 0); + fNum := 0; + end; + + Poly1305Emit(mac, FNonce); +end; + +procedure TPoly1305.Poly1305Init_SSE(initVec: PByte); +begin + FillChar(fPoly1305State[0], Length(fPoly1305State), 0); + + _poly1305_init_ext_sse2( fPPoly1305State, initVec, 0); +end; + +procedure TPoly1305.Poly1305Blocks_SSE(pData: PByteArray; size: integer); +begin + _Poly1305_Blocks_sse2( fPPoly1305State, PByte(pData), size ); +end; + +procedure TPoly1305.U32ToU8(pData: PByteArray; value: UInt32); +begin + pData^[0] := Byte(value); + pData^[1] := Byte(value shr 8); + pData^[2] := Byte(value shr 16); + pData^[3] := Byte(value shr 24); +end; + +function TPoly1305.U8ToU32(pData: PByteArray): UInt32; +begin + Result := (UInt32(pData^[0]) and $ff) or + ((UInt32(pData^[1]) and $ff) shl 8) or + ((UInt32(pData^[2]) and $ff) shl 16) or + ((UInt32(pData^[3]) and $ff) shl 24); +end; + +procedure TPoly1305.UpdatePoly(pData: PByteArray; size: integer); +var num : integer; + rem : integer; +begin + num := fNum; + + if num <> 0 then + begin + rem := POLY1305_BLOCK_SIZE - num; + if size >= rem then + begin + Move( pData^, fData[num], rem); + Poly1305Blocks(@fData[0], POLY1305_BLOCK_SIZE, 1); + inc( PByte(pData), rem); + dec( size, rem); + end + else + begin + //* Still not enough data to process a block. */ + move( pData^, fData[num], size ); + inc(fNum, size); + exit; + end; + end; + + rem := size mod POLY1305_BLOCK_SIZE; + size := size - rem; + + if size >= POLY1305_BLOCK_SIZE then + begin + Poly1305Blocks(pData, size, 1); + inc(PByte(pData), size); + end; + + if rem > 0 then + Move( pData^, fData[0], rem ); + + fNum := rem; +end; + +initialization + TPoly1305.UseSSE := False; + +end. diff --git a/Source/DECCiphers.pas b/Source/DECCiphers.pas index d91593a..6da6e74 100644 --- a/Source/DECCiphers.pas +++ b/Source/DECCiphers.pas @@ -20,8 +20,13 @@ interface {$INCLUDE DECOptions.inc} -uses - DECCipherBase, DECCipherFormats, DECUtil, DECTypes; +uses {$IFDEF FPC} + SysUtils, + {$ELSE} + System.SysUtils, + {$ENDIF} + DECCipherBase, DECCipherFormats, DECUtil, DECTypes, DECCipherMOdesPoly1305, + System.Types; type // Cipher Classes @@ -201,6 +206,11 @@ TCipher_TEAN = class; /// TCipher_XTEA_DEC52 = class; + /// + /// ChaCha20 cipher. + /// + TCipher_ChaCha20 = class; + // Definitions needed for Skipjack algorithm PSkipjackTab = ^TSkipjackTab; TSkipjackTab = array[0..255] of Byte; @@ -477,6 +487,65 @@ TCipher_AES256 = class(TCipher_AES) class function Context: TCipherContext; override; end; + TChaChaMode = (cmSpeed, cmBalance, cmSecure); // number of rounds... (4, 12, 20) default is 20 + TChaChaMtx = Array[0..15] of LongWord; + PChaChaMtx = ^TChaChaMtx; + TChaChaCpuMode = (cmPas, cmSSE, cmAVX); + TChaChaAVXMtx = Array[0..31] of LongWord; + PChaChaAVXMtx = ^TChaChaAVXMtx; + + TCipher_ChaCha20 = class(TDECFormattedCipher) + private + type + TChaChaKey = Array[0..7] of LongWord; // key - always 256 bit + TChaChaNonce = Array[0..1] of LongWord; // nonce - 96 bit, one longword is the counter + + type + // double the size -> two blocks at once + TChaChaEncodeBlkFunc = procedure(ChaChaMtx : PChaChaAVXMtx; Source, Dest: Pointer); register; {$IFDEF FPC} assembler; {$ENDIF} + + private + fInpChaChaMTX : PChaChaMtx; + fOutChaChaMtx : PChaChaAVXMtx; + + fChaChaBlkLen : integer; + fChaChaIdx : integer; + fChaChaMode : TChaChaMode; + fNumChaChaRounds : integer; + fFullBlockFunc : TChaChaEncodeBlkFunc; + + procedure ChaChaQuarterRound( var a, b, c, d : LongWord ); + procedure PasChaChaDoubleQuarterRound(mtx : PChaChaMtx); // one column and row round + + procedure InitChaChaBlk; + {$IFNDEF PUREPASCAL} + procedure SSEChaChaDoubleQuarterRound(mtx : PChaChaMtx); register; {$IFDEF FPC} assembler; {$ENDIF} + {$ENDIF} + procedure SetChaChaMode(const Value: TChaChaMode); + protected + procedure DoInit(const Key; Size: Integer); override; + procedure DoEncode(Source, Dest: Pointer; Size: Integer); override; + procedure DoDecode(Source, Dest: Pointer; Size: Integer); override; + procedure OnAfterInitVectorInitialization(const OriginalInitVector: TBytes); override; + + // some internal test functions + function TestChaChaMtx( const expectedMtx : TChaChaMtx ) : boolean; + public + property ChaChaMode : TChaChaMode read fChaChaMode write SetChaChaMode; + + procedure AfterConstruction; override; + + /// + /// Provides meta data about the cipher algorithm used like key size. + /// + class function Context: TCipherContext; override; + + /// + /// Set to true if the routine shall use SSE instructinos to build the chacha matrix + /// + class var CpuMode : TChaChaCpuMode; + end; + TCipher_Square = class(TDECFormattedCipher) protected /// @@ -1020,11 +1089,6 @@ implementation {$IFOPT R+}{$DEFINE RESTORE_RANGECHECKS}{$R-}{$ENDIF} uses - {$IFDEF FPC} - SysUtils, - {$ELSE} - System.SysUtils, - {$ENDIF} DECData, DECDataCipher; { TCipher_Null } @@ -6786,12 +6850,1019 @@ procedure TCipher_XTEA_DEC52.DoDecode(Source, Dest: Pointer; Size: Integer); PUInt32Array(Dest)[1] := Y; end; +{ TCipher_ChaCha20 } + +{$IFDEF FPC} {$ASMMODE intel} {$S-} {$ENDIF} + +{$IFDEF CPUX64} +{$DEFINE x64} +{$ENDIF} +{$IFDEF cpux86_64} +{$DEFINE x64} +{$ENDIF} + +{$IFDEF CPU86} +{$DEFINE x86} +{$ENDIF} +{$IFDEF CPUX86} +{$DEFINE x86} +{$ENDIF} + +{$IFDEF CPU386} +{$DEFINE x86} +{$ENDIF} + + +{$IF defined(MRMATH_NOASM)} +function rol(value: LongWord; Bits: Byte): LongWord; +begin + Result := (value shl Bits) or (value shr (32 - bits)); +end; +{$ELSE} +function rol(value: LongWord; Bits: Byte): LongWord; assembler; +{$IFDEF FPC} +begin +{$ENDIF} +asm + {$IF defined(x64)} + mov eax, ecx; + mov cl, dl; + rol eax, cl; + {$ELSE} + xchg cl,dl + rol eax, cl + {$IFEND} +end; +{$IFDEF FPC} +end; +{$ENDIF} +{$IFEND} + +procedure TCipher_ChaCha20.ChaChaQuarterRound(var a, b, c, d: LongWord); +begin + // ########################################### + // #### + // a += b; d ^= a; d <<<= 16; + // c += d; b ^= c; b <<<= 12; + // a += b; d ^= a; d <<<= 8; + // c += d; b ^= c; b <<<= 7; + a := a + b; + d := d xor a; + d := rol(d, 16); + + c := c + d; + b := b xor c; + b := rol(b, 12); + + a := a + b; + d := d xor a; + d := rol(d, 8); + + c := c + d; + b := b xor c; + b := rol(b, 7); +end; + + +class function TCipher_ChaCha20.Context: TCipherContext; +begin + Result.KeySize := 256; + Result.BlockSize := 1; + Result.BufferSize := 16; + Result.AdditionalBufferSize := $40 + 3*sizeof(TChaChaMtx); + Result.NeedsAdditionalBufferBackup := False; + Result.MinRounds := 1; + Result.MaxRounds := 1; + Result.CipherType := [ctSymmetric, ctStream]; +end; + + +procedure TCipher_ChaCha20.DoDecode(Source, Dest: Pointer; Size: Integer); +begin + // should be the same + DoEncode( Source, Dest, size ); +end; + +procedure TCipher_ChaCha20.DoEncode(Source, Dest: Pointer; Size: Integer); +var pChaCha : PByte; + + procedure TryEncodeFullBlocks; + begin + // ########################################### + // #### Full block version -> + if fChaChaIdx = 0 then + begin + while size >= sizeof(TChaChaAVXMtx) do + begin + fFullBlockFunc(fOutChaChaMtx, Source, Dest); + + inc(PByte(Source), sizeof(TChaChaAVXMtx)); + inc(PByte(Dest), sizeof(TChaChaAVXMtx)); + dec(size, sizeof(TChaChaAVXMtx)); + + InitChaChaBlk; + end; + end; + end; +begin + pChaCha := PByte(fOutChaChaMtx); + inc(pChaCha, fChaChaIdx); + + TryEncodeFullBlocks; + + // ########################################### + // #### Single byte version + while size > 0 do + begin + if fChaChaIdx = fChaChaBlkLen then + begin + InitChaChaBlk; + pChaCha := PByte(fOutChaChaMtx); + + if size >= sizeof(TChaChaAVXMtx) then + begin + TryEncodeFullBlocks; + if size = 0 then + break; + end; + end; + + PByte(dest)^ := PByte(Source)^ xor pChaCha^; + + inc(pChaCha); + inc(PByte(dest)); + inc(PByte(source)); + inc(fChaChaIdx); + + dec(size); + end; +end; + +procedure TCipher_ChaCha20.OnAfterInitVectorInitialization( + const OriginalInitVector: TBytes); +var iv : TBytes; +begin + if FInitVectorSize <> 12 then + raise Exception.Create('Nonce is not 96 bit.'); + + fInpChaChaMTX^[12] := 0; // counter + Move( FInitializationVector^, fInpChaChaMTX^[13], 3*sizeof(longword)); + + // special care in case of poly1305: + if FMode = cmPoly1305 then + begin + // according to RFC7539 (chapter 2.6) we create the R and S (the IV vector) value as: + // block counter is 0 key and nonce (96 or 64 bits) + // build iv by applying the key/nonce pair on the first "block" which results + // in an 512bit vector -> use the first 256 bit as IV and discard the remaining one + // -> update the counter to 1 and setup the next block + + SetLength(iv, 32); + FillChar(iv[0], 32, 0); + + // init with count 0 + fInpChaChaMTX^[12] := 0; + InitChaChaBlk; + DoEncode(@iv[0], @iv[0], Length(iv)); + + inherited OnAfterInitVectorInitialization( iv ); + + // dismiss the remaining block -> + // first block was for the polynom... increment block number for the rest + fChaChaIdx := sizeof(TChaChaMtx); + + // setup complete -> We are ready to encrypt... + end + else + inherited; +end; + +procedure TCipher_ChaCha20.DoInit(const Key; Size: Integer); +// from chacha-prng.h +const cChaChaConst : Array[0..15] of AnsiChar = 'expand 32-byte k'; + +function AlignPtr32( A : Pointer ) : Pointer; +begin + Result := A; + if (NativeUint(A) and $1F) <> 0 then + Result := Pointer( NativeUint(Result) + $20 - NativeUint(Result) and $1F ); +end; + +begin + inherited; + + if size <> 32 then + raise Exception.Create('Given ChaCha key size is not 256 bit'); + + // allocate for the AVX case -> 2 cha cha matrices at once + fInpChaChaMtx := AlignPtr32(FAdditionalBuffer); + fOutChaChaMTX := PChaChaAVXMtx( fInpChaChaMtx ); + inc(PByte(fOutChaChaMTX), sizeof(TChaChaMtx)); + + Move(cChaChaConst[0], fInpChaChaMTX^[0], sizeof(cChaChaConst)); + Move(key, fInpChaChaMTX^[4], 32); + + fChaChaBlkLen := sizeof(TChaChaAvxMtx);// 2*Length(fInpChaChaMTX^)*sizeof(fInpChaChaMTX^[0]); + fChaChaIdx := fChaChaBlkLen; +end; + +procedure FullBlockPas(ChaChaMtx : PChaChaAVXMtx; Source, Dest: Pointer); register; +var i, j : integer; +begin + // xor the complete block + for i := 0 to 7 do + begin + j := i shl 2; + PChaChaAVXMtx(Dest)^[j + 0] := PChaChaAVXMtx(Source)^[j + 0] xor ChaChaMtx^[j + 0]; + PChaChaAVXMtx(Dest)^[j + 1] := PChaChaAVXMtx(Source)^[j + 1] xor ChaChaMtx^[j + 1]; + PChaChaAVXMtx(Dest)^[j + 2] := PChaChaAVXMtx(Source)^[j + 2] xor ChaChaMtx^[j + 2]; + PChaChaAVXMtx(Dest)^[j + 3] := PChaChaAVXMtx(Source)^[j + 3] xor ChaChaMtx^[j + 3]; + end; +end; + +procedure TCipher_ChaCha20.SetChaChaMode(const Value: TChaChaMode); +begin + fChaChaMode := Value; + + // secure would be 20 rounds + case fChaChaMode of + cmSpeed: fNumChaChaRounds := 4; + cmBalance: fNumChaChaRounds := 6; + cmSecure: fNumChaChaRounds := 10; + end; +end; + +// ########################################### +// #### AVX chacha assembler code +// ########################################### + +{$IFNDEF PUREPASCAL} + +{$IFDEF x86} + +procedure FullBlockSSE(ChaChaMtx : PChaChaAVXMtx; Source, Dest: Pointer); register; {$IFDEF FPC}assembler;{$ENDIF} +// eax = ChaChaMtx, edx = source, ecx = dest +asm + movapd xmm0, [eax]; + movupd xmm1, [edx]; + xorpd xmm0, xmm1; + movupd [ecx], xmm0; + + movapd xmm0, [eax + 16]; + movupd xmm1, [edx + 16]; + xorpd xmm0, xmm1; + movupd [ecx + 16], xmm0; + + movapd xmm0, [eax + 32]; + movupd xmm1, [edx + 32]; + xorpd xmm0, xmm1; + movupd [ecx + 32], xmm0; + + movapd xmm0, [eax + 48]; + movupd xmm1, [edx + 48]; + xorpd xmm0, xmm1; + movupd [ecx + 48], xmm0; + + movapd xmm0, [eax + 64]; + movupd xmm1, [edx + 64]; + xorpd xmm0, xmm1; + movupd [ecx + 64], xmm0; + + movapd xmm0, [eax + 80]; + movupd xmm1, [edx + 80]; + xorpd xmm0, xmm1; + movupd [ecx + 80], xmm0; + + movapd xmm0, [eax + 96]; + movupd xmm1, [edx + 96]; + xorpd xmm0, xmm1; + movupd [ecx + 96], xmm0; + + movapd xmm0, [eax + 112]; + movupd xmm1, [edx + 112]; + xorpd xmm0, xmm1; + movupd [ecx + 112], xmm0; +end; + +procedure FullBlockAVX(ChaChaMtx : PChaChaAVXMtx; Source, Dest: Pointer); register; {$IFDEF FPC}assembler;{$ENDIF} +// eax = ChaChaMtx, edx = source, ecx = dest +asm + {$IFDEF AVXSUP}vmovapd ymm0, [eax]; {$ELSE}db $C5,$FD,$28,$00;{$ENDIF} + {$IFDEF AVXSUP}vmovupd ymm1, [edx]; {$ELSE}db $C5,$FD,$10,$0A;{$ENDIF} + {$IFDEF AVXSUP}vxorpd ymm0, ymm1, ymm0; {$ELSE}db $C5,$F5,$57,$C0;{$ENDIF} + {$IFDEF AVXSUP}vmovupd [ecx], ymm0; {$ELSE}db $C5,$FD,$11,$01;{$ENDIF} + + {$IFDEF AVXSUP}vmovapd ymm0, [eax + 32]; {$ELSE}db $C5,$FD,$28,$40,$20;{$ENDIF} + {$IFDEF AVXSUP}vmovupd ymm1, [edx + 32]; {$ELSE}db $C5,$FD,$10,$4A,$20;{$ENDIF} + {$IFDEF AVXSUP}vxorpd ymm0, ymm1, ymm0; {$ELSE}db $C5,$F5,$57,$C0;{$ENDIF} + {$IFDEF AVXSUP}vmovupd [ecx + 32], ymm0; {$ELSE}db $C5,$FD,$11,$41,$20;{$ENDIF} + + {$IFDEF AVXSUP}vmovapd ymm0, [eax + 64]; {$ELSE}db $C5,$FD,$28,$40,$40;{$ENDIF} + {$IFDEF AVXSUP}vmovupd ymm1, [edx + 64]; {$ELSE}db $C5,$FD,$10,$4A,$40;{$ENDIF} + {$IFDEF AVXSUP}vxorpd ymm0, ymm1, ymm0; {$ELSE}db $C5,$F5,$57,$C0;{$ENDIF} + {$IFDEF AVXSUP}vmovupd [ecx + 64], ymm0; {$ELSE}db $C5,$FD,$11,$41,$40;{$ENDIF} + + {$IFDEF AVXSUP}vmovapd ymm0, [eax + 96]; {$ELSE}db $C5,$FD,$28,$40,$60;{$ENDIF} + {$IFDEF AVXSUP}vmovupd ymm1, [edx + 96]; {$ELSE}db $C5,$FD,$10,$4A,$60;{$ENDIF} + {$IFDEF AVXSUP}vxorpd ymm0, ymm1, ymm0; {$ELSE}db $C5,$F5,$57,$C0;{$ENDIF} + {$IFDEF AVXSUP}vmovupd [ecx + 96], ymm0; {$ELSE}db $C5,$FD,$11,$41,$60;{$ENDIF} + + {$IFDEF AVXSUP}vzeroupper; {$ELSE}db $C5,$F8,$77;{$ENDIF} +end; + + +{$ENDIF} +{$IFDEF x64} +procedure FullBlockSSE(ChaChaMtx : PChaChaAVXMtx; Source, Dest: Pointer); +// rcx = ChaChaMtx, rdx = source, r8 = dest +asm + {$IFDEF UNIX} + // Linux uses a diffrent ABI -> copy over the registers so they meet with winABI + // The parameters are passed in the following order: + // RDI, RSI, RDX, RCX, r8, r9 -> mov to RCX, RDX, R8, R9, width and height + mov r8, rdx; + mov r9, rcx; + mov rcx, rdi; + mov rdx, rsi; + {$ENDIF} + + movapd xmm0, [rcx]; + movupd xmm1, [rdx]; + xorpd xmm0, xmm1; + movupd [r8], xmm0; + + movapd xmm0, [rcx + 16]; + movupd xmm1, [rdx + 16]; + xorpd xmm0, xmm1; + movupd [r8 + 16], xmm0; + + movapd xmm0, [rcx + 32]; + movupd xmm1, [rdx + 32]; + xorpd xmm0, xmm1; + movupd [r8 + 32], xmm0; + + movapd xmm0, [rcx + 48]; + movupd xmm1, [rdx + 48]; + xorpd xmm0, xmm1; + movupd [r8 + 48], xmm0; + + movapd xmm0, [rcx + 64]; + movupd xmm1, [rdx + 64]; + xorpd xmm0, xmm1; + movupd [r8 + 64], xmm0; + + movapd xmm0, [rcx + 80]; + movupd xmm1, [rdx + 80]; + xorpd xmm0, xmm1; + movupd [r8 + 80], xmm0; + + movapd xmm0, [rcx + 96]; + movupd xmm1, [rdx + 96]; + xorpd xmm0, xmm1; + movupd [r8 + 96], xmm0; + + movapd xmm0, [rcx + 112]; + movupd xmm1, [rdx + 112]; + xorpd xmm0, xmm1; + movupd [r8 + 112], xmm0; +end; + +procedure FullBlockAVX(ChaChaMtx : PChaChaAVXMtx; Source, Dest: Pointer); +// rcx = ChaChaMtx, rdx = source, r8 = dest +asm + {$IFDEF UNIX} + // Linux uses a diffrent ABI -> copy over the registers so they meet with winABI + // The parameters are passed in the following order: + // RDI, RSI, RDX, RCX, r8, r9 -> mov to RCX, RDX, R8, R9, width and height + mov r8, rdx; + mov r9, rcx; + mov rcx, rdi; + mov rdx, rsi; + {$ENDIF} + + {$IFDEF AVXSUP}vmovapd ymm0, [rcx]; {$ELSE}db $C5,$FD,$28,$01;{$ENDIF} + {$IFDEF AVXSUP}vmovupd ymm1, [rdx]; {$ELSE}db $C5,$FD,$10,$0A;{$ENDIF} + {$IFDEF AVXSUP}vxorpd ymm0, ymm1, ymm0; {$ELSE}db $C5,$F5,$57,$C0;{$ENDIF} + {$IFDEF AVXSUP}vmovupd [r8], ymm0; {$ELSE}db $C4,$C1,$7D,$11,$00;{$ENDIF} + + {$IFDEF AVXSUP}vmovapd ymm0, [rcx + 32]; {$ELSE}db $C5,$FD,$28,$41,$20;{$ENDIF} + {$IFDEF AVXSUP}vmovupd ymm1, [rdx + 32]; {$ELSE}db $C5,$FD,$10,$4A,$20;{$ENDIF} + {$IFDEF AVXSUP}vxorpd ymm0, ymm1, ymm0; {$ELSE}db $C5,$F5,$57,$C0;{$ENDIF} + {$IFDEF AVXSUP}vmovupd [r8 + 32], ymm0; {$ELSE}db $C4,$C1,$7D,$11,$40,$20;{$ENDIF} + + {$IFDEF AVXSUP}vmovapd ymm0, [rcx + 64]; {$ELSE}db $C5,$FD,$28,$41,$40;{$ENDIF} + {$IFDEF AVXSUP}vmovupd ymm1, [rdx + 64]; {$ELSE}db $C5,$FD,$10,$4A,$40;{$ENDIF} + {$IFDEF AVXSUP}vxorpd ymm0, ymm1, ymm0; {$ELSE}db $C5,$F5,$57,$C0;{$ENDIF} + {$IFDEF AVXSUP}vmovupd [r8 + 64], ymm0; {$ELSE}db $C4,$C1,$7D,$11,$40,$40;{$ENDIF} + + {$IFDEF AVXSUP}vmovapd ymm0, [rcx + 96]; {$ELSE}db $C5,$FD,$28,$41,$60;{$ENDIF} + {$IFDEF AVXSUP}vmovupd ymm1, [rdx + 96]; {$ELSE}db $C5,$FD,$10,$4A,$60;{$ENDIF} + {$IFDEF AVXSUP}vxorpd ymm0, ymm1, ymm0; {$ELSE}db $C5,$F5,$57,$C0;{$ENDIF} + {$IFDEF AVXSUP}vmovupd [r8 + 96], ymm0; {$ELSE}db $C4,$C1,$7D,$11,$40,$60;{$ENDIF} + + {$IFDEF AVXSUP}vzeroupper; {$ELSE}db $C5,$F8,$77;{$ENDIF} +end; +{$ENDIF} + +const cShuf16 : Array[0..31] of byte = (3, 0, 1, 2, + 7, 4, 5, 6, + 11, 8, 9, 10, + 15, 12, 13, 14, + 3, 0, 1, 2, + 7, 4, 5, 6, + 11, 8, 9, 10, + 15, 12, 13, 14); + +{$IFDEF x86} + +procedure AVXChaChaDoubleQuarterRound( chachaMtx : PChaChaAVXMtx ); {$IFDEF FPC} assembler; {$ELSE} register; {$ENDIF} +asm + lea ecx, cShuf16; + {$IFDEF AVXSUP}vmovdqu ymm5, [ecx]; {$ELSE}db $C5,$FE,$6F,$29;{$ENDIF} + + // move the matrix to xmm0 to xmm3 + {$IFDEF AVXSUP}vmovdqa ymm0, [eax]; {$ELSE}db $C5,$FD,$6F,$00;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa ymm1, [eax + 32]; {$ELSE}db $C5,$FD,$6F,$48,$20;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa ymm2, [eax + 64]; {$ELSE}db $C5,$FD,$6F,$50,$40;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa ymm3, [eax + 96]; {$ELSE}db $C5,$FD,$6F,$58,$60;{$ENDIF} + + // v0 += v1; v3 ^= v0; v3 <<<= 16 + {$IFDEF AVXSUP}vpaddd ymm0, ymm0, ymm1; {$ELSE}db $C5,$FD,$FE,$C1;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm3, ymm3, ymm0; {$ELSE}db $C5,$E5,$EF,$D8;{$ENDIF} + {$IFDEF AVXSUP}vpshufhw ymm3, ymm3, $B1; {$ELSE}db $C5,$FE,$70,$DB,$B1;{$ENDIF} // 10 11 00 01 + {$IFDEF AVXSUP}vpshuflw ymm3, ymm3, $B1; {$ELSE}db $C5,$FF,$70,$DB,$B1;{$ENDIF} + + // v2 += v3; v1 ^= v2; v1 <<<= (12, 12, 12, 12); + {$IFDEF AVXSUP}vpaddd ymm2, ymm2, ymm3; {$ELSE}db $C5,$ED,$FE,$D3;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm1, ymm1, ymm2; {$ELSE}db $C5,$F5,$EF,$CA;{$ENDIF} + // rotate is x << n | x >> 32 - n + {$IFDEF AVXSUP}vpslld ymm4, ymm1, 12; {$ELSE}db $C5,$DD,$72,$F1,$0C;{$ENDIF} + {$IFDEF AVXSUP}vpsrld ymm1, ymm1, 20; {$ELSE}db $C5,$F5,$72,$D1,$14;{$ENDIF} // 32 - 12 + {$IFDEF AVXSUP}vpor ymm1, ymm1, ymm4; {$ELSE}db $C5,$F5,$EB,$CC;{$ENDIF} + + // v0 += v1; v3 ^= v0; v3 <<<= ( 8, 8, 8, 8); + {$IFDEF AVXSUP}vpaddd ymm0, ymm0, ymm1; {$ELSE}db $C5,$FD,$FE,$C1;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm3, ymm3, ymm0; {$ELSE}db $C5,$E5,$EF,$D8;{$ENDIF} + {$IFDEF AVXSUP}vpshufb ymm3, ymm3, ymm5; {$ELSE}db $C4,$E2,$65,$00,$DD;{$ENDIF} + + // v2 += v3; v1 ^= v2; v1 <<<= ( 7, 7, 7, 7); + {$IFDEF AVXSUP}vpaddd ymm2, ymm2, ymm3; {$ELSE}db $C5,$ED,$FE,$D3;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm1, ymm1, ymm2; {$ELSE}db $C5,$F5,$EF,$CA;{$ENDIF} + {$IFDEF AVXSUP}vpslld ymm4, ymm1, 7; {$ELSE}db $C5,$DD,$72,$F1,$07;{$ENDIF} + {$IFDEF AVXSUP}vpsrld ymm1, ymm1, 25; {$ELSE}db $C5,$F5,$72,$D1,$19;{$ENDIF} // 32 - 7 + {$IFDEF AVXSUP}vpor ymm1, ymm1, ymm4; {$ELSE}db $C5,$F5,$EB,$CC;{$ENDIF} + + // v1 >>>= 32; v2 >>>= 64; v3 >>>= 96; + + // palignr is actually a sse3 opcode but ok... + {$IFDEF AVXSUP}vmovapd ymm4, ymm1; {$ELSE}db $C5,$FD,$29,$CC;{$ENDIF} + {$IFDEF AVXSUP}vpalignr ymm1, ymm1, ymm4, 4; {$ELSE}db $C4,$E3,$75,$0F,$CC,$04;{$ENDIF} + {$IFDEF AVXSUP}vmovapd ymm4, ymm2; {$ELSE}db $C5,$FD,$29,$D4;{$ENDIF} + {$IFDEF AVXSUP}vpalignr ymm2, ymm2, ymm4, 8; {$ELSE}db $C4,$E3,$6D,$0F,$D4,$08;{$ENDIF} + {$IFDEF AVXSUP}vmovapd ymm4, ymm3; {$ELSE}db $C5,$FD,$29,$DC;{$ENDIF} + {$IFDEF AVXSUP}vpalignr ymm3, ymm3, ymm4, 12; {$ELSE}db $C4,$E3,$65,$0F,$DC,$0C;{$ENDIF} + + // v0 += v1; v3 ^= v0; v3 <<<= 16 + {$IFDEF AVXSUP}vpaddd ymm0, ymm0, ymm1; {$ELSE}db $C5,$FD,$FE,$C1;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm3, ymm3, ymm0; {$ELSE}db $C5,$E5,$EF,$D8;{$ENDIF} + {$IFDEF AVXSUP}vpshufhw ymm3, ymm3, $B1; {$ELSE}db $C5,$FE,$70,$DB,$B1;{$ENDIF} // 10 11 00 01 + {$IFDEF AVXSUP}vpshuflw ymm3, ymm3, $B1; {$ELSE}db $C5,$FF,$70,$DB,$B1;{$ENDIF} + + // v2 += v3; v1 ^= v2; v1 <<<= (12, 12, 12, 12); + {$IFDEF AVXSUP}vpaddd ymm2, ymm2, ymm3; {$ELSE}db $C5,$ED,$FE,$D3;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm1, ymm1, ymm2; {$ELSE}db $C5,$F5,$EF,$CA;{$ENDIF} + // rotate is x << n | x >> 32 - n + {$IFDEF AVXSUP}vpslld ymm4, ymm1, 12; {$ELSE}db $C5,$DD,$72,$F1,$0C;{$ENDIF} + {$IFDEF AVXSUP}vpsrld ymm1, ymm1, 20; {$ELSE}db $C5,$F5,$72,$D1,$14;{$ENDIF} // 32 - 12 + {$IFDEF AVXSUP}vpor ymm1, ymm1, ymm4; {$ELSE}db $C5,$F5,$EB,$CC;{$ENDIF} + + // v0 += v1; v3 ^= v0; v3 <<<= ( 8, 8, 8, 8); + {$IFDEF AVXSUP}vpaddd ymm0, ymm0, ymm1; {$ELSE}db $C5,$FD,$FE,$C1;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm3, ymm3, ymm0; {$ELSE}db $C5,$E5,$EF,$D8;{$ENDIF} + {$IFDEF AVXSUP}vpshufb ymm3, ymm3, ymm5; {$ELSE}db $C4,$E2,$65,$00,$DD;{$ENDIF} + + // v2 += v3; v1 ^= v2; v1 <<<= ( 7, 7, 7, 7); + {$IFDEF AVXSUP}vpaddd ymm2, ymm2, ymm3; {$ELSE}db $C5,$ED,$FE,$D3;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm1, ymm1, ymm2; {$ELSE}db $C5,$F5,$EF,$CA;{$ENDIF} + {$IFDEF AVXSUP}vpslld ymm4, ymm1, 7; {$ELSE}db $C5,$DD,$72,$F1,$07;{$ENDIF} + {$IFDEF AVXSUP}vpsrld ymm1, ymm1, 25; {$ELSE}db $C5,$F5,$72,$D1,$19;{$ENDIF} // 32 - 7 + {$IFDEF AVXSUP}vpor ymm1, ymm1, ymm4; {$ELSE}db $C5,$F5,$EB,$CC;{$ENDIF} + + // v1 <<<= 32; v2 <<<= 64; v3 <<<= 96; Return + {$IFDEF AVXSUP}vmovapd ymm4, ymm1; {$ELSE}db $C5,$FD,$29,$CC;{$ENDIF} + {$IFDEF AVXSUP}vpalignr ymm1, ymm1, ymm4, 12; {$ELSE}db $C4,$E3,$75,$0F,$CC,$0C;{$ENDIF} + {$IFDEF AVXSUP}vmovapd ymm4, ymm2; {$ELSE}db $C5,$FD,$29,$D4;{$ENDIF} + {$IFDEF AVXSUP}vpalignr ymm2, ymm2, ymm4, 8; {$ELSE}db $C4,$E3,$6D,$0F,$D4,$08;{$ENDIF} + {$IFDEF AVXSUP}vmovapd ymm4, ymm3; {$ELSE}db $C5,$FD,$29,$DC;{$ENDIF} + {$IFDEF AVXSUP}vpalignr ymm3, ymm3, ymm4, 4; {$ELSE}db $C4,$E3,$65,$0F,$DC,$04;{$ENDIF} + + // move back + {$IFDEF AVXSUP}vmovdqa [eax], ymm0; {$ELSE}db $C5,$FD,$7F,$00;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [eax + 32], ymm1; {$ELSE}db $C5,$FD,$7F,$48,$20;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [eax + 64], ymm2; {$ELSE}db $C5,$FD,$7F,$50,$40;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [eax + 96], ymm3; {$ELSE}db $C5,$FD,$7F,$58,$60;{$ENDIF} + {$IFDEF AVXSUP}vzeroupper; {$ELSE}db $C5,$F8,$77;{$ENDIF} +end; + +// realign the chacha matrix such that further access to it is linear +procedure AVXRealingAndAddMtx( chaChaMtx : PChaChaAVXMtx; inpChaCha : PChaChaMtx ); {$IFDEF FPC} assembler; {$ELSE} register; {$ENDIF} +asm + // store second matrix + {$IFDEF AVXSUP}vmovdqa xmm0, [eax + 16]; {$ELSE}db $C5,$F9,$6F,$40,$10;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa xmm1, [eax + 48]; {$ELSE}db $C5,$F9,$6F,$48,$30;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa xmm2, [eax + 80]; {$ELSE}db $C5,$F9,$6F,$50,$50;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa xmm3, [eax + 112]; {$ELSE}db $C5,$F9,$6F,$58,$70;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm0, xmm0, [edx]; {$ELSE}db $C5,$F9,$FE,$02;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm1, xmm1, [edx + 16]; {$ELSE}db $C5,$F1,$FE,$4A,$10;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm2, xmm2, [edx + 32]; {$ELSE}db $C5,$E9,$FE,$52,$20;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm3, xmm3, [edx + 48]; {$ELSE}db $C5,$E1,$FE,$5A,$30;{$ENDIF} + + // move positions + {$IFDEF AVXSUP}vmovapd xmm5, [eax]; {$ELSE}db $C5,$F9,$28,$28;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm5, xmm5, [edx]; {$ELSE}db $C5,$D1,$FE,$2A;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [eax], xmm5; {$ELSE}db $C5,$F9,$7F,$28;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa xmm5, [eax + 32]; {$ELSE}db $C5,$F9,$6F,$68,$20;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm5, xmm5, [edx + 16]; {$ELSE}db $C5,$D1,$FE,$6A,$10;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [eax + 16], xmm5; {$ELSE}db $C5,$F9,$7F,$68,$10;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa xmm5, [eax + 64]; {$ELSE}db $C5,$F9,$6F,$68,$40;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm5, xmm5, [edx + 32]; {$ELSE}db $C5,$D1,$FE,$6A,$20;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [eax + 32], xmm5; {$ELSE}db $C5,$F9,$7F,$68,$20;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa xmm5, [eax + 96]; {$ELSE}db $C5,$F9,$6F,$68,$60;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm5, xmm5, [edx + 48]; {$ELSE}db $C5,$D1,$FE,$6A,$30;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [eax + 48], xmm5; {$ELSE}db $C5,$F9,$7F,$68,$30;{$ENDIF} + + // append second matrix + {$IFDEF AVXSUP}vmovdqa [eax + 64], xmm0; {$ELSE}db $C5,$F9,$7F,$40,$40;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [eax + 80], xmm1; {$ELSE}db $C5,$F9,$7F,$48,$50;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [eax + 96], xmm2; {$ELSE}db $C5,$F9,$7F,$50,$60;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [eax + 112], xmm3; {$ELSE}db $C5,$F9,$7F,$58,$70;{$ENDIF} + + {$IFDEF AVXSUP}vzeroupper; {$ELSE}db $C5,$F8,$77;{$ENDIF} +end; +{$ENDIF} + +{$IFDEF x64} + +procedure AVXChaChaDoubleQuarterRound( chachaMtx : PChaChaAVXMtx ); +var dYMM4, dYMM5 : Array[0..4] of int64; +{$IFDEF FPC} +begin +{$ENDIF} +asm + {$IFDEF UNIX} + // Linux uses a diffrent ABI -> copy over the registers so they meet with winABI + // The parameters are passed in the following order: + // RDI, RSI, RDX, RCX, r8, r9 -> mov to RCX, RDX, R8, R9, width and height + // in our case only rdi to rcx + mov rcx, rdi; + {$ENDIF} + {$IFDEF x64} + {$IFDEF AVXSUP}vmovupd dYMM4, ymm4; {$ELSE}db $C5,$FD,$11,$65,$D8;{$ENDIF} + {$IFDEF AVXSUP}vmovupd dYMM5, ymm5; {$ELSE}db $C5,$FD,$11,$6D,$B8;{$ENDIF} + + // 64bit version + lea rdx, [rip + cShuf16]; + {$IFDEF AVXSUP}vmovdqu ymm5, [rdx]; {$ELSE}db $C5,$FE,$6F,$2A;{$ENDIF} + + // move the matrix to xmm0 to xmm3 + {$IFDEF AVXSUP}vmovdqa ymm0, [rcx]; {$ELSE}db $C5,$FD,$6F,$01;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa ymm1, [rcx + 32]; {$ELSE}db $C5,$FD,$6F,$49,$20;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa ymm2, [rcx + 64]; {$ELSE}db $C5,$FD,$6F,$51,$40;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa ymm3, [rcx + 96]; {$ELSE}db $C5,$FD,$6F,$59,$60;{$ENDIF} + + // v0 += v1; v3 ^= v0; v3 <<<= 16 + {$IFDEF AVXSUP}vpaddd ymm0, ymm0, ymm1; {$ELSE}db $C5,$FD,$FE,$C1;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm3, ymm3, ymm0; {$ELSE}db $C5,$E5,$EF,$D8;{$ENDIF} + {$IFDEF AVXSUP}vpshufhw ymm3, ymm3, $B1; {$ELSE}db $C5,$FE,$70,$DB,$B1;{$ENDIF} // 10 11 00 01 + {$IFDEF AVXSUP}vpshuflw ymm3, ymm3, $B1; {$ELSE}db $C5,$FF,$70,$DB,$B1;{$ENDIF} + + // v2 += v3; v1 ^= v2; v1 <<<= (12, 12, 12, 12); + {$IFDEF AVXSUP}vpaddd ymm2, ymm2, ymm3; {$ELSE}db $C5,$ED,$FE,$D3;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm1, ymm1, ymm2; {$ELSE}db $C5,$F5,$EF,$CA;{$ENDIF} + // rotate is x << n | x >> 32 - n + {$IFDEF AVXSUP}vpslld ymm4, ymm1, 12; {$ELSE}db $C5,$DD,$72,$F1,$0C;{$ENDIF} + {$IFDEF AVXSUP}vpsrld ymm1, ymm1, 20; {$ELSE}db $C5,$F5,$72,$D1,$14;{$ENDIF} // 32 - 12 + {$IFDEF AVXSUP}vpor ymm1, ymm1, ymm4; {$ELSE}db $C5,$F5,$EB,$CC;{$ENDIF} + + // v0 += v1; v3 ^= v0; v3 <<<= ( 8, 8, 8, 8); + {$IFDEF AVXSUP}vpaddd ymm0, ymm0, ymm1; {$ELSE}db $C5,$FD,$FE,$C1;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm3, ymm3, ymm0; {$ELSE}db $C5,$E5,$EF,$D8;{$ENDIF} + {$IFDEF AVXSUP}vpshufb ymm3, ymm3, ymm5; {$ELSE}db $C4,$E2,$65,$00,$DD;{$ENDIF} + + // v2 += v3; v1 ^= v2; v1 <<<= ( 7, 7, 7, 7); + {$IFDEF AVXSUP}vpaddd ymm2, ymm2, ymm3; {$ELSE}db $C5,$ED,$FE,$D3;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm1, ymm1, ymm2; {$ELSE}db $C5,$F5,$EF,$CA;{$ENDIF} + {$IFDEF AVXSUP}vpslld ymm4, ymm1, 7; {$ELSE}db $C5,$DD,$72,$F1,$07;{$ENDIF} + {$IFDEF AVXSUP}vpsrld ymm1, ymm1, 25; {$ELSE}db $C5,$F5,$72,$D1,$19;{$ENDIF} // 32 - 7 + {$IFDEF AVXSUP}vpor ymm1, ymm1, ymm4; {$ELSE}db $C5,$F5,$EB,$CC;{$ENDIF} + + // v1 >>>= 32; v2 >>>= 64; v3 >>>= 96; + + // palignr is actually a sse3 opcode but ok... + {$IFDEF AVXSUP}vmovapd ymm4, ymm1; {$ELSE}db $C5,$FD,$29,$CC;{$ENDIF} + {$IFDEF AVXSUP}vpalignr ymm1, ymm1, ymm4, 4; {$ELSE}db $C4,$E3,$75,$0F,$CC,$04;{$ENDIF} + {$IFDEF AVXSUP}vmovapd ymm4, ymm2; {$ELSE}db $C5,$FD,$29,$D4;{$ENDIF} + {$IFDEF AVXSUP}vpalignr ymm2, ymm2, ymm4, 8; {$ELSE}db $C4,$E3,$6D,$0F,$D4,$08;{$ENDIF} + {$IFDEF AVXSUP}vmovapd ymm4, ymm3; {$ELSE}db $C5,$FD,$29,$DC;{$ENDIF} + {$IFDEF AVXSUP}vpalignr ymm3, ymm3, ymm4, 12; {$ELSE}db $C4,$E3,$65,$0F,$DC,$0C;{$ENDIF} + + // v0 += v1; v3 ^= v0; v3 <<<= 16 + {$IFDEF AVXSUP}vpaddd ymm0, ymm0, ymm1; {$ELSE}db $C5,$FD,$FE,$C1;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm3, ymm3, ymm0; {$ELSE}db $C5,$E5,$EF,$D8;{$ENDIF} + {$IFDEF AVXSUP}vpshufhw ymm3, ymm3, $B1; {$ELSE}db $C5,$FE,$70,$DB,$B1;{$ENDIF} // 10 11 00 01 + {$IFDEF AVXSUP}vpshuflw ymm3, ymm3, $B1; {$ELSE}db $C5,$FF,$70,$DB,$B1;{$ENDIF} + + // v2 += v3; v1 ^= v2; v1 <<<= (12, 12, 12, 12); + {$IFDEF AVXSUP}vpaddd ymm2, ymm2, ymm3; {$ELSE}db $C5,$ED,$FE,$D3;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm1, ymm1, ymm2; {$ELSE}db $C5,$F5,$EF,$CA;{$ENDIF} + // rotate is x << n | x >> 32 - n + {$IFDEF AVXSUP}vpslld ymm4, ymm1, 12; {$ELSE}db $C5,$DD,$72,$F1,$0C;{$ENDIF} + {$IFDEF AVXSUP}vpsrld ymm1, ymm1, 20; {$ELSE}db $C5,$F5,$72,$D1,$14;{$ENDIF} // 32 - 12 + {$IFDEF AVXSUP}vpor ymm1, ymm1, ymm4; {$ELSE}db $C5,$F5,$EB,$CC;{$ENDIF} + + // v0 += v1; v3 ^= v0; v3 <<<= ( 8, 8, 8, 8); + {$IFDEF AVXSUP}vpaddd ymm0, ymm0, ymm1; {$ELSE}db $C5,$FD,$FE,$C1;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm3, ymm3, ymm0; {$ELSE}db $C5,$E5,$EF,$D8;{$ENDIF} + {$IFDEF AVXSUP}vpshufb ymm3, ymm3, ymm5; {$ELSE}db $C4,$E2,$65,$00,$DD;{$ENDIF} + + // v2 += v3; v1 ^= v2; v1 <<<= ( 7, 7, 7, 7); + {$IFDEF AVXSUP}vpaddd ymm2, ymm2, ymm3; {$ELSE}db $C5,$ED,$FE,$D3;{$ENDIF} + {$IFDEF AVXSUP}vpxor ymm1, ymm1, ymm2; {$ELSE}db $C5,$F5,$EF,$CA;{$ENDIF} + {$IFDEF AVXSUP}vpslld ymm4, ymm1, 7; {$ELSE}db $C5,$DD,$72,$F1,$07;{$ENDIF} + {$IFDEF AVXSUP}vpsrld ymm1, ymm1, 25; {$ELSE}db $C5,$F5,$72,$D1,$19;{$ENDIF} // 32 - 7 + {$IFDEF AVXSUP}vpor ymm1, ymm1, ymm4; {$ELSE}db $C5,$F5,$EB,$CC;{$ENDIF} + + // v1 <<<= 32; v2 <<<= 64; v3 <<<= 96; Return + {$IFDEF AVXSUP}vmovapd ymm4, ymm1; {$ELSE}db $C5,$FD,$29,$CC;{$ENDIF} + {$IFDEF AVXSUP}vpalignr ymm1, ymm1, ymm4, 12; {$ELSE}db $C4,$E3,$75,$0F,$CC,$0C;{$ENDIF} + {$IFDEF AVXSUP}vmovapd ymm4, ymm2; {$ELSE}db $C5,$FD,$29,$D4;{$ENDIF} + {$IFDEF AVXSUP}vpalignr ymm2, ymm2, ymm4, 8; {$ELSE}db $C4,$E3,$6D,$0F,$D4,$08;{$ENDIF} + {$IFDEF AVXSUP}vmovapd ymm4, ymm3; {$ELSE}db $C5,$FD,$29,$DC;{$ENDIF} + {$IFDEF AVXSUP}vpalignr ymm3, ymm3, ymm4, 4; {$ELSE}db $C4,$E3,$65,$0F,$DC,$04;{$ENDIF} + + // move back + {$IFDEF AVXSUP}vmovdqa [rcx], ymm0; {$ELSE}db $C5,$FD,$7F,$01;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [rcx + 32], ymm1; {$ELSE}db $C5,$FD,$7F,$49,$20;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [rcx + 64], ymm2; {$ELSE}db $C5,$FD,$7F,$51,$40;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [rcx + 96], ymm3; {$ELSE}db $C5,$FD,$7F,$59,$60;{$ENDIF} + + // cleanup registers + {$IFDEF AVXSUP}vmovupd ymm4, dYMM4; {$ELSE}db $C5,$FD,$10,$65,$D8;{$ENDIF} + {$IFDEF AVXSUP}vmovupd ymm5, dYMM5; {$ELSE}db $C5,$FD,$10,$6D,$B8;{$ENDIF} + {$IFDEF AVXSUP}vzeroupper; {$ELSE}db $C5,$F8,$77;{$ENDIF} +{$ENDIF} +end; +{$IFDEF FPC} +end; +{$ENDIF} + +// realign the chacha matrix such that further access to it is linear +procedure AVXRealingAndAddMtx( chaChaMtx : PChaChaAVXMtx; inpChaCha : PChaChaMtx ); +var dXMM4, dXMM5 : Array[0..2] of Double; +{$IFDEF FPC} +begin +{$ENDIF} +asm + {$IFDEF UNIX} + // Linux uses a diffrent ABI -> copy over the registers so they meet with winABI + // The parameters are passed in the following order: + // RDI, RSI -> mov to RCX, RDX + mov rcx, rdi; + mov rdx, rsi; + {$ENDIF} + {$IFDEF AVXSUP}vmovupd dXMM4, xmm4; {$ELSE}db $C5,$F9,$11,$65,$E0;{$ENDIF} + {$IFDEF AVXSUP}vmovupd dXMM5, xmm5; {$ELSE}db $C5,$F9,$11,$6D,$D0;{$ENDIF} + + // store second matrix + {$IFDEF AVXSUP}vmovdqa xmm0, [rcx + 16]; {$ELSE}db $C5,$F9,$6F,$41,$10;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa xmm1, [rcx + 48]; {$ELSE}db $C5,$F9,$6F,$49,$30;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa xmm2, [rcx + 80]; {$ELSE}db $C5,$F9,$6F,$51,$50;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa xmm3, [rcx + 112]; {$ELSE}db $C5,$F9,$6F,$59,$70;{$ENDIF} + + {$IFDEF AVXSUP}vpaddd xmm0, xmm0, [rdx]; {$ELSE}db $C5,$F9,$FE,$02;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm1, xmm1, [rdx + 16]; {$ELSE}db $C5,$F1,$FE,$4A,$10;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm2, xmm2, [rdx + 32]; {$ELSE}db $C5,$E9,$FE,$52,$20;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm3, xmm3, [rdx + 48]; {$ELSE}db $C5,$E1,$FE,$5A,$30;{$ENDIF} + + + // move positions + {$IFDEF AVXSUP}vmovapd xmm5, [rcx]; {$ELSE}db $C5,$F9,$28,$29;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm5, xmm5, [rdx]; {$ELSE}db $C5,$D1,$FE,$2A;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [rcx], xmm5; {$ELSE}db $C5,$F9,$7F,$29;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa xmm5, [rcx + 32]; {$ELSE}db $C5,$F9,$6F,$69,$20;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm5, xmm5, [rdx + 16]; {$ELSE}db $C5,$D1,$FE,$6A,$10;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [rcx + 16], xmm5; {$ELSE}db $C5,$F9,$7F,$69,$10;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa xmm5, [rcx + 64]; {$ELSE}db $C5,$F9,$6F,$69,$40;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm5, xmm5, [rdx + 32]; {$ELSE}db $C5,$D1,$FE,$6A,$20;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [rcx + 32], xmm5; {$ELSE}db $C5,$F9,$7F,$69,$20;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa xmm5, [rcx + 96]; {$ELSE}db $C5,$F9,$6F,$69,$60;{$ENDIF} + {$IFDEF AVXSUP}vpaddd xmm5, xmm5, [rdx + 48]; {$ELSE}db $C5,$D1,$FE,$6A,$30;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [rcx + 48], xmm5; {$ELSE}db $C5,$F9,$7F,$69,$30;{$ENDIF} + + // append second matrix + {$IFDEF AVXSUP}vmovdqa [rcx + 64], xmm0; {$ELSE}db $C5,$F9,$7F,$41,$40;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [rcx + 80], xmm1; {$ELSE}db $C5,$F9,$7F,$49,$50;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [rcx + 96], xmm2; {$ELSE}db $C5,$F9,$7F,$51,$60;{$ENDIF} + {$IFDEF AVXSUP}vmovdqa [rcx + 112], xmm3; {$ELSE}db $C5,$F9,$7F,$59,$70;{$ENDIF} + + // cleanup registers + {$IFDEF AVXSUP}vmovupd xmm4, dXMM4; {$ELSE}db $C5,$F9,$10,$65,$E0;{$ENDIF} + {$IFDEF AVXSUP}vmovupd xmm5, dXMM5; {$ELSE}db $C5,$F9,$10,$6D,$D0;{$ENDIF} + + {$IFDEF AVXSUP}vzeroupper; {$ELSE}db $C5,$F8,$77;{$ENDIF} +end; +{$IFDEF FPC} +end; +{$ENDIF} + +{$ENDIF} + +{$ENDIF} + +procedure TCipher_ChaCha20.AfterConstruction; +begin + inherited; + + // default is poly1305!! + Mode := cmPoly1305; + SetChaChaMode( cmSecure ); + {$IFDEF PUREPASCAL} + fFullBlockFunc := FullBlockPas; + {$ELSE} + case CpuMode of + cmSSE: fFullBlockFunc := FullBlockSSE; + cmAVX: fFullBlockFunc := FullBlockAVX; + else + fFullBlockFunc := FullBlockPas; + end; + {$ENDIF} +end; + + +// ########################################### +// #### ChaChaBlock init +// ########################################### +procedure TCipher_ChaCha20.InitChaChaBlk; +var i : integer; + m1, m2 : PChaChaMtx; +begin + if fChaChaIdx >= fChaChaBlkLen then + begin + {$IFNDEF PUREPASCAL} + if CpuMode = cmAVX then + begin + // move the input matrix so we have nicely aligned memory for the quarter round + for i := 0 to 3 do + begin + Move(fInpChaChaMtx^[4*i], fOutChaChaMtx^[8*i], 4*sizeof(LongWord)); + Move(fInpChaChaMtx^[4*i], fOutChaChaMtx^[8*i + 4], 4*sizeof(LongWord)); + end; + + // update index in the "second" matrix - this one is always odd so no further check + inc(fOutChaChaMtx^[3*8 + 4]); + + for i := 0 to fNumChaChaRounds - 1 do + AVXChaChaDoubleQuarterRound(fOutChaChaMtx); + // undo the alignment so both matrices are adjacent again (not intermittent as used for the rounds) + AVXRealingAndAddMtx(fOutChaChaMtx, fInpChaChaMTX); + + // increment the one value that is off by one from the input matrix + inc(fOutChaChaMtx^[16 + 12]); + + // increment input matrix by 2 + inc(fInpChaChaMTX^[12], 2); + end + else + {$ENDIF} + begin + // ########################################### + // #### Create two chacha matrices in one go + m1 := PChaChaMtx(fOutChaChaMtx); + m2 := m1; + inc(m2); + Move(fInpChaChaMTX^, m1^, sizeof(TChaChaMtx)); + Move(fInpChaChaMTX^, m2^, sizeof(TChaChaMtx)); + inc(m2^[12]); + + {$IFNDEF PUREPASCAL} + if cpuMode = cmSSE then + begin + for i := 0 to fNumChaChaRounds - 1 do + begin + SSEChaChaDoubleQuarterRound(m1); + SSEChaChaDoubleQuarterRound(m2); + end; + end + else + {$ENDIF} + begin + for i := 0 to fNumChaChaRounds - 1 do + begin + PasChaChaDoubleQuarterRound(m1); + PasChaChaDoubleQuarterRound(m2); + end; + + end; + for i := 0 to High(fInpChaChaMTX^) do + inc(m1^[i], fInpChaChaMTX^[i]); + inc(fInpChaChaMTX^[12]); + for i := 0 to High(fInpChaChaMTX^) do + inc(m2^[i], fInpChaChaMTX^[i]); + inc(fInpChaChaMTX^[12]); + end; + + fChaChaIdx := 0; + + // is this here for xchacha? + if fInpChaChaMTX^[12] <= 1 then + raise Exception.Create('Counter overflow!'); +// inc(fInpChaChaMTX^[13]); + end; +end; + +procedure TCipher_ChaCha20.PasChaChaDoubleQuarterRound(mtx : PChaChaMtx); +begin + ChaChaQuarterRound( mtx^[0], mtx^[4], mtx^[8], mtx^[12]); + ChaChaQuarterRound( mtx^[1], mtx^[5], mtx^[9], mtx^[13]); + ChaChaQuarterRound( mtx^[2], mtx^[6], mtx^[10], mtx^[14]); + ChaChaQuarterRound( mtx^[3], mtx^[7], mtx^[11], mtx^[15]); + ChaChaQuarterRound( mtx^[0], mtx^[5], mtx^[10], mtx^[15]); + ChaChaQuarterRound( mtx^[1], mtx^[6], mtx^[11], mtx^[12]); + ChaChaQuarterRound( mtx^[2], mtx^[7], mtx^[8], mtx^[13]); + ChaChaQuarterRound( mtx^[3], mtx^[4], mtx^[9], mtx^[14]); +end; + +{$IFNDEF PUREPASCAL} + +(* +// https://eprint.iacr.org/2013/759.pdf +Algorithm 5: DOUBLEQUARTERROUND (optimized for 128-bit vectors) +Input: v0, v1, v2, v3 (state matrix as four 4x32-bit vectors, each vector includes one row) +Output: v0, v1, v2, v3 (updated state matrix) +Flow + v0 += v1; v3 ^= v0; v3 <<<= (16, 16, 16, 16); + v2 += v3; v1 ^= v2; v1 <<<= (12, 12, 12, 12); + v0 += v1; v3 ^= v0; v3 <<<= ( 8, 8, 8, 8); + v2 += v3; v1 ^= v2; v1 <<<= ( 7, 7, 7, 7); + v1 >>>= 32; v2 >>>= 64; v3 >>>= 96; + v0 += v1; v3 ^= v0; v3 <<<= (16, 16, 16, 16); + v2 += v3; v1 ^= v2; v1 <<<= (12, 12, 12, 12); + v0 += v1; v3 ^= v0; v3 <<<= ( 8, 8, 8, 8); + v2 += v3; v1 ^= v2; v1 <<<= ( 7, 7, 7, 7); + v1 <<<= 32; v2 <<<= 64; v3 <<<= 96; Return +*) +const cShuf8 : Array[0..15] of byte = (3, 0, 1, 2, + 7, 4, 5, 6, + 11, 8, 9, 10, + 15, 12, 13, 14 ); + +procedure TCipher_ChaCha20.SSEChaChaDoubleQuarterRound(mtx : PChaChaMtx); +// 32Bit: ecx = self, edx = mtx +// 64bit: rcx = self, rdx = mtx +{$IFDEF x64} +var dXMM4, dXMM5 : Array[0..1] of Int64; +{$ENDIF} +asm + {$IFDEF x64} + // rcx seems to have "self" as reference + {$IFDEF UNIX} + // Linux uses a diffrent ABI -> copy over the registers so they meet with winABI + // The parameters are passed in the following order: + // RDI, RSI, RDX, RCX, r8, r9 -> mov to RCX, RDX, R8, R9, width and height + // in our case only rdi to rcx + mov rdx, rsi; + {$ENDIF} + movupd dXMM4, xmm4; + movupd dXMM5, xmm5; + + // 64bit version + movdqu xmm5, [rip + cShuf8]; + + // move the matrix to xmm0 to xmm3 + movdqa xmm0, [rdx]; + movdqa xmm1, [rdx + 16]; + movdqa xmm2, [rdx + 32]; + movdqa xmm3, [rdx + 48]; + {$ELSE} + movdqu xmm5, cShuf8; + + // move the matrix to xmm0 to xmm3 + mov edx, mtx; + movdqa xmm0, [edx]; + movdqa xmm1, [edx + 16]; + movdqa xmm2, [edx + 32]; + movdqa xmm3, [edx + 48]; + {$ENDIF} + + // v0 += v1; v3 ^= v0; v3 <<<= 16 + paddd xmm0, xmm1; + pxor xmm3, xmm0; + pshufhw xmm3, xmm3, $B1; // 10 11 00 01 + pshuflw xmm3, xmm3, $B1; + + // v2 += v3; v1 ^= v2; v1 <<<= (12, 12, 12, 12); + paddd xmm2, xmm3; + pxor xmm1, xmm2; + // rotate is x << n | x >> 32 - n + movapd xmm4, xmm1; + pslld xmm4, 12; + psrld xmm1, 20; // 32 - 12 + por xmm1, xmm4; + + // v0 += v1; v3 ^= v0; v3 <<<= ( 8, 8, 8, 8); + paddd xmm0, xmm1; + pxor xmm3, xmm0; + pshufb xmm3, xmm5; + + // v2 += v3; v1 ^= v2; v1 <<<= ( 7, 7, 7, 7); + paddd xmm2, xmm3; + pxor xmm1, xmm2; + movapd xmm4, xmm1; + pslld xmm4, 7; + psrld xmm1, 25; // 32 - 7 + por xmm1, xmm4; + + // v1 >>>= 32; v2 >>>= 64; v3 >>>= 96; + + // palignr is actually a sse3 opcode but ok... + movapd xmm4, xmm1; + palignr xmm1, xmm4, 4; + movapd xmm4, xmm2; + palignr xmm2, xmm4, 8; + movapd xmm4, xmm3; + palignr xmm3, xmm4, 12; + + + // v0 += v1; v3 ^= v0; v3 <<<= 16 + paddd xmm0, xmm1; + pxor xmm3, xmm0; + pshufhw xmm3, xmm3, $B1; // 10 11 00 01 + pshuflw xmm3, xmm3, $B1; + + // v2 += v3; v1 ^= v2; v1 <<<= (12, 12, 12, 12); + paddd xmm2, xmm3; + pxor xmm1, xmm2; + // rotate is x << n | x >> 32 - n + movapd xmm4, xmm1; + pslld xmm4, 12; + psrld xmm1, 20; // 32 - 12 + por xmm1, xmm4; + + // v0 += v1; v3 ^= v0; v3 <<<= ( 8, 8, 8, 8); + paddd xmm0, xmm1; + pxor xmm3, xmm0; + pshufb xmm3, xmm5; + + // v2 += v3; v1 ^= v2; v1 <<<= ( 7, 7, 7, 7); + paddd xmm2, xmm3; + pxor xmm1, xmm2; + movapd xmm4, xmm1; + pslld xmm4, 7; + psrld xmm1, 25; // 32 - 7 + por xmm1, xmm4; + + // v1 <<<= 32; v2 <<<= 64; v3 <<<= 96; Return + movapd xmm4, xmm1; + palignr xmm1, xmm4, 12; + movapd xmm4, xmm2; + palignr xmm2, xmm4, 8; + movapd xmm4, xmm3; + palignr xmm3, xmm4, 4; + + // move back + {$IFDEF x64} + movdqa [rdx], xmm0; + movdqa [rdx + 16], xmm1; + movdqa [rdx + 32], xmm2; + movdqa [rdx + 48], xmm3; + + // cleanup registers + movupd xmm4, dXMM4; + movupd xmm5, dXMM5; + {$ELSE} + movdqa [edx], xmm0; + movdqa [edx + 16], xmm1; + movdqa [edx + 32], xmm2; + movdqa [edx + 48], xmm3; + {$ENDIF} +end; + +{$ENDIF} + {$IFDEF RESTORE_RANGECHECKS}{$R+}{$ENDIF} {$IFDEF RESTORE_OVERFLOWCHECKS}{$Q+}{$ENDIF} +function TCipher_ChaCha20.TestChaChaMtx( + const expectedMtx: TChaChaMtx): boolean; +begin + Result := CompareMem(fOutChaChaMtx, @expectedMtx, sizeof(TChaChaMtx)); +end; + initialization SetDefaultCipherClass(TCipher_Null); + TCipher_ChaCha20.CpuMode := cmPas; + {$IFNDEF ManualRegisterCipherClasses} TCipher_Null.RegisterClass(TDECCipher.ClassList); TCipher_Blowfish.RegisterClass(TDECCipher.ClassList); diff --git a/Source/DECOptions.inc b/Source/DECOptions.inc index eceec52..da05d4b 100644 --- a/Source/DECOptions.inc +++ b/Source/DECOptions.inc @@ -119,7 +119,7 @@ /// {$IF DECLARED(FireMonkeyVersion)} {$DEFINE FMXTranslateableExceptions} -{$ENDIF} +{$IFEND} { TODO: convert to $ENDIF when raising minimum supported version to XE4+} //------------------------------------------------------------------------------ // Do NOT change anything below! //------------------------------------------------------------------------------ diff --git a/Unit Tests/DECDUnitTestSuite.dpr b/Unit Tests/DECDUnitTestSuite.dpr index 224f30c..0c9b25d 100644 --- a/Unit Tests/DECDUnitTestSuite.dpr +++ b/Unit Tests/DECDUnitTestSuite.dpr @@ -16,7 +16,6 @@ program DECDUnitTestSuite; {$ENDIF} uses -// FastMM4, Vcl.Forms, {$IFDEF TESTINSIGHT} TestInsight.Client, @@ -42,8 +41,36 @@ uses TestDECCipherModesGCM in 'Tests\TestDECCipherModesGCM.pas', TestDECZIPHelper in 'Tests\TestDECZIPHelper.pas', TestDECCipherPaddings in 'Tests\TestDECCipherPaddings.pas', + DECBaseClass in '..\Source\DECBaseClass.pas', + DECCipherBase in '..\Source\DECCipherBase.pas', + DECCipherFormats in '..\Source\DECCipherFormats.pas', + DECCipherInterface in '..\Source\DECCipherInterface.pas', TestDECCipherModesCCM in 'Tests\TestDECCipherModesCCM.pas', - AuthenticatedCiphersCommonTestData in 'Tests\AuthenticatedCiphersCommonTestData.pas'; + DECCipherModes in '..\Source\DECCipherModes.pas', + DECCipherPaddings in '..\Source\DECCipherPaddings.pas', + DECCiphers in '..\Source\DECCiphers.pas', + DECCRC in '..\Source\DECCRC.pas', + DECData in '..\Source\DECData.pas', + DECDataCipher in '..\Source\DECDataCipher.pas', + DECDataHash in '..\Source\DECDataHash.pas', + DECFormat in '..\Source\DECFormat.pas', + DECFormatBase in '..\Source\DECFormatBase.pas', + DECHash in '..\Source\DECHash.pas', + AuthenticatedCiphersCommonTestData in 'Tests\AuthenticatedCiphersCommonTestData.pas', + DECAuthenticatedCipherModesBase in '..\Source\DECAuthenticatedCipherModesBase.pas', + DECCipherModesCCM in '..\Source\DECCipherModesCCM.pas', + DECCipherModesPoly1305 in '..\Source\DECCipherModesPoly1305.pas', + DECHashAuthentication in '..\Source\DECHashAuthentication.pas', + DECHashBase in '..\Source\DECHashBase.pas', + DECHashBitBase in '..\Source\DECHashBitBase.pas', + DECHashInterface in '..\Source\DECHashInterface.pas', + DECRandom in '..\Source\DECRandom.pas', + DECTypes in '..\Source\DECTypes.pas', + DECUtil in '..\Source\DECUtil.pas', + DECUtilRawByteStringHelper in '..\Source\DECUtilRawByteStringHelper.pas', + DECZIPHelper in '..\Source\DECZIPHelper.pas', + DECCipherModesGCM in '..\Source\DECCipherModesGCM.pas', + TESTDECChaChaPoly1305 in 'Tests\TESTDECChaChaPoly1305.pas'; {$R *.RES} diff --git a/Unit Tests/DECDUnitTestSuite.dproj b/Unit Tests/DECDUnitTestSuite.dproj index 1670f30..3611840 100644 --- a/Unit Tests/DECDUnitTestSuite.dproj +++ b/Unit Tests/DECDUnitTestSuite.dproj @@ -163,8 +163,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Base diff --git a/Unit Tests/Tests/TestDECChaChaPoly1305.pas b/Unit Tests/Tests/TestDECChaChaPoly1305.pas new file mode 100644 index 0000000..946f7a2 --- /dev/null +++ b/Unit Tests/Tests/TestDECChaChaPoly1305.pas @@ -0,0 +1,353 @@ +unit TestDECChaChaPoly1305; + +interface + +uses {$IFDEF DUnitX} + DUnitX.TestFramework,DUnitX.DUnitCompatibility, + {$ELSE} + TestFramework, + {$ENDIF} + System.SysUtils, Generics.Collections, System.Math, + DECBaseClass, + DECCipherBase, DECCipherModes, DECCipherFormats, DECCiphers; + + +type +// Testmethods for class TDECCipher + {$IFDEF DUnitX} [TestFixture] {$ENDIF} + TestChaCha20Poly1305 = class(TTestCase) + published + procedure TestPoly1305; + procedure TestChaCha20_Poly1305_KeySetup; + procedure TestChaCha20_Poly1305_AEAD; + procedure TestChaChaEncodeDecodeSpeed; + + end; +implementation + +uses DECCipherModesPoly1305, System.Diagnostics; + + +//// ########################################### +//// #### OpenSSL reference implementation +//// ########################################### +// +// +//const POLY1305_KEY_SIZE = 32; +// POLY1305_DIGEST_SIZE = 16; +// +//type +// PPoly1305Ctx = PByte; +// TPoly1305Key = Array[0..POLY1305_KEY_SIZE-1] of byte; +// TPoly1305Mac = Array[0..POLY1305_DIGEST_SIZE-1] of byte; +// size_t = Integer; +// +// +//function Poly1305_ctx_size : size_t; cdecl; external 'libcrypto-3.dll'; +//procedure Poly1305_Init(ctx : PPoly1305Ctx; const key : TPoly1305Key); cdecl; external 'libcrypto-3.dll'; +//procedure Poly1305_Update(ctx : PPoly1305Ctx; inp : PByte; len : size_t); cdecl; external 'libcrypto-3.dll'; +//procedure Poly1305_Final(ctx : PPoly1305Ctx; var mac : TPoly1305Mac); cdecl; external 'libcrypto-3.dll'; + + + +// ########################################### +// #### +// ########################################### + +{ TestTDECGCM } +type + THackChaChaCipher = class(TCipher_chacha20); + + +procedure TestChaCha20Poly1305.TestChaCha20_Poly1305_KeySetup; +// test vector of chapter 2.6.2 in RFC 7538 +const cKey : TBytes = [$80, $81, $82, $83, $84, $85, $86, $87, $88, $89, $8a, $8b, $8c, $8d, $8e, $8f, + $90, $91, $92, $93, $94, $95, $96, $97, $98, $99, $9a, $9b, $9c, $9d, $9e, $9f]; + cNonce : TBytes = [$00, $00, $00, $00, $00, $01, $02, $03, $04, $05, $06, $07]; + + cChaChaMtx : TChaChaMtx = ($8ba0d58a, $cc815f90, $27405081, $7194b24a, + $37b633a8, $a50dfde3, $e2b8db08, $46a6d1fd, + $7da03782, $9183a233, $148ad271, $b46773d1, + $3cc1875a, $8607def1, $ca5c3086, $7085eb87); + + +var chaCha : TCipher_ChaCha20; + cpuMode : TChaChaCpuMode; +begin + for cpuMode in [cmPas, cmSSE, cmAVX] do + begin + TCipher_ChaCha20.CpuMode := cpuMode; + chaCha := TCipher_ChaCha20.Create; + try + chaCha.Mode := cmPoly1305; + chaCha.Init( cKey, cNonce ); + Check( THackChaChaCipher(chaCha).TestChaChaMtx(cChaChaMtx), 'Initialization failed'); + finally + chaCha.Free; + end; + end; +end; + +procedure TestChaCha20Poly1305.TestChaChaEncodeDecodeSpeed; +var startStop : TStopWatch; + encBuf : TBytes; + decBuf_pas, decBuf_SSe, decBuf_AVX, testDecode : TBytes; + i : Integer; + chaCha : TCipher_ChaCha20; + cpuMode : TChaChaCpuMode; + +const cKey : TBytes = [$80, $81, $82, $83, $84, $85, $86, $87, $88, $89, $8a, $8b, $8c, $8d, $8e, $8f, + $90, $91, $92, $93, $94, $95, $96, $97, $98, $99, $9a, $9b, $9c, $9d, $9e, $9f]; + + cNonce : TBytes = [$07, $00, $00, $00, $40, $43, $41, $43, $44, $45, $46, $47]; + +procedure EncodeBuf( var dest : TBytes ); +var i : integer; +begin + for i := 0 to 3 do + begin + chaCha := TCipher_ChaCha20.Create; + try + chaCha.Mode := cmECBx; + chaCha.Init(cKey, cNonce); + + dest := chaCha.EncodeBytes(encBuf); + finally + chaCha.Free; + end; + end; + +end; + +begin + SetLength(encBuf, 10000000); + + for i := 0 to Length(encBuf) - 1 do + encBuf[i] := Byte(Random(255)); + + + SetLength(decBuf_pas, Length(encBuf)); + SetLength(decBuf_sse, Length(encBuf)); + SetLength(decBuf_avx, Length(encBuf)); + SetLength(testDecode, Length(encBuf)); + + // ########################################### + // #### Perform encryption of all 3 types + // + TCipher_ChaCha20.CpuMode := cmPas; + startStop.Reset; + startStop.Start; + EncodeBuf( decbuf_pas ); + startStop.Stop; + Status( Format( 'Pas Encoding took %dms', [startStop.ElapsedMilliseconds])); + + TCipher_ChaCha20.CpuMode := cmSSE; + startStop.Reset; + startStop.Start; + EncodeBuf( decbuf_sse ); + startStop.Stop; + Status( Format( 'SSE Encoding took %dms', [startStop.ElapsedMilliseconds])); + + TCipher_ChaCha20.CpuMode := cmAVX; + startStop.Reset; + startStop.Start; + EncodeBuf( decbuf_avx ); + startStop.Stop; + Status( Format( 'AVX Encoding took %dms', [startStop.ElapsedMilliseconds])); + + + Check( CompareMem( @decbuf_pas[0], @decBuf_sse[0], Length(decBuf_sse)), 'SSE encoding failed'); + Check( CompareMem( @decbuf_pas[0], @decBuf_sse[0], Length(decBuf_sse)), 'avx encoding failed'); + + + // test decode + for cpuMode in [cmPas, cmSSE, cmAVX] do + begin + chaCha := TCipher_ChaCha20.Create; + try + chaCha.Mode := cmECBx; + chaCha.Init(cKey, cNonce); + + testDecode := chaCha.DecodeBytes(decbuf_sse); + finally + chaCha.Free; + end; + + Check( CompareMem( @encBuf[0], @testDecode[0], Length(testDecode)), 'Dencoding failed'); + end; +end; + +procedure TestChaCha20Poly1305.TestPoly1305; +const// cKey : Array of Byte = [$85, $d6, $be, $78, $57, $55, $6d, $33, $7f, $44, $52, $fe, $42, $d5, $06, $a8, $01, $0, +// $3, $80, $8a, $fb, $0d, $b2, $fd, $4a, $bf, $f6, $af, $41, $49, $f5, $1b ]; + + // original test vector from rfc7539 + cMsg : AnsiString = 'Cryptographic Forum Research Group'; // without trailing #0! + cS : Array of BYte = [$01, $03, $80, $8a, $fb, $0d, $b2, $fd, $4a, $bf, $f6, $af, $41, $49, $f5, $1b]; + cR : Array of Byte = [$85, $d6, $be, $78, $57, $55, $6d, $33, $7f, $44, $52, $fe, $42, $d5, $06, $a8]; + cTag : Array of Byte = [$a8, $06, $1d, $c1, $30, $51, $36, $c6, $c2, $2b, $8b, $af, $0c, $01, $27, $a9]; + +// cKey1 : Array of Byte = [$ec, $07, $4c, $83, $55, $80, $74, $17, $01, $42, $5b, $62, $32, $35, $ad, $d6]; + cR1 : Array of BYte = [$85, $1f, $c4, $0c, $34, $67, $ac, $0b, $e0, $5c, $c2, $04, $04, $f3, $f7, $00]; + cS1 : Array of BYte = [$ec, $07, $4c, $83, $55, $80, $74, $17, $01, $42, $5b, $62, $32, $35, $ad, $d6]; + cMsg1 : Array of Byte = [$f3, $f6]; + cTag1 : Array of BYte = [$88, $C3, $44, $37, $C6, $87, $7A, $3E, $90, $C1, $F8, $08, $58, $C3, $92, $F8]; + +var poly : TPoly1305; + iv : T32ByteArray; + msg : TBytes; + calcTag : TBytes; + // ctx : PPoly1305Ctx; +procedure InvData( data : PByte; len : integer ); +var tmp : byte; + pEnd : PByte; +begin + pEnd := data; + inc(pEnd, len - 1); + while pEnd > data do + begin + tmp := pEnd^; + pEnd^ := data^; + data^ := tmp; + + inc(data); + dec(pEnd); + end; +end; +begin + // test vector from https://datatracker.ietf.org/doc/html/rfc7539.html#page-15 + // RFC 7538 + FillChar(iv[0], Length(iv), 0); + Move( cR1[0], iv[0], Length(cr)); + Move( cS1[0], iv[16], Length(cS1)); + + //InvData( @iv[0], 16); + //invData( @iv[16], 16); + SetLength(msg, length(cMsg1)); + Move(cMsg1[0], msg[0], Length(msg)); + //InvData( @msg[0], Length(msg)); + + poly := TPoly1305.Create; + try + poly.InitInternal(iv); + poly.UpdatePoly(@msg[0], Length(msg)); + poly.Finalize; + + calcTag := poly.CalculatedAuthenticationTag; + finally + poly.Free; + end; + + Check(Length(cTag) = Length(calcTag), 'MAC length is wrong'); + Check( CompareMem(@cTag1[0], @calcTag[0], Length(calcTag)), 'Polynom calculated tag does not match'); + + // ########################################### + // #### second test + FillChar(iv[0], Length(iv), 0); + Move( cR[0], iv[0], Length(cr)); + Move( cS[0], iv[16], Length(cS1)); + + SetLength(msg, length(cMsg)); + Move(cMsg[1], msg[0], Length(msg)); + + poly := TPoly1305.Create; + try + poly.InitInternal(iv); + poly.UpdatePoly(@msg[0], Length(msg)); + poly.Finalize; + + calcTag := poly.CalculatedAuthenticationTag; + finally + poly.Free; + end; + + Check(Length(cTag) = Length(calcTag), 'MAC length is wrong'); + Check( CompareMem(@cTag[0], @calcTag[0], Length(calcTag)), 'Polynom calculated tag does not match'); +end; + +procedure TestChaCha20Poly1305.TestChaCha20_Poly1305_AEAD; +const cMsg : AnsiString = 'Ladies and Gentlemen of the class of ''99: If I could offer you only one tip for the future, sunscreen would be it.'; + cAAD : TBytes = [$50, $51, $52, $53, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7]; + + cKey : TBytes = [$80, $81, $82, $83, $84, $85, $86, $87, $88, $89, $8a, $8b, $8c, $8d, $8e, $8f, + $90, $91, $92, $93, $94, $95, $96, $97, $98, $99, $9a, $9b, $9c, $9d, $9e, $9f]; + + cNonce : TBytes = [$07, $00, $00, $00, $40, $41, $42, $43, $44, $45, $46, $47]; + + cTag : TBytes = [$1a, $e1, $0b, $59, $4f, $09, $e2, $6a, $7e, $90, $2e, $cb, $d0, $60, $06, $91]; + + cCipherText : TBytes = [$d3, $1a, $8d, $34, $64, $8e, $60, $db, $7b, $86, $af, $bc, $53, $ef, $7e, $c2, + $a4, $ad, $ed, $51, $29, $6e, $08, $fe, $a9, $e2, $b5, $a7, $36, $ee, $62, $d6, + $3d, $be, $a4, $5e, $8c, $a9, $67, $12, $82, $fa, $fb, $69, $da, $92, $72, $8b, + $1a, $71, $de, $0a, $9e, $06, $0b, $29, $05, $d6, $a5, $b6, $7e, $cd, $3b, $36, + $92, $dd, $bd, $7f, $2d, $77, $8b, $8c, $98, $03, $ae, $e3, $28, $09, $1b, $58, + $fa, $b3, $24, $e4, $fa, $d6, $75, $94, $55, $85, $80, $8b, $48, $31, $d7, $bc, + $3f, $f4, $de, $f0, $8e, $4b, $7a, $9d, $e5, $76, $d2, $65, $86, $ce, $c6, $4b, + $61, $16 ]; + +var chaCha : TCipher_ChaCha20; + msg : TBytes; + encr : TBytes; + encrTag : TBytes; + decr : TBytes; + decodeTag : TBytes; + cpuMode : TChaChaCpuMode; +begin + for cpuMode in [cmPas, cmSSE, cmAVX] do + begin + TCipher_ChaCha20.CpuMode := cpuMode; + + SetLength(msg, Length(cMsg)); + Move( cMsg[1], msg[0], Length(msg)); + + + chaCha := TCipher_ChaCha20.Create; + try + chaCha.DataToAuthenticate := cAAD; + chaCha.Init(cKey, cNonce); + encr := chaCha.EncodeBytes(msg); + chaCha.Done; + + encrTag := chaCha.CalculatedAuthenticationResult; + finally + chaCha.Free; + end; + + Check( Length(cCipherText) = Length(encr), 'Encryption length is wrong'); + Check( CompareMem( @cCipherText[0], @encr[0], Length(encr) ), 'Encryption failed'); + + + Check( Length(cTag) = Length(encrTag), 'Tag length is wrong'); + Check( CompareMem( @encrTag[0], @cTag[0], Length(cTag)), 'Calculated Tag is wrong'); + + // ########################################### + // #### Test decode + chaCha := TCipher_ChaCha20.Create; + try + chaCha.ExpectedAuthenticationResult := encrTag; + chaCha.DataToAuthenticate := cAAD; + + chaCha.Init(cKey, cNonce); + decr := chaCha.DecodeBytes(encr); + chaCha.Done; + + decodeTag := chaCha.CalculatedAuthenticationResult + finally + chaCha.Free; + end; + + Check( Length(cTag) = Length(decodeTag), 'Tag length is wrong'); + Check( CompareMem( @decodeTag[0], @cTag[0], Length(cTag)), 'Calculated Tag is wrong'); + end; +end; + + +initialization + // Register all test cases to be run + {$IFDEF DUnitX} + TDUnitX.RegisterTestFixture(TPoly1305); + {$ELSE} + RegisterTest(TestChaCha20Poly1305.Suite); + {$ENDIF} + +end. From 90987167dd72fc58a5fd2f0aa86d45a770555b2f Mon Sep 17 00:00:00 2001 From: Michael Rabatscher Date: Wed, 9 Jul 2025 23:17:15 +0200 Subject: [PATCH 09/17] * Poly1305 hash updated to new authentication Class * Fixed bugs to get stream functions working - not yet tested * shrinking base classes to get the extended aead functions working (ccm, gcm, poly1305) - not yet tested --- Source/DECAuthenticatedCipherModesBase.pas | 94 +- Source/DECCipherBase.pas | 2 +- Source/DECCipherFormats.pas | 15 +- Source/DECCipherModes.pas | 280 +--- Source/DECCipherModesCCM.pas | 40 + Source/DECCipherModesGCM.pas | 475 +++--- Source/DECCipherModesPoly1305.pas | 1686 ++------------------ Source/DECCiphers.pas | 3 + Unit Tests/DECDUnitTestSuite.dpr | 3 +- Unit Tests/DECDUnitTestSuite.dproj | 9 +- Unit Tests/Tests/TestDECChaChaPoly1305.pas | 21 +- 11 files changed, 666 insertions(+), 1962 deletions(-) diff --git a/Source/DECAuthenticatedCipherModesBase.pas b/Source/DECAuthenticatedCipherModesBase.pas index 6245f1a..b24c14d 100644 --- a/Source/DECAuthenticatedCipherModesBase.pas +++ b/Source/DECAuthenticatedCipherModesBase.pas @@ -59,6 +59,16 @@ EDECAuthLengthException = class(EDECException); /// Base class for authenticated cipher modes /// TAuthenticatedCipherModesBase = class(TObject) + private + /// + /// Flag that indicates if the method has been initialized + /// + fAuthMethodInit : boolean; + + /// + /// Length of the processed buffer + /// + fEncDecLen : Int64; strict protected /// /// The data which shall be authenticated in parallel to the encryption @@ -99,6 +109,32 @@ TAuthenticatedCipherModesBase = class(TObject) /// Length of the calculated authentication value in bit /// function GetAuthenticationTagBitLength: UInt32; virtual; + + + /// + /// Initialize the authentication procedure. This is a good place to initialize + /// the data structures and initialize with the (unencrypted) authenticated buffer + /// + procedure InitAuth; virtual; abstract; + + /// + /// Updates the mac. Can be called multiple times! + /// + procedure UpdateWithEncDecBuf(buf : PUInt8Array; Size : Integer); virtual; abstract; + + /// + /// finalize the MAC - typically the processed buffer lengths are incorporated here. + /// + procedure FinalizeMAC( authLen, encDecBufSize : Int64 ); virtual; abstract; + + /// + /// Encoding/Decoding routine that is called in the Encode/Decode routine. + /// This version simply calls the EncryptionMethod givin in the init call + /// + procedure LocEncodeDecode(Source, Dest: Pointer; Size: Integer); virtual; + + procedure Burn; virtual; abstract; + public /// /// Should be called when starting encryption/decryption in order to @@ -127,7 +163,7 @@ TAuthenticatedCipherModesBase = class(TObject) /// procedure Encode(Source, Dest : PUInt8Array; - Size : Integer); virtual; abstract; + Size : Integer); virtual; /// /// Decodes a block of data using the supplied cipher /// @@ -142,7 +178,13 @@ TAuthenticatedCipherModesBase = class(TObject) /// procedure Decode(Source, Dest : PUInt8Array; - Size : Integer); virtual; abstract; + Size : Integer); virtual; + + /// + /// Call this function to calculate the final MAC - typically one updates the + /// remaining buffer with buffer lengths. + /// + procedure FinalizeAEAD; /// /// Returns a list of authentication tag lengths explicitely specified by @@ -192,6 +234,48 @@ implementation { TAuthenticatedCipherModesBase } +procedure TAuthenticatedCipherModesBase.Decode(Source, Dest: PUInt8Array; + Size: Integer); +begin + if not fAuthMethodInit then + begin + InitAuth; + fAuthMethodInit := True; + end; + + UpdateWithEncDecBuf(Source, size); + LocEncodeDecode(Source, Dest, Size); + inc(fEncDecLen, Size); +end; + +procedure TAuthenticatedCipherModesBase.Encode(Source, Dest: PUInt8Array; + Size: Integer); +begin + if not fAuthMethodInit then + begin + InitAuth; + fAuthMethodInit := True; + end; + + LocEncodeDecode(Source, Dest, Size); + UpdateWithEncDecBuf(Dest, size); + inc(fEncDecLen, Size); +end; + +procedure TAuthenticatedCipherModesBase.FinalizeAEAD; +begin + if not fAuthMethodInit then + begin + InitAuth; + fAuthMethodInit := True; + end; + + SetLength(FCalcAuthenticationTag, FCalcAuthenticationTagLength); + FinalizeMAC(Length(FDataToAuthenticate), fEncDecLen); + + Burn; +end; + function TAuthenticatedCipherModesBase.GetAuthenticationTagBitLength: UInt32; begin Result := FCalcAuthenticationTagLength shl 3; @@ -221,6 +305,12 @@ procedure TAuthenticatedCipherModesBase.Init(EncryptionMethod : TEncodeDecodeMet FEncryptionMethod := EncryptionMethod; end; +procedure TAuthenticatedCipherModesBase.LocEncodeDecode(Source, Dest: Pointer; + Size: Integer); +begin + FEncryptionMethod(Source, Dest, Size); +end; + procedure TAuthenticatedCipherModesBase.SetAuthenticationTagLength(const Value: UInt32); begin FCalcAuthenticationTagLength := Value shr 3; diff --git a/Source/DECCipherBase.pas b/Source/DECCipherBase.pas index c2efe35..99e0a4f 100644 --- a/Source/DECCipherBase.pas +++ b/Source/DECCipherBase.pas @@ -1013,7 +1013,7 @@ procedure TDECCipher.Init(const Key; Size: Integer; const IVector; if (Size > Context.KeySize) and (not (ctNull in Context.CipherType)) then raise EDECCipherException.CreateRes(@sKeyMaterialTooLarge); - if (FInitVectorSize > FBufferSize) and (not (FMode = cmGCM)) then + if (FInitVectorSize > FBufferSize) and (not (FMode in [cmGCM, cmPoly1305])) then raise EDECCipherException.CreateRes(@sIVMaterialTooLarge); DoInit(Key, Size); diff --git a/Source/DECCipherFormats.pas b/Source/DECCipherFormats.pas index 20e5c14..eb3d5ac 100644 --- a/Source/DECCipherFormats.pas +++ b/Source/DECCipherFormats.pas @@ -733,7 +733,7 @@ function TDECFormattedCipher.EncodeBytes(const Source: TBytes): TBytes; Encode(Source[0], Result[0], Length(Source)) else if (FMode = cmGCM) then - EncodeGCM(nil, nil, 0); + EncodeWithAuthObj(nil, nil, 0); end; begin @@ -745,18 +745,15 @@ function TDECFormattedCipher.EncodeBytes(const Source: TBytes): TBytes; function TDECFormattedCipher.DecodeBytes(const Source: TBytes): TBytes; begin - Result := Source; + SetLength(Result, Length(Source)); if Length(Result) > 0 then begin - if (FMode = cmGCM) then - SetLength(Result, Length(Source)); - Decode(Source[0], Result[0], Length(Source)); - end - else - if (FMode = cmGCM) then - DecodeGCM(nil, nil, 0); + end; + //else + //if (FMode = cmGCM) then + // DecodeGCM(nil, nil, 0); if not (FPaddingClass = nil) then Result := FPaddingClass.RemovePadding(Result, Context.BlockSize); diff --git a/Source/DECCipherModes.pas b/Source/DECCipherModes.pas index 7f3e4c0..4840ce0 100644 --- a/Source/DECCipherModes.pas +++ b/Source/DECCipherModes.pas @@ -26,9 +26,8 @@ interface {$ELSE} System.SysUtils, {$ENDIF} - DECTypes, DECCipherBase, DECCipherModesGCM, DECCipherModesCCM, - DECCipherModesPoly1305, - DECCipherInterface; + DECTypes, DECCipherBase, + DECCipherInterface, DECAuthenticatedCipherModesBase; type /// @@ -140,29 +139,11 @@ TDECCipherModes = class(TDECCipher, IDECAuthenticatedCipher) procedure SetExpectedAuthenticationResult(const Value: TBytes); strict protected /// - /// Implementation of the Galois counter mode. Only created when gmGCM is - /// set as mode. + /// Implementation of the Galois counter mode, poly1305 or ccm. Only created if + /// such a mode is selected /// - FGCM : TGCM; - /// - /// Implementation of the Counter with CBC-MAC mode. Only created when - /// gmCCM is set as mode. - /// - FCCM : TCCM; - /// - /// Raises an EDECCipherException exception and provides the correct value - /// for block size in that message - /// - /// - /// Exception raised unconditionally. - /// - + fAuthObj : TAuthenticatedCipherModesBase; - /// - /// Implementation of the Pol1305 polynom. Only created when gmPoly1305 is - /// set as mode. - /// - FPOLY1305 : TPoly1305; procedure ReportInvalidMessageLength(Cipher: TDECCipher); /// @@ -253,21 +234,8 @@ TDECCipherModes = class(TDECCipher, IDECAuthenticatedCipher) /// Implemented in its own unit, but needed here to be callable even if /// source length is 0. /// - procedure EncodeGCM(Source, Dest: PUInt8Array; Size: Integer); virtual; + procedure EncodeWithAuthObj(Source, Dest: PUInt8Array; Size: Integer); virtual; - /// - /// Poly1305: encryption with addtional optional authentication. - /// Implemented in its own unit and only usable with ChaCha20 (and maybe AES128) - - /// Currently only one class overrides this method. - /// - procedure EncodePoly1305(Source, Dest: PUInt8Array; Size: Integer); virtual; - - /// - /// Counter with CBC-MAC Mode: encryption with addtional optional authentication. - /// Implemented in its own unit, but needed here to be callable even if - /// source length is 0. - /// - procedure EncodeCCM(Source, Dest: PUInt8Array; Size: Integer); virtual; {$IFDEF DEC3_CMCTS} /// /// double CBC, with @@ -351,21 +319,11 @@ TDECCipherModes = class(TDECCipher, IDECAuthenticatedCipher) /// inputstream into feedback register. /// procedure DecodeCTSx(Source, Dest: PUInt8Array; Size: Integer); virtual; - /// - /// Galois Counter Mode, details are implemented in DECCipherModesGCM - /// - procedure DecodeGCM(Source, Dest: PUInt8Array; Size: Integer); virtual; - /// - /// Poly1305, details are implemented in DECCipherModesPoly1305. - /// This routine needs to be overridden by successor classes. - /// - procedure DecodePoly1305(Source, Dest: PUInt8Array; Size: Integer); virtual; - /// - /// Counter with CBC-MAC Mode, details are implemented in DECCipherModesCCM + /// Decoding for gcm, poly1305 and ccm, details are implemented in special units. /// - procedure DecodeCCM(Source, Dest: PUInt8Array; Size: Integer); virtual; + procedure DecodeWithAuthObj(Source, Dest: PUInt8Array; Size: Integer); virtual; {$IFDEF DEC3_CMCTS} /// /// double CBC @@ -491,7 +449,10 @@ implementation {$ELSE} System.TypInfo, {$ENDIF} - DECUtil; + DECUtil, + DECCipherModesGCM, + DECCipherModesCCM, + DECCipherModesPoly1305; resourcestring sInvalidMessageLength = 'Message length for mode %0:s must be a multiple of %1:d bytes'; @@ -515,36 +476,30 @@ procedure TDECCipherModes.ReportInvalidMessageLength(Cipher: TDECCipher); procedure TDECCipherModes.SetDataToAuthenticate(const Value: TBytes); begin - case FMode of - cmGCM: FGCM.DataToAuthenticate := Value; - cmCCM: FCCM.DataToAuthenticate := Value; - cmPoly1305: FPOLY1305.DataToAuthenticate := Value; - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); - end; + if Assigned(fAuthObj) + then + fAuthObj.DataToAuthenticate := Value + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); end; procedure TDECCipherModes.SetExpectedAuthenticationResult(const Value: TBytes); begin - case FMode of - cmGCM: FGCM.ExpectedAuthenticationTag := Value; - cmCCM: FCCM.ExpectedAuthenticationTag := Value; - cmPoly1305: FPOLY1305.ExpectedAuthenticationTag := Value; - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); - end; + if Assigned(fAuthObj) + then + fAuthObj.ExpectedAuthenticationTag := Value + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); end; procedure TDECCipherModes.SetAuthenticationResultBitLength( const Value: Integer); begin - case FMode of - cmGCM: FGCM.AuthenticationTagBitLength := Value; - cmCCM: FCCM.AuthenticationTagBitLength := Value; - cmPoly1305: FPOLY1305.AuthenticationTagBitLength := Value; - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); - end; + if Assigned(fAuthObj) + then + fAuthObj.AuthenticationTagBitLength := Value + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); end; procedure TDECCipherModes.Encode(const Source; var Dest; DataSize: Integer); @@ -564,9 +519,9 @@ procedure TDECCipherModes.Encode(const Source; var Dest; DataSize: Integer); cmOFBx: EncodeOFBx(@Source, @Dest, DataSize); cmCFS8: EncodeCFS8(@Source, @Dest, DataSize); cmCFSx: EncodeCFSx(@Source, @Dest, DataSize); - cmGCM : EncodeGCM(@Source, @Dest, DataSize); - cmPoly1305: EncodePoly1305(@Source, @Dest, DataSize); - cmCCM : EncodeCCM(@Source, @Dest, DataSize); + cmGCM, + cmPoly1305, + cmCCM: EncodeWithAuthObj(@Source, @Dest, DataSize); end; end; @@ -738,60 +693,50 @@ procedure TDECCipherModes.EncodeOFBx(Source, Dest: PUInt8Array; Size: Integer); function TDECCipherModes.GetDataToAuthenticate: TBytes; begin - case FMode of - cmGCM: Result := FGCM.DataToAuthenticate; - cmCCM: Result := FCCM.DataToAuthenticate; - cmPoly1305: Result := FPOLY1305.DataToAuthenticate; - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cCCM']); - end; + if Assigned(fAuthObj) + then + Result := fAuthObj.DataToAuthenticate + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM, cmPoly1305 or cCCM']); end; function TDECCipherModes.GetExpectedAuthenticationResult: TBytes; begin - case FMode of - cmGCM: Result := FGCM.ExpectedAuthenticationTag; - cmCCM: Result := FCCM.ExpectedAuthenticationTag; - cmPoly1305: REsult := FPOLY1305.ExpectedAuthenticationTag; - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); - end; + if Assigned(fAuthObj) + then + Result := fAuthObj.ExpectedAuthenticationTag + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM, cmPoly1305 or cmCCM']); end; function TDECCipherModes.GetStandardAuthenticationTagBitLengths: TStandardBitLengths; begin - case FMode of - cmGCM: Result := FGCM.GetStandardAuthenticationTagBitLengths; - cmCCM: Result := FCCM.GetStandardAuthenticationTagBitLengths; - cmPoly1305: Result := FPOLY1305.GetStandardAuthenticationTagBitLengths; - else - begin - SetLength(Result, 1); - Result[0] := 0; - end; - end; + if Assigned(fAuthObj) + then + Result := fAuthObj.GetStandardAuthenticationTagBitLengths + else + begin + SetLength(Result, 1); + Result[0] := 0; + end; end; function TDECCipherModes.GetAuthenticationResultBitLength: Integer; begin - case FMode of - cmGCM: Result := FGCM.AuthenticationTagBitLength; - cmCCM: Result := FCCM.AuthenticationTagBitLength; - cmPoly1305: Result := FPOLY1305.AuthenticationTagBitLength; - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); - end; + if Assigned(fAuthObj) + then + Result := fAuthObj.AuthenticationTagBitLength + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM, cmPoly1305 or cmCCM']); end; function TDECCipherModes.GetCalcAuthenticatonResult: TBytes; begin - case FMode of - cmGCM: Result := FGCM.CalculatedAuthenticationTag; - cmCCM: Result := FCCM.CalculatedAuthenticationTag; - cmPoly1305: Result := FPOLY1305.CalculatedAuthenticationTag; - else - raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM or cmCCM']); - end; + if Assigned(fAuthObj) + then + Result := fAuthObj.CalculatedAuthenticationTag + else + raise EDECCipherException.CreateResFmt(@sInvalidModeForMethod, ['cmGCM, cmPoly1305 or cmCCM']); end; procedure TDECCipherModes.InitMode; @@ -800,15 +745,15 @@ procedure TDECCipherModes.InitMode; begin if (Context.BlockSize = 16) then begin - case FMode of - cmGCM: FGCM := TGCM.Create; - cmCCM: FCCM := TCCM.Create; - cmPoly1305: FPOLY1305 := TPoly1305.Create; + case FMode of + cmGCM: fAuthObj := TGCM.Create; + cmCCM: fAuthObj := TCCM.Create; + cmPoly1305: fAuthObj := TPoly1305.Create; end; end else if Context.BlockSize < 16 then - FPOLY1305 := TPoly1305.Create + fAuthObj := TPoly1305.Create else // GCM and CCM require a cipher with 128 bit block size raise EDECCipherException.CreateResFmt(@sInvalidBlockSize, @@ -817,14 +762,8 @@ procedure TDECCipherModes.InitMode; end else begin - if Assigned(FGCM) then - FreeAndNil(FGCM); - - if Assigned(FCCM) then - FreeAndNil(FCCM); - - if Assigned(FPOLY1305) then - FreeAndNil(FPOLY1305); + if Assigned(fAuthObj) then + FreeAndNil(fAuthObj); end; end; @@ -918,28 +857,12 @@ procedure TDECCipherModes.EncodeCTSx(Source, Dest: PUInt8Array; Size: Integer); FState := csEncode; end; -procedure TDECCipherModes.EncodeGCM(Source, Dest: PUInt8Array; Size: Integer); -begin - if (Size < 0) then - Size := 0; - - FGCM.Encode(Source, Dest, Size); -end; - -procedure TDECCipherModes.EncodePoly1305(Source, Dest: PUInt8Array; - Size: Integer); -begin - if size < 0 then - Size := 0; - FPOLY1305.Encode(Source, Dest, Size); -end; - -procedure TDECCipherModes.EncodeCCM(Source, Dest: PUInt8Array; Size: Integer); +procedure TDECCipherModes.EncodeWithAuthObj(Source, Dest: PUInt8Array; Size: Integer); begin if (Size < 0) then Size := 0; - FCCM.Encode(Source, Dest, Size); + fAuthObj.Encode(Source, Dest, Size); end; {$IFDEF DEC3_CMCTS} @@ -984,9 +907,9 @@ procedure TDECCipherModes.Decode(const Source; var Dest; DataSize: Integer); cmOFBx: DecodeOFBx(@Source, @Dest, DataSize); cmCFS8: DecodeCFS8(@Source, @Dest, DataSize); cmCFSx: DecodeCFSx(@Source, @Dest, DataSize); - cmGCM : DecodeGCM(@Source, @Dest, DataSize); - cmPoly1305: DecodePoly1305(@Source, @Dest, DataSize); - cmCCM : DecodeCCM(@Source, @Dest, DataSize); + cmGCM, + cmPoly1305, + cmCCM : DecodeWithAuthObj(@Source, @Dest, DataSize); end; end; @@ -1025,20 +948,12 @@ procedure TDECCipherModes.DecodeECBx(Source, Dest: PUInt8Array; Size: Integer); end; end; -procedure TDECCipherModes.DecodeGCM(Source, Dest: PUInt8Array; Size: Integer); +procedure TDECCipherModes.DecodeWithAuthObj(Source, Dest: PUInt8Array; Size: Integer); begin if (Size < 0) then Size := 0; - FGCM.Decode(Source, Dest, Size); -end; - -procedure TDECCipherModes.DecodeCCM(Source, Dest: PUInt8Array; Size: Integer); -begin - if (Size < 0) then - Size := 0; - - FCCM.Decode(Source, Dest, Size); + fAuthObj.Decode(Source, Dest, Size); end; procedure TDECCipherModes.DecodeCFB8(Source, Dest: PUInt8Array; Size: Integer); @@ -1184,49 +1099,25 @@ procedure TDECCipherModes.DecodeOFBx(Source, Dest: PUInt8Array; Size: Integer); end; end; -procedure TDECCipherModes.DecodePoly1305(Source, Dest: PUInt8Array; - Size: Integer); -begin - if (Size < 0) then - Size := 0; - - FPOLY1305.Decode(Source, Dest, Size); -end; - destructor TDECCipherModes.Destroy; begin - FGCM.Free; - FCCM.Free; - FPOLY1305.Free; + fAuthObj.Free; - inherited; + inherited; end; procedure TDECCipherModes.Done; begin inherited; - case FMode of - cmGCM : begin - if (length(FGCM.ExpectedAuthenticationTag) > 0) and - (not IsEqual(FGCM.ExpectedAuthenticationTag, FGCM.CalculatedAuthenticationTag)) then - raise EDECCipherAuthenticationException.CreateRes(@sInvalidAuthenticationValue); - end; - - cmCCM : begin - if (length(FCCM.ExpectedAuthenticationTag) > 0) and - (not IsEqual(FCCM.ExpectedAuthenticationTag, FCCM.CalculatedAuthenticationTag)) then - raise EDECCipherAuthenticationException.CreateRes(@sInvalidAuthenticationValue); - end; - cmPoly1305: begin - // ########################################### - // #### Now the time is here to create the tag... - FPOLY1305.Finalize; - - if (length(FPOLY1305.ExpectedAuthenticationTag) > 0) and - (not IsEqual(FPOLY1305.ExpectedAuthenticationTag, FPOLY1305.CalculatedAuthenticationTag)) then - raise EDECCipherAuthenticationException.CreateRes(@sInvalidAuthenticationValue); - end; + if Assigned(fAuthObj) then + begin + fAuthObj.FinalizeAEAD; + + if (length(fAuthObj.ExpectedAuthenticationTag) > 0) and + (not IsEqual(fAuthObj.ExpectedAuthenticationTag, fAuthObj.CalculatedAuthenticationTag)) + then + raise EDECCipherAuthenticationException.CreateRes(@sInvalidAuthenticationValue); end; end; @@ -1234,11 +1125,8 @@ procedure TDECCipherModes.OnAfterInitVectorInitialization(const OriginalInitVect begin inherited; - case FMode of - cmGCM: FGCM.Init(self.DoEncode, OriginalInitVector); - cmCCM: FCCM.Init(self.DoEncode, OriginalInitVector); - cmPoly1305: FPOLY1305.Init(self.DoEncode, OriginalInitVector); - end; + if Assigned(fAuthObj) then + fAuthObj.Init(self.DoEncode, OriginalInitVector); end; procedure TDECCipherModes.DecodeCFSx(Source, Dest: PUInt8Array; Size: Integer); diff --git a/Source/DECCipherModesCCM.pas b/Source/DECCipherModesCCM.pas index 3f835f6..966ce7a 100644 --- a/Source/DECCipherModesCCM.pas +++ b/Source/DECCipherModesCCM.pas @@ -78,6 +78,15 @@ TCCM = class(TAuthenticatedCipherModesBase) /// are: 32, 48, 64, 80, 96, 112, 128 /// procedure SetAuthenticationTagLength(const Value: UInt32); override; + + // ########################################### + // #### Empty routines - just to satisfy the interface + procedure InitAuth; override; + procedure UpdateWithEncDecBuf(buf : PUInt8Array; Size : Integer); override; + procedure LocEncodeDecode(Source, Dest: Pointer; Size: Integer); override; + procedure FinalizeMAC( authLen, encDecBufSize : Int64 ); override; + + procedure Burn; override; public /// /// Savely clear any buffers @@ -396,4 +405,35 @@ procedure TCCM.Init(EncryptionMethod : TEncodeDecodeMethod; FOrigInitVector := InitVector; end; + +procedure TCCM.UpdateWithEncDecBuf(buf: PUInt8Array; Size: Integer); +begin + inherited; + +end; + +procedure TCCM.InitAuth; +begin + inherited; + +end; + +procedure TCCM.LocEncodeDecode(Source, Dest: Pointer; Size: Integer); +begin + inherited; + +end; + +procedure TCCM.Burn; +begin + inherited; + +end; + +procedure TCCM.FinalizeMAC(authLen, encDecBufSize: Int64); +begin + inherited; + +end; + end. diff --git a/Source/DECCipherModesGCM.pas b/Source/DECCipherModesGCM.pas index ed2cde5..1eb2473 100644 --- a/Source/DECCipherModesGCM.pas +++ b/Source/DECCipherModesGCM.pas @@ -52,16 +52,37 @@ interface /// Galois Counter Mode specific methods /// TGCM = class(TAuthenticatedCipherModesBase) + strict private + const cGCMBlkSize = 16; strict private /// /// Empty value? /// nullbytes : T128; + + /// + /// if flag is set no more encoding is allowed + /// + fIsLastBlock : boolean; + + /// + /// One reserve buffer for the GCM intermediate blocks + /// + FData : Array[0..cGCMBlkSize-1] of Byte; + + /// + /// Current index of non encoded fdata bytes + /// + FDataIdx : integer; + /// /// Table with precalculated values /// FM : array[0..15,0..255] of T128; + /// + FGHash : T128; + /// /// Required for creating the table and encryption at least /// @@ -174,6 +195,11 @@ TGCM = class(TAuthenticatedCipherModesBase) /// procedure INCR(var Y : T128); + /// + // ########################################### + // #### blocked version of GaloisHash functions + // ########################################### + /// /// Calculates the hash value /// @@ -193,10 +219,18 @@ TGCM = class(TAuthenticatedCipherModesBase) /// /// Calculated raw hash value which will later get returned as AuthenticatedTag /// - function CalcGaloisHash(AuthenticatedData : PUInt8Array; - AuthLen : Integer; - Ciphertext : PUInt8Array; - CiphertextSize : Integer): T128; + function CalcGaloisHash(AuthenticatedData: PUInt8Array; AuthLen: integer; + Ciphertext: PUInt8Array; CiphertextSize: Integer): T128; + + + + /// + /// Finalizes the Hash using the Authdata length and accumulated CipherText length. + /// + /// + /// Calculated raw hash value which will later get returned as AuthenticatedTag + /// + /// /// Encrypts a T128 value using the encryption method specified on init @@ -207,7 +241,7 @@ TGCM = class(TAuthenticatedCipherModesBase) /// /// Encrypted value /// - function EncodeT128(Value: T128): T128; + function EncodeT128(Value: T128): T128; inline; strict protected /// /// Defines the length of the resulting authentication value in bit. @@ -219,50 +253,65 @@ TGCM = class(TAuthenticatedCipherModesBase) /// constrains the length of the input data and the lifetime of the key. /// procedure SetAuthenticationTagLength(const Value: UInt32); override; - public + + /// - /// Should be called when starting encryption/decryption in order to - /// initialize internal tables etc. + /// Finalizes the Poly1305 calculation + /// The last block is padded and one additional block containing + /// the processed length + the legnth processed AuthenticationBytes + /// is created and fed into the polynom. + /// After this call the MAC is valid. /// - /// - /// Encryption method of the cypher used + procedure FinalizeMAC( authLen, encDecBufLen : int64); override; + + /// + /// Initializes the Galois Hash function internal data and starts + /// with the DataToAuthenticate field. + /// + procedure InitAuth; override; + + /// + /// Updates the hash with a given cipher text. Internally the ciphertext length + /// field is also updated + /// + /// + /// Pointer the data that updates the hash. /// - /// - /// Initialization vector + /// + /// Length of the buffer /// - procedure Init(EncryptionMethod : TEncodeDecodeMethod; - InitVector : TBytes); override; + procedure UpdateWithEncDecBuf(buf : PUInt8Array; Size : Integer); override; + /// - /// Encodes a block of data using the supplied cipher + /// Encoding/Decoding routine - For some methods it is sufficient to just call + /// the given encoding routine (poly1305, chacha handles that internally) - some need to update the cipher (e.g. aes gcm) /// /// - /// Plain text to encrypt + /// Pointer the data that updates the hash. /// /// - /// Ciphertext after encryption + /// Pointer the data that updates the hash. /// - /// - /// Number of bytes to encrypt + /// + /// Length of the buffer /// - procedure Encode(Source, - Dest : PUInt8Array; - Size : Integer); override; + procedure LocEncodeDecode(Source, Dest: Pointer; Size: Integer); override; + + + procedure Burn; override; + public /// - /// Decodes a block of data using the supplied cipher + /// Should be called when starting encryption/decryption in order to + /// initialize internal tables etc. /// - /// - /// Encrypted ciphertext to decrypt - /// - /// - /// Plaintext after decryption + /// + /// Encryption method of the cypher used /// - /// - /// Number of bytes to decrypt + /// + /// Initialization vector /// - procedure Decode(Source, - Dest : PUInt8Array; - Size : Integer); override; - + procedure Init(EncryptionMethod : TEncodeDecodeMethod; + InitVector : TBytes); override; /// /// Returns a list of authentication tag lengths explicitely specified by /// the official specification of the standard. @@ -275,6 +324,14 @@ TGCM = class(TAuthenticatedCipherModesBase) implementation +uses Math; + +function TGCM.EncodeT128(Value: T128): T128; +begin + FEncryptionMethod(@Value[0], @Result[0], 16); +end; + + function TGCM.XOR_T128(const x, y : T128): T128; begin Result[0] := x[0] xor y[0]; @@ -436,11 +493,13 @@ procedure TGCM.Init(EncryptionMethod : TEncodeDecodeMethod; begin inherited; + FEncryptionMethod := EncryptionMethod; + Nullbytes[0] := 0; Nullbytes[1] := 0; OldH := FH; - EncryptionMethod(@Nullbytes[0], @FH[0], 16); + FEncryptionMethod(@Nullbytes[0], @FH[0], 16); // Only generate the table when not already generated if (OldH[0] <> FH[0]) or (OldH[1] <> FH[1]) then @@ -460,205 +519,259 @@ procedure TGCM.Init(EncryptionMethod : TEncodeDecodeMethod; FEncryptionMethod(@FY[0], @FE_K_Y0[0], 16); end; -function TGCM.CalcGaloisHash(AuthenticatedData : PUInt8Array; AuthLen : integer; Ciphertext : PUInt8Array; - CiphertextSize: Integer): T128; -var - AuthCipherLength : T128; - x : T128; - n : Uint64; +procedure TGCM.InitAuth; +begin + FillChar(fData, sizeof(fData), 0); + FDataIdx := 0; - procedure encode(data : PUInt8Array; dataSize: Integer); - var - i, mod_d, div_d, len_d : UInt64; - hdata : T128; - begin - len_d := dataSize; - if (len_d > 0) then - begin - n := 0; - div_d := len_d div 16; - if div_d > 0 then - begin - for i := 0 to div_d-1 do - begin - x := poly_mult_H(XOR_PointerWithT128(@data^[n], x )); - inc(n, 16); - end; - end; + FGHash := nullbytes; + if Length(FDataToAuthenticate) > 0 then + UpdateWithEncDecBuf(@FDataToAuthenticate[0], Length(FDataToAuthenticate)); +end; - mod_d := len_d mod 16; - if mod_d > 0 then - begin - hdata := nullbytes; - Move(data^[n], hdata[0], mod_d); - x := poly_mult_H(XOR_T128(hdata, x)); - end; +procedure TGCM.LocEncodeDecode(Source, Dest: Pointer; Size: Integer); +var i, j : integer; + div_len_plain : integer; + +begin + if fIsLastBlock then + raise Exception.Create('Already last block processed. Call the encode only with a blocksize of 16 bytes'); + i := 0; + div_len_plain := Size div 16; + + for j := 1 to div_len_plain do + begin + INCR(FY); + + P128(@PUInt8Array(Dest)^[i])^ := XOR_PointerWithT128(@PUInt8Array(Source)^[i], EncodeT128(FY)); + + inc(i,16); + end; + + // is it the last block? + if i < Size then + begin + fIsLastBlock := True; + INCR(FY); + XOR_ArrayWithT128(Source, i, Size - i, EncodeT128(FY), Dest); end; - end; +end; +procedure TGCM.FinalizeMAC(authLen, encDecBufLen: int64); +var res : T128; + AuthCipherLength : T128; + x : T128; begin - x := nullbytes; - if AuthLen > 0 then - encode(@AuthenticatedData[0], AuthLen); - //Assert(length(Ciphertext) >= CiphertextSize); - encode(Ciphertext, CiphertextSize); - SetAuthenticationCipherLength(AuthCipherLength, AuthLen shl 3, CiphertextSize shl 3); + // last block::: + if FDataIdx > 0 then + begin + x := nullbytes; + Move(fData[0], x, FDataIdx); + FGHash := poly_mult_H(XOR_T128(x, FGHash)); + fDataIdx := 0; + end; + + // update hash with the lengths... + SetAuthenticationCipherLength(AuthCipherLength, authLen shl 3, encDecBufLen shl 3); + res := XOR_T128(poly_mult_H(XOR_T128(AuthCipherLength, FGHash)), FE_K_Y0); + + // copy and burn + if Length(FCalcAuthenticationTag) > 0 then + Move(res, FCalcAuthenticationTag[0], Min(sizeof(res), length(FCalcAuthenticationTag))); + res := nullbytes; + FillChar(FData, sizeof(fData), 0); +end; - Result := poly_mult_H(XOR_T128(AuthCipherLength, x)); +procedure TGCM.UpdateWithEncDecBuf(buf: PUInt8Array; Size: Integer); +var i, div_d, len_d : integer; + n : integer; +begin + n := 0; + if FDataIdx > 0 then + begin + if cGCMBlkSize - fDataIdx > Size then + begin + Move(buf^[0], fData[FDataIdx], size); + inc(FDataIdx, size); + exit; + end + else + begin + // encode one block + n := cGCMBlkSize - fDataIdx; + Move( buf^[0], fData[fDataIdx], n); + FGHash := poly_mult_H(XOR_PointerWithT128(@fData[0], FGHash )); + FDataIdx := 0; + end; + + end; + + len_d := size - n; + if (len_d > 0) then + begin + div_d := len_d div cGCMBlkSize; + if div_d > 0 then + begin + for i := 0 to div_d - 1 do + begin + FGHash := poly_mult_H(XOR_PointerWithT128(@buf^[n], FGHash )); + inc(n, cGCMBlkSize); + end; + end; + end; + + if n < size then + begin + Move(buf^[n], fData[fDataIdx], size - n); + inc(FDataIdx, size - n); + end; end; -procedure TGCM.Decode(Source, Dest: PUInt8Array; Size: Integer); +(* +procedure TGCM.Encode(Source, Dest: PUInt8Array; Size: Integer); var - i, j, BlockCount : UInt64; - a_tag : T128; + i, j, div_len_plain : UInt64; + AuthTag : T128; pDataToAuth : PUInt8Array; - pSrc : PUInt8Array; begin i := 0; - BlockCount := Size div 16; + div_len_plain := Size div 16; - for j := 1 to BlockCount do + for j := 1 to div_len_plain do begin INCR(FY); + P128(@Dest^[i])^ := XOR_PointerWithT128(@Source^[i], EncodeT128(FY)); - inc(i, 16); + + inc(i,16); end; if i < Size then begin INCR(FY); - XOR_ArrayWithT128(@Source^[0], i, UInt64(Size)-i, EncodeT128(FY), @Dest^[0]); + XOR_ArrayWithT128(Source, i, UInt64(Size)-i, EncodeT128(FY), Dest); end; pDataToAuth := nil; if Length(DataToAuthenticate) > 0 then pDataToAuth := @DataToAuthenticate[0]; - pSrc := nil; - if Size > 0 then - pSrc := @source[0]; - - a_tag := XOR_T128(CalcGaloisHash(pDataToAuth, Length(DataToAuthenticate), - pSrc, Size), FE_K_Y0); - + AuthTag := XOR_T128(CalcGaloisHash(pDataToAuth, Length(DataToAuthenticate), @Dest[0], Size), FE_K_Y0); Setlength(FCalcAuthenticationTag, FCalcAuthenticationTagLength); if (FCalcAuthenticationTagLength > 0) then - Move(a_tag[0], FCalcAuthenticationTag[0], FCalcAuthenticationTagLength); - - // Check for correct authentication result is in Done of DECCipherModes - // if not IsEqual(FExpectedAuthenticationTag, FCalcAuthenticationTag) then - // raise EDECCipherAuthenticationException.CreateRes(@sInvalidAuthenticationValue); - - // In difference to the NIST recommendation we do not discard plaintext if - // authentication failed to make data recovery possible. But since we throw - // an exception the user will get notified that there's something wrong - // if not IsEqual(authenticaton_tag, ba_tag) then - // SetLength(plaintext, 0); // NIST FAIL => pt='' + Move(AuthTag[0], FCalcAuthenticationTag[0], FCalcAuthenticationTagLength); end; -procedure TGCM.Encode(Source, Dest: PUInt8Array; Size: Integer); +*) + +(* +procedure TGCM.EncodeGCMBlk(Ciphertext, Dest : PUInt8Array; + CiphertextSize : Integer; + lastBlock: boolean); var - i, j, div_len_plain : UInt64; + i, j, div_len_plain : integer; AuthTag : T128; - pDataToAuth : PUInt8Array; begin + // len = 0 -> first block. Init the hash + if FCipherLen = 0 then + BeginCalcGaloisHash; + + if not lastBlock and (CiphertextSize mod 16 <> 0) then + raise Exception.Create('Only multiple of 16bytes are allowed in block mode'); + i := 0; - div_len_plain := Size div 16; + div_len_plain := CiphertextSize div 16; for j := 1 to div_len_plain do begin INCR(FY); - P128(@Dest^[i])^ := XOR_PointerWithT128(@Source^[i], EncodeT128(FY)); + P128(@Dest^[i])^ := XOR_PointerWithT128(@Ciphertext^[i], EncodeT128(FY)); inc(i,16); end; - if i < Size then + if lastBlock then begin - INCR(FY); - XOR_ArrayWithT128(Source, i, UInt64(Size)-i, EncodeT128(FY), Dest); - end; + if i < CiphertextSize then + begin + INCR(FY); + XOR_ArrayWithT128(Ciphertext, i, CiphertextSize-i, EncodeT128(FY), Dest); + end; - pDataToAuth := nil; - if Length(DataToAuthenticate) > 0 then - pDataToAuth := @DataToAuthenticate[0]; - AuthTag := XOR_T128(CalcGaloisHash(pDataToAuth, Length(DataToAuthenticate), @Dest[0], Size), FE_K_Y0); - Setlength(FCalcAuthenticationTag, FCalcAuthenticationTagLength); - if (FCalcAuthenticationTagLength > 0) then - Move(AuthTag[0], FCalcAuthenticationTag[0], FCalcAuthenticationTagLength); -end; + UpdateGaloisHash(Dest, CiphertextSize); -function TGCM.EncodeT128(Value: T128): T128; -begin - FEncryptionMethod(@Value[0], @Result[0], 16); + //AuthTag := XOR_T128(CalcGaloisHash(DataToAuthenticate, Dest, Size), FE_K_Y0); + AuthTag := XOR_T128(FinishGaloisHash, FE_K_Y0); + Setlength(FCalcAuthenticationTag, FCalcAuthenticationTagLength); + if (FCalcAuthenticationTagLength > 0) then + Move(AuthTag[0], FCalcAuthenticationTag[0], FCalcAuthenticationTagLength); + end + else + begin + UpdateGaloisHash(Dest, CiphertextSize); + end; end; +*) + function TGCM.GetStandardAuthenticationTagBitLengths: TStandardBitLengths; begin SetLength(Result, 5); Result := [96, 104, 112, 120, 128]; end; -// -//function decrypt( const key, IV : TBytes; out plaintext : TBytes; const authenticated_data, -//ciphertext : TBytes; len_auth_tag : integer; const authenticaton_tag : TBytes ) : boolean; -//var -// i, j, div_len_ciph, len_ciph : Uint64; -// a_tag, E_K_Y0, Y, H : T128; -// bY : array[0..15] of byte absolute Y[0]; -// ba_Tag : TBytes; -// -// function equal( const a, b : TBytes ):boolean; -// begin -// if length(a) <> length(b) then Result := false -// else -// Result := CompareMem( @a[0], @b[0], length(a) ); -// end; -// -//begin -// len_auth_tag := len_auth_tag shr 3; -// -// E_Init( key ); -// H := E_Cipher( nullbytes ); -// Table_M_8Bit(H); -// -// len_ciph := length( ciphertext ); -// SetLength( plaintext, len_ciph ); -// -// if length(IV) = 12 then -// begin -// Y[1] := 0; -// Move( IV[0], Y[0], 12 ); -// bY[15] := 1; -// end -// else -// Y := CalcGaloisHash( H, nil, IV ); -// -// E_K_Y0 := E_Cipher( y ); -// -// i := 0; -// div_len_ciph := len_ciph div 16; -// for j := 1 to div_len_ciph do -// begin -// INCR( Y ); -// P128(@plaintext[i])^ := XOR_128_n( @ciphertext[i], E_cipher( Y ) ); -// inc(i,16); -// end; -// -// if i < len_ciph then -// begin -// INCR( Y ); -// XOR_128_n_l( ciphertext, i, len_ciph-i, E_cipher( Y ), plaintext ); -// end; -// -// a_tag := XOR_128( CalcGaloisHash( H, authenticated_data, ciphertext ), E_K_Y0 ); -// -// Setlength( ba_tag, len_auth_tag ); -// Move( a_tag[0], ba_tag[0], len_auth_tag ); -// -// Result := equal( authenticaton_tag, ba_tag ); -// if not Result then SetLength( plaintext, 0 ); // NIST FAIL => pt='' -//end; -// +procedure TGCM.Burn; +begin + inherited; + + FH := nullbytes; +end; + +function TGCM.CalcGaloisHash(AuthenticatedData : PUInt8Array; AuthLen : integer; Ciphertext : PUInt8Array; + CiphertextSize: Integer): T128; +var + AuthCipherLength : T128; + x : T128; + n : Uint64; + + procedure encode(data : PUInt8Array; dataSize: Integer); + var + i, mod_d, div_d, len_d : UInt64; + hdata : T128; + begin + len_d := dataSize; + if (len_d > 0) then + begin + n := 0; + div_d := len_d div 16; + if div_d > 0 then + begin + for i := 0 to div_d-1 do + begin + x := poly_mult_H(XOR_PointerWithT128(@data^[n], x )); + inc(n, 16); + end; + end; + + mod_d := len_d mod 16; + if mod_d > 0 then + begin + hdata := nullbytes; + Move(data^[n], hdata[0], mod_d); + x := poly_mult_H(XOR_T128(hdata, x)); + end; + end; + end; + +begin + x := nullbytes; + if AuthLen > 0 then + encode(@AuthenticatedData[0], AuthLen); + //Assert(length(Ciphertext) >= CiphertextSize); + encode(Ciphertext, CiphertextSize); + SetAuthenticationCipherLength(AuthCipherLength, AuthLen shl 3, CiphertextSize shl 3); + + Result := poly_mult_H(XOR_T128(AuthCipherLength, x)); +end; end. diff --git a/Source/DECCipherModesPoly1305.pas b/Source/DECCipherModesPoly1305.pas index 5495a6f..c09ae04 100644 --- a/Source/DECCipherModesPoly1305.pas +++ b/Source/DECCipherModesPoly1305.pas @@ -41,101 +41,88 @@ TPoly1305 = class(TAuthenticatedCipherModesBase) const POLY1305_BLOCK_SIZE = 16; POLY1305_DIGEST_SIZE = 16; POLY1305_KEY_SIZE = 32; - - POLY1305_SSE2_BLOCK_SIZE = 32; type TPoly1305Nonce = Array[0..3] of UInt32; TPoly1305BlockMethod = procedure ( pData : PByteArray; size : integer ) of Object; TPoly1305EmitMethod = procedure( var mac: TBlock16Byte ) of Object; + TPoly1305PadAndFinalize = procedure( authLen, encDecBufLen : int64) of Object; THArr = Array[0..4] of UInt32; PHArr = ^THArr; TRArr = Array[0..3] of UInt32; PRArr = ^TRArr; - TYMM = Array[0..7] of UInt32; - TYMM64 = Array[0..3] of Int64; - TXMM = Array[0..3] of UInt32; - - TPoly1305StateFlag = (poly1305_started = 1, poly1305_final_shift8 = 4, - poly1305_final_shift16 = 8, - poly1305_final_r2_r = 16, // use [r^2,r] for the final block */ - poly1305_final_r_1 = 32 // use [r,1] for the final block */ - ); - - type - TH = packed record - case byte of - 0: (H : Array[0..2] of UInt64); - 1: (HH: Array[0..19] of Uint32); - 2: (H32 : THArr); - end; - - TPoly1305_state_internal_t = packed record - hmac : TH; - R : Array[0..4] of Uint32; - R2 : Array[0..4] of UInt32; - R3 : Array[0..4] of UInt32; - R4 : Array[0..4] of UInt32; - pad : Array[0..1] of UInt64; - flags : Uint64; - end; - PPoly1305_state_internal_t = ^TPoly1305_state_internal_t; - - TPoly1305State = Array[0..191] of Byte; + TPoly1305State = Array[0..160] of Byte; private - // avx structures + /// + /// avx buffer for h and R (may be extended in future fo for SSE, AVX) + /// fPoly1305State : TPoly1305State; - fPPoly1305State : PPoly1305_state_internal_t; - // pas structures + /// + /// H and R values. These point ot the fPoly1305State buffer + /// FH : PHArr; FR : PRArr; - // - + /// + /// Initialization vector and nonce - initialized from the 32Byte init vector + /// fIV : T32ByteArray; FNonce : TPoly1305Nonce; - FEncryptionMethod : TEncodeDecodeMethod; - - // reminder... + /// + /// Internal state variables - number of remaining bytes, block size and intermediate buffer + /// fNum : integer; - fData : Array[0..POLY1305_BLOCK_SIZE - 1] of Byte; + fPolyBlockSize : integer; + fData : Array[0..2*POLY1305_BLOCK_SIZE - 1] of Byte; // for both the sse and pas version - FAuthDataLen : integer; - FCipherLen : integer; + /// + /// #bytes of encrypted data + /// fPolyInitComplete : boolean; + + /// + /// Reference to the actual poly block function. For future SSE, AVX use + /// fPolyBlkFunc : TPoly1305BlockMethod; - fPolyEmitFunc : TPoly1305EmitMethod; + fPadAndFinalizeFunc : TPoly1305PadAndFinalize; - function U8ToU32( pData : PByteArray ) : UInt32; - procedure U32ToU8(pData : PByteArray; value : UInt32); + function U8ToU32( pData : PByteArray ) : UInt32; inline; + procedure U32ToU8(pData : PByteArray; value : UInt32); inline; + /// + /// Poly1305 blocks function + /// procedure Poly1305Blocks( pData : PByteArray; size : integer; padBit : UInt32 ); - procedure Poly1305Emit( var mac : TBlock16Byte; const nonce : TPoly1305Nonce ); - procedure Poly1305Finalize( var mac : TBlock16Byte ); - - procedure Poly1305Init_SSE( initVec : PByte ); - procedure Poly1305Blocks_SSE( pData : PByteArray; size : integer ); - procedure Poly1305Emit_SSE( var mac : TBlock16Byte ); - procedure Burn; + /// + /// Finaly MAC creating function + /// + procedure Poly1305Emit( var mac : TBlock16Byte; const nonce : TPoly1305Nonce ); + procedure PadAndFinalizePAS( authLen, encDecBufLen : int64); protected - // ########################################### - // #### base functionality - // ########################################### // #### Authentication block functions - procedure BeginPoly1304Auth; - public - class var UseSSE : boolean; - - constructor Create; - procedure UpdatePoly( pData : PByteArray; size : integer); procedure InitInternal(const InitVector : T32ByteArray); - procedure PadAndFinalizeAEAD; procedure Finalize; + /// + /// Finalizes the Poly1305 calculation + /// The last block is padded and one additional block containing + /// the processed length + the legnth processed AuthenticationBytes + /// is created and fed into the polynom. + /// After this call the MAC is valid. + /// + procedure FinalizeMAC( authLen, encDecBufLen : int64); override; + procedure InitAuth; override; + procedure UpdateWithEncDecBuf(buf : PUInt8Array; Size : Integer); override; + + procedure Burn; override; + + public + constructor Create; + /// /// Should be called when starting encryption/decryption in order to /// initialize internal tables etc. @@ -148,24 +135,6 @@ TPoly1305 = class(TAuthenticatedCipherModesBase) /// procedure Init(EncryptionMethod : TEncodeDecodeMethod; InitVector : TBytes); override; - - /// - /// Encodes a block of data using the supplied cipher. - /// The function can be called multiple times. Internally the - /// hash is updated on each call. - /// - procedure Encode(Source, Dest : PUInt8Array; - CiphertextSize : Integer); override; - - /// - /// Decodes a block of data using the supplied cipher. - /// The function can be called multiple times. Internally the - /// hash is updated on each call. - /// - procedure Decode(Source, - Dest : PUInt8Array; - Size : Integer - ); override; end; implementation @@ -180,40 +149,56 @@ function ConstTimeCarray32(a, b : UInt32 ) : UInt32; inline; Result := (a xor ( (a xor b) or (a - b) xor b )) shr 31; end; + { TPoly1305 } -procedure TPoly1305.BeginPoly1304Auth; +procedure TPoly1305.U32ToU8(pData: PByteArray; value: UInt32); +begin + pData^[0] := Byte(value); + pData^[1] := Byte(value shr 8); + pData^[2] := Byte(value shr 16); + pData^[3] := Byte(value shr 24); +end; + +function TPoly1305.U8ToU32(pData: PByteArray): UInt32; +begin + Result := (UInt32(pData^[0]) and $ff) or + ((UInt32(pData^[1]) and $ff) shl 8) or + ((UInt32(pData^[2]) and $ff) shl 16) or + ((UInt32(pData^[3]) and $ff) shl 24); +end; + + +procedure TPoly1305.InitAuth; var aLen : integer; vec : TBytes; begin InitInternal(fIV); + aLen := Length(FDataToAuthenticate); // update the polynom with the unencrypted authentication data - FAuthDataLen := Length(FDataToAuthenticate); - if fAuthDataLen > 0 then + if aLen > 0 then begin // pad to blocksize if ncessary - aLen := Length(FDataToAuthenticate); if aLen mod POLY1305_BLOCK_SIZE <> 0 then begin - aLen := aLen + POLY1305_BLOCK_SIZE - aLen mod POLY1305_BLOCK_SIZE; - SetLength(vec, aLen); - Move(FDataToAuthenticate[0], vec[0], FAuthDataLen); - //vec[FAuthDataLen] := 1; + aLen := aLen + POLY1305_BLOCK_SIZE - aLen mod POLY1305_BLOCK_SIZE; + SetLength(vec, aLen); + Move(FDataToAuthenticate[0], vec[0], Length(FDataToAuthenticate)); end else vec := FDataToAuthenticate; - fPolyBlkFunc( @vec[0], POLY1305_BLOCK_SIZE); + fPolyBlkFunc( @vec[0], Length(vec)); end; - FCipherLen := 0; end; procedure TPoly1305.Burn; begin - FillChar(FH, sizeof(FH), 0); + inherited; + + FillChar(fPoly1305State, sizeof(fPoly1305State), 0); FillChar(FNonce, sizeof(FNonce), 0); - FillChar(FR, sizeof(FR), 0); FillChar(fIV, sizeof(fIV), 0); FillChar(fData, sizeof(fData), 0); fNum := 0; @@ -231,58 +216,38 @@ constructor TPoly1305.Create; begin inherited Create; - fPPoly1305State := @fPoly1305State[0]; FH := @fPoly1305State[0]; FR := @fPoly1305State[sizeof(THarr)]; - if useSSE then - begin - fPolyBlkFunc := Poly1305Blocks_SSE; // Poly1305Blocks_AVX - fPolyEmitFunc := Poly1305Emit_SSE; - end - else - begin - fPolyBlkFunc := UpdatePoly; - fPolyEmitFunc := Poly1305Finalize; - end; + // for future use -> here is a good place to add sse AVX functions + fPadAndFinalizeFunc := PadAndFinalizePAS; + fPolyBlkFunc := UpdatePoly; + fPolyBlockSize := POLY1305_BLOCK_SIZE; end; -procedure TPoly1305.Decode(Source, Dest: PUInt8Array; Size: Integer); -begin - if not fPolyInitComplete then - BeginPoly1304Auth; - - fPolyBlkFunc( PByteArray( Source ), Size ); - inc( FCipherLen, Size ); - - FEncryptionMethod( Source, Dest, Size ); -end; - -procedure TPoly1305.Encode(Source, Dest: PUInt8Array; - CiphertextSize: Integer); -begin - if not fPolyInitComplete then - BeginPoly1304Auth; - - FEncryptionMethod( Source, Dest, CipherTextSize ); - fPolyBlkFunc( PByteArray( dest ), CipherTextSize ); - inc( FCipherLen, CipherTextSize ); -end; - - procedure TPoly1305.Finalize; var mac : TBlock16Byte; begin - if not fPolyInitComplete then - BeginPoly1304Auth; + if fNum > 0 then + begin + fData[fNum] := 1; + inc(fNum); + while fNum < Length(fData) do + begin + fData[fNum] := 0; + inc(fNum); + end; + + Poly1305Blocks(@fData[0], POLY1305_BLOCK_SIZE, 0); + fNum := 0; + end; - fPolyEmitFunc( mac ); + Poly1305Emit(mac, FNonce); SetLength(FCalcAuthenticationTag, sizeof(mac)); Move(mac, FCalcAuthenticationTag[0], sizeof(mac)); FillChar(mac, sizeof(mac), 0); - Burn; end; procedure TPoly1305.Init(EncryptionMethod: TEncodeDecodeMethod; @@ -302,51 +267,43 @@ procedure TPoly1305.Init(EncryptionMethod: TEncodeDecodeMethod; // build the poly1305 iv from the first 256 bits FillChar( fIV, sizeof(fIV), 0); Move( initVector[0], fIV, Length(initVector)); - - FAuthDataLen := 0; - FCipherLen := 0; - FEncryptionMethod := EncryptionMethod; end; procedure TPoly1305.InitInternal(const InitVector: T32ByteArray); begin fPolyInitComplete := True; - if UseSSE then - begin - Poly1305Init_SSE(@initVector[0]); - end - else - begin - FillChar(FH^, sizeof(FH^), 0); - - ///* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ - // st->r[0] = U8TOU32(&key[0]) & 0x0fffffff; - // st->r[1] = U8TOU32(&key[4]) & 0x0ffffffc; - // st->r[2] = U8TOU32(&key[8]) & 0x0ffffffc; - // st->r[3] = U8TOU32(&key[12]) & 0x0ffffffc; - FR^[0] := U8ToU32(@initVector[0]) and $0fffffff; - FR^[1] := U8ToU32(@initVector[4]) and $0ffffffc; - FR^[2] := U8ToU32(@initVector[8]) and $0ffffffc; - FR^[3] := U8ToU32(@initVector[12]) and $0ffffffc; + FillChar(FH^, sizeof(FH^), 0); + ///* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ +// st->r[0] = U8TOU32(&key[0]) & 0x0fffffff; +// st->r[1] = U8TOU32(&key[4]) & 0x0ffffffc; +// st->r[2] = U8TOU32(&key[8]) & 0x0ffffffc; +// st->r[3] = U8TOU32(&key[12]) & 0x0ffffffc; + FR^[0] := U8ToU32(@initVector[0]) and $0fffffff; + FR^[1] := U8ToU32(@initVector[4]) and $0ffffffc; + FR^[2] := U8ToU32(@initVector[8]) and $0ffffffc; + FR^[3] := U8ToU32(@initVector[12]) and $0ffffffc; - FNonce[0] := U8ToU32(@initVector[16]); - FNonce[1] := U8ToU32(@initVector[20]); - FNonce[2] := U8ToU32(@initVector[24]); - FNonce[3] := U8ToU32(@initVector[28]); - end; - fNum := 0; + FNonce[0] := U8ToU32(@initVector[16]); + FNonce[1] := U8ToU32(@initVector[20]); + FNonce[2] := U8ToU32(@initVector[24]); + FNonce[3] := U8ToU32(@initVector[28]); + fNum := 0; end; -procedure TPoly1305.PadAndFinalizeAEAD; -var lens : Array[0..1] of UInt64; +procedure TPoly1305.FinalizeMAC( authLen, encDecBufLen : int64); begin - if not fPolyInitComplete then - BeginPoly1304Auth; + inherited; + fPadAndFinalizeFunc(authLen, encDecBufLen); +end; + +procedure TPoly1305.PadAndFinalizePAS( authLen, encDecBufLen : int64); +var lens : Array[0..1] of Int64; +begin // pad the last block with 0 if fNum > 0 then begin @@ -364,8 +321,8 @@ procedure TPoly1305.PadAndFinalizeAEAD; // finalize with the tag with the lengths! - lens[0] := FAuthDataLen; - lens[1] := FCipherLen; + lens[0] := authLen; + lens[1] := encDecBufLen; Poly1305Blocks(@lens[0], POLY1305_BLOCK_SIZE, 1); Finalize; @@ -576,1401 +533,6 @@ procedure TPoly1305.Poly1305Emit(var mac: TBlock16Byte; U32ToU8(@mac[12], h3); end; -type - XMMWORD = Array[0..7] of Word; - -// dump from poly1305-donna-x86-sse2-incremental-source.c -procedure _poly1305_init_ext_sse2(st : TPoly1305.PPoly1305_state_internal_t; key : PByte; bytes : integer); cdecl; -asm - push ebp - pxor xmm0, xmm0 - mov ebp, esp - push edi - push esi - push ebx - and esp, -8 - sub esp, 56 - mov edx, DWORD PTR [ebp+12] - mov eax, DWORD PTR [ebp+8] - movups XMMWORD PTR [eax+32], xmm0 - movups XMMWORD PTR [eax], xmm0 - movups XMMWORD PTR [eax+16], xmm0 - mov ebx, DWORD PTR [edx] - mov eax, DWORD PTR [edx+4] - mov ecx, DWORD PTR [edx+8] - mov edi, DWORD PTR [edx+12] - mov esi, ebx - shr ebx, 26 - and esi, 67108863 - mov DWORD PTR [esp+40], esi - mov esi, eax - shr eax, 20 - sal esi, 6 - or esi, ebx - mov ebx, esi - mov esi, DWORD PTR [esp+40] - and ebx, 67108611 - mov DWORD PTR [esp+36], ebx - mov ebx, ecx - shr ecx, 14 - sal ebx, 12 - or ebx, eax - mov eax, edi - shr edi, 8 - sal eax, 18 - and ebx, 67092735 - and edi, 1048575 - or eax, ecx - mov ecx, eax - mov eax, DWORD PTR [ebp+8] - and ecx, 66076671 - mov DWORD PTR [eax+40], esi - mov esi, eax - mov eax, DWORD PTR [esp+36] - mov DWORD PTR [esi+48], ebx - mov DWORD PTR [esi+52], ecx - mov DWORD PTR [esi+56], edi - mov DWORD PTR [esi+44], eax - mov eax, DWORD PTR [edx+16] - mov DWORD PTR [esi+100], eax - mov eax, DWORD PTR [edx+20] - mov DWORD PTR [esi+104], eax - mov eax, DWORD PTR [edx+24] - mov DWORD PTR [esi+108], eax - mov eax, DWORD PTR [edx+28] - lea edx, [ecx+ecx*4] - mov DWORD PTR [esi+112], eax - mov esi, edx - lea edx, [edi+edi] - mov DWORD PTR [esp+28], edi - lea edi, [ebx+ebx] - mov eax, edi - mov DWORD PTR [esp+48], edx - mul esi - mov DWORD PTR [esp+12], esi - mov DWORD PTR [esp+32], ebx - lea ebx, [ebx+ebx*4] - mov esi, eax - mov eax, DWORD PTR [esp+36] - mov edi, edx - lea eax, [eax+eax*4] - mul DWORD PTR [esp+48] - add esi, eax - mov eax, DWORD PTR [esp+40] - adc edi, edx - mul eax - add esi, eax - adc edi, edx - mov eax, esi - mov edx, edi - mov edi, DWORD PTR [esp+36] - mov DWORD PTR [esp], eax - and eax, 67108863 - mov DWORD PTR [esp+4], edx - lea esi, [edi+edi] - mov DWORD PTR [esp+8], eax - mov eax, ebx - mov DWORD PTR [esp+20], esi - mul DWORD PTR [esp+48] - mov esi, DWORD PTR [esp+40] - lea edi, [esi+esi] - lea esi, [ecx+ecx] - mov DWORD PTR [esp+24], edi - mov edi, edx - mov DWORD PTR [esp+16], esi - mov esi, eax - mov eax, DWORD PTR [esp+20] - mul DWORD PTR [esp+40] - add esi, eax - mov eax, ecx - mov ecx, DWORD PTR [esp+36] - adc edi, edx - mul DWORD PTR [esp+12] - add eax, esi - mov esi, DWORD PTR [esp] - adc edx, edi - mov edi, DWORD PTR [esp+4] - shrd esi, edi, 26 - shr edi, 26 - add esi, eax - mov eax, esi - adc edi, edx - and eax, 67108863 - mov DWORD PTR [esp], eax - mov eax, ecx - mul ecx - mov ecx, eax - mov ebx, edx - mov eax, DWORD PTR [esp+32] - mul DWORD PTR [esp+24] - add ecx, eax - mov eax, DWORD PTR [esp+12] - adc ebx, edx - mul DWORD PTR [esp+48] - add eax, ecx - adc edx, ebx - shrd esi, edi, 26 - shr edi, 26 - add esi, eax - mov eax, esi - adc edi, edx - and eax, 67108863 - mov DWORD PTR [esp+48], eax - mov eax, DWORD PTR [esp+40] - mul DWORD PTR [esp+16] - mov ecx, eax - mov ebx, edx - mov eax, DWORD PTR [esp+20] - mul DWORD PTR [esp+32] - add ecx, eax - adc ebx, edx - mov edx, DWORD PTR [esp+28] - lea eax, [edx+edx*4] - mul edx - add eax, ecx - adc edx, ebx - shrd esi, edi, 26 - shr edi, 26 - add eax, esi - mov DWORD PTR [esp+40], eax - mov ebx, DWORD PTR [esp+32] - adc edx, edi - mov esi, eax - mov DWORD PTR [esp+44], edx - and esi, 67108863 - mov edi, DWORD PTR [esp+48] - mov eax, ebx - mul ebx - mov ecx, eax - mov ebx, edx - mov eax, DWORD PTR [esp+28] - mul DWORD PTR [esp+24] - add ecx, eax - mov eax, DWORD PTR [esp+36] - adc ebx, edx - mul DWORD PTR [esp+16] - add eax, ecx - mov ecx, DWORD PTR [esp+40] - adc edx, ebx - mov ebx, DWORD PTR [esp+44] - shrd ecx, ebx, 26 - shr ebx, 26 - add eax, ecx - adc edx, ebx - mov ecx, eax - mov ebx, DWORD PTR [esp+8] - shrd eax, edx, 26 - mov edx, DWORD PTR [esp] - and ecx, 67108863 - lea eax, [eax+eax*4] - mov DWORD PTR [esp+32], ecx - add eax, ebx - mov ebx, eax - shr eax, 26 - add edx, eax - mov eax, DWORD PTR [ebp+8] - and ebx, 67108863 - mov DWORD PTR [esp+40], ebx - mov DWORD PTR [eax+60], ebx - mov DWORD PTR [eax+64], edx - mov DWORD PTR [eax+68], edi - mov DWORD PTR [eax+72], esi - mov DWORD PTR [eax+76], ecx - lea eax, [esi+esi*4] - mov ebx, eax - lea eax, [ecx+ecx] - lea ecx, [edi+edi] - mov DWORD PTR [esp+36], edx - mov DWORD PTR [esp+28], eax - mov eax, ecx - lea edi, [edi+edi*4] - mul ebx - mov DWORD PTR [esp+12], ebx - mov ecx, eax - mov eax, DWORD PTR [esp+40] - mov ebx, edx - mul eax - add ecx, eax - adc ebx, edx - mov edx, DWORD PTR [esp+36] - lea eax, [edx+edx*4] - mul DWORD PTR [esp+28] - add ecx, eax - adc ebx, edx - mov edx, DWORD PTR [esp+36] - lea eax, [edx+edx] - lea edx, [esi+esi] - mov DWORD PTR [esp+16], eax - mov eax, DWORD PTR [esp+40] - mov DWORD PTR [esp+24], edx - add eax, eax - mov DWORD PTR [esp+20], eax - mov eax, ecx - and eax, 67108863 - mov DWORD PTR [esp+8], eax - mov eax, edi - mul DWORD PTR [esp+28] - mov DWORD PTR [esp], eax - mov eax, esi - mov DWORD PTR [esp+4], edx - mul DWORD PTR [esp+12] - mov esi, eax - mov edi, edx - add esi, DWORD PTR [esp] - mov eax, DWORD PTR [esp+16] - adc edi, DWORD PTR [esp+4] - mul DWORD PTR [esp+40] - add eax, esi - adc edx, edi - shrd ecx, ebx, 26 - shr ebx, 26 - add ecx, eax - mov eax, DWORD PTR [esp+48] - adc ebx, edx - mul DWORD PTR [esp+20] - mov esi, ecx - and esi, 67108863 - mov DWORD PTR [esp], esi - mov esi, eax - mov eax, DWORD PTR [esp+36] - mov edi, edx - mul eax - add esi, eax - mov eax, DWORD PTR [esp+28] - adc edi, edx - mul DWORD PTR [esp+12] - add eax, esi - adc edx, edi - shrd ecx, ebx, 26 - shr ebx, 26 - add eax, ecx - mov ecx, DWORD PTR [esp+32] - adc edx, ebx - mov esi, eax - lea ebx, [ecx+ecx*4] - mov edi, edx - mov eax, ebx - mov DWORD PTR [esp+44], edi - mul ecx - mov ecx, eax - mov ebx, edx - mov eax, DWORD PTR [esp+40] - mov DWORD PTR [esp+40], esi - mul DWORD PTR [esp+24] - add ecx, eax - mov eax, DWORD PTR [esp+16] - adc ebx, edx - mul DWORD PTR [esp+48] - add eax, ecx - adc edx, ebx - shrd esi, edi, 26 - shr edi, 26 - add esi, eax - mov eax, DWORD PTR [esp+24] - adc edi, edx - mul DWORD PTR [esp+36] - mov ecx, eax - mov ebx, edx - mov eax, DWORD PTR [esp+32] - mul DWORD PTR [esp+20] - add ecx, eax - mov eax, DWORD PTR [esp+48] - adc ebx, edx - mul eax - add eax, ecx - mov ecx, esi - adc edx, ebx - shrd ecx, edi, 26 - mov ebx, edi - shr ebx, 26 - add eax, ecx - adc edx, ebx - mov DWORD PTR [esp+48], eax - mov ebx, DWORD PTR [ebp+8] - shrd eax, edx, 26 - mov DWORD PTR [esp+52], edx - lea edx, [eax+eax*4] - mov eax, DWORD PTR [esp+8] - add edx, eax - mov eax, DWORD PTR [esp] - mov ecx, edx - shr edx, 26 - add edx, eax - and ecx, 67108863 - mov DWORD PTR [ebx+80], ecx - mov DWORD PTR [ebx+84], edx - mov edx, DWORD PTR [esp+40] - mov eax, DWORD PTR [esp+48] - mov DWORD PTR [ebx+116], 0 - and edx, 67108863 - mov DWORD PTR [ebx+88], edx - mov edx, esi - and eax, 67108863 - and edx, 67108863 - mov DWORD PTR [ebx+96], eax - mov DWORD PTR [ebx+92], edx - lea esp, [ebp-12] - pop ebx - pop esi - pop edi - pop ebp -end; - -procedure _Poly1305_Blocks_sse2( st : TPoly1305.PPoly1305_state_internal_t; m : PByte; bytes : integer ); cdecl; -label L6, L7, L8, L10, L12, L13, L14, L15, L16, L18, L19, L28, L29; -const LC3 : TPoly1305.TXMM = ( 1, 0, 0, 0 ); - LC4 : TPoly1305.TXMM = ( 16777216, 0, 16777216, 0); - LC5 : TPoly1305.TXMM = ( 67108863, 0, 67108863, 0); - LC6 : TPoly1305.TXMM = ( 5, 0, 5, 0); -asm - push ebp - mov ebp, esp - push edi - push esi - push ebx - and esp, -16 - sub esp, 1168 - mov esi, DWORD PTR [ebp+8] - mov eax, DWORD PTR [ebp+12] - movdqa xmm0, XMMWORD PTR LC4 - mov ecx, DWORD PTR [ebp+16] - mov edx, DWORD PTR [esi+116] - movaps XMMWORD PTR [esp+1136], xmm0 - test dl, 4 - je L6 - movdqa xmm7, xmm0 - psrldq xmm7, 8 - movaps XMMWORD PTR [esp+1136], xmm7 -L6: - test dl, 8 - je L7 - pxor xmm7, xmm7 - movaps XMMWORD PTR [esp+1136], xmm7 -L7: - test dl, 1 - jne L8 - movq xmm0, QWORD PTR [eax+16] - movq xmm1, QWORD PTR [eax] - or edx, 1 - add eax, 32 - movq xmm2, QWORD PTR [eax-8] - sub ecx, 32 - movdqa xmm7, XMMWORD PTR LC5 - movdqa xmm5, XMMWORD PTR LC5 - punpcklqdq xmm1, xmm0 - movq xmm0, QWORD PTR [eax-24] - mov DWORD PTR [esi+116], edx - pand xmm7, xmm1 - punpcklqdq xmm0, xmm2 - movdqa xmm2, xmm1 - psrlq xmm2, 26 - psrlq xmm1, 52 - pand xmm2, XMMWORD PTR LC5 - movaps XMMWORD PTR [esp+1120], xmm2 - movdqa xmm2, xmm0 - psrlq xmm0, 40 - por xmm0, XMMWORD PTR [esp+1136] - psllq xmm2, 12 - por xmm1, xmm2 - movdqa xmm2, xmm0 - pand xmm5, xmm1 - psrlq xmm1, 26 - pand xmm1, XMMWORD PTR LC5 - test dl, 48 - je L10 -L29: - movd xmm3, DWORD PTR [esi+56] - and edx, 16 - movdqu xmm4, XMMWORD PTR [esi+40] - movaps XMMWORD PTR [esp+320], xmm3 - jne L28 - movdqa xmm6, xmm4 - movdqa xmm0, xmm4 - punpckldq xmm6, XMMWORD PTR LC3 - punpckhdq xmm0, XMMWORD PTR LC3 -L12: - pshufd xmm4, xmm6, 80 - movaps XMMWORD PTR [esp+304], xmm4 - pshufd xmm4, xmm6, 250 - movaps XMMWORD PTR [esp+336], xmm4 - pshufd xmm4, xmm0, 80 - pshufd xmm0, xmm0, 250 - movaps XMMWORD PTR [esp+288], xmm4 - movaps XMMWORD PTR [esp+272], xmm0 - jmp L13 - // .p2align 4,,10 - // .p2align 3 -L8: - movdqu xmm1, XMMWORD PTR [esi] - movdqu xmm0, XMMWORD PTR [esi+16] - movdqu xmm7, XMMWORD PTR [esi] - pshufd xmm1, xmm1, 250 - pshufd xmm5, xmm0, 80 - movaps XMMWORD PTR [esp+1120], xmm1 - pshufd xmm1, xmm0, 250 - movdqu xmm0, XMMWORD PTR [esi+32] - pshufd xmm7, xmm7, 80 - pshufd xmm2, xmm0, 80 - test dl, 48 - jne L29 -L10: - movdqu xmm0, XMMWORD PTR [esi+60] - pshufd xmm0, xmm0, 0 - movaps XMMWORD PTR [esp+304], xmm0 - movdqu xmm0, XMMWORD PTR [esi+60] - pshufd xmm0, xmm0, 85 - movaps XMMWORD PTR [esp+336], xmm0 - movdqu xmm0, XMMWORD PTR [esi+60] - pshufd xmm0, xmm0, 170 - movaps XMMWORD PTR [esp+288], xmm0 - movdqu xmm0, XMMWORD PTR [esi+60] - pshufd xmm0, xmm0, 255 - movaps XMMWORD PTR [esp+272], xmm0 - movd xmm0, DWORD PTR [esi+76] - pshufd xmm0, xmm0, 0 - movaps XMMWORD PTR [esp+320], xmm0 -L13: - movdqa xmm0, XMMWORD PTR [esp+336] - movdqa xmm4, XMMWORD PTR [esp+288] - pmuludq xmm0, XMMWORD PTR LC6 - movaps XMMWORD PTR [esp+48], xmm0 - movdqa xmm6, XMMWORD PTR [esp+320] - movdqa xmm0, XMMWORD PTR [esp+272] - pmuludq xmm6, XMMWORD PTR LC6 - movaps XMMWORD PTR [esp+32], xmm6 - pmuludq xmm4, XMMWORD PTR LC6 - pmuludq xmm0, XMMWORD PTR LC6 - cmp ecx, 63 - jbe L14 - movdqu xmm6, XMMWORD PTR [esi+80] - lea edx, [ecx-64] - movaps XMMWORD PTR [esp], xmm0 - movaps XMMWORD PTR [esp+160], xmm0 - and edx, -64 - movdqa xmm0, XMMWORD PTR [esp+1120] - pshufd xmm6, xmm6, 0 - movaps XMMWORD PTR [esp+112], xmm4 - lea edx, [eax+64+edx] - movaps XMMWORD PTR [esp+208], xmm6 - movdqu xmm6, XMMWORD PTR [esi+80] - movaps XMMWORD PTR [esp+16], xmm4 - pshufd xmm3, xmm6, 85 - pshufd xmm6, xmm6, 170 - movaps XMMWORD PTR [esp+256], xmm6 - movdqu xmm6, XMMWORD PTR [esi+80] - movaps XMMWORD PTR [esp+192], xmm3 - pshufd xmm6, xmm6, 255 - movaps XMMWORD PTR [esp+240], xmm6 - movd xmm6, DWORD PTR [esi+96] - pshufd xmm6, xmm6, 0 - movaps XMMWORD PTR [esp+224], xmm6 - movdqa xmm6, XMMWORD PTR LC5 - movaps XMMWORD PTR [esp+1152], xmm6 - movdqa xmm6, XMMWORD PTR [esp+32] - movaps XMMWORD PTR [esp+176], xmm6 - movdqa xmm6, XMMWORD PTR [esp+240] - pmuludq xmm6, XMMWORD PTR LC6 - movaps XMMWORD PTR [esp+144], xmm6 - movdqa xmm6, XMMWORD PTR [esp+224] - pmuludq xmm6, XMMWORD PTR LC6 - movaps XMMWORD PTR [esp+128], xmm6 - movdqa xmm6, XMMWORD PTR [esp+48] - movaps XMMWORD PTR [esp+96], xmm6 - movdqa xmm6, xmm3 - pmuludq xmm6, XMMWORD PTR LC6 - movaps XMMWORD PTR [esp+80], xmm6 - movdqa xmm6, XMMWORD PTR [esp+256] - pmuludq xmm6, XMMWORD PTR LC6 - movaps XMMWORD PTR [esp+64], xmm6 - movdqa xmm6, xmm2 - movdqa xmm2, xmm1 - movdqa xmm1, xmm7 -// .p2align 4,,10 -// .p2align 3 -L15: - movdqa xmm7, XMMWORD PTR [esp+64] - movdqa xmm4, XMMWORD PTR [esp+80] - add eax, 64 - movdqa xmm3, xmm7 - pmuludq xmm4, xmm6 - pmuludq xmm3, xmm2 - movaps XMMWORD PTR [esp+1120], xmm4 - movaps XMMWORD PTR [esp+1104], xmm3 - movdqa xmm3, xmm7 - movdqa xmm7, XMMWORD PTR [esp+144] - pmuludq xmm3, xmm6 - movdqa xmm4, xmm7 - pmuludq xmm4, xmm2 - movaps XMMWORD PTR [esp+1088], xmm3 - movdqa xmm3, xmm7 - pmuludq xmm3, xmm6 - movaps XMMWORD PTR [esp+1072], xmm4 - movdqa xmm4, xmm7 - movdqa xmm7, XMMWORD PTR [esp+128] - pmuludq xmm4, xmm5 - movaps XMMWORD PTR [esp+1056], xmm3 - movdqa xmm3, xmm7 - pmuludq xmm3, xmm5 - movaps XMMWORD PTR [esp+1040], xmm4 - movdqa xmm4, xmm7 - pmuludq xmm4, xmm6 - movaps XMMWORD PTR [esp+992], xmm3 - movdqa xmm3, XMMWORD PTR [esp+208] - movaps XMMWORD PTR [esp+1024], xmm4 - movdqa xmm4, xmm7 - pmuludq xmm6, xmm3 - pmuludq xmm4, xmm0 - movaps XMMWORD PTR [esp+944], xmm6 - movdqa xmm6, xmm3 - movaps XMMWORD PTR [esp+1008], xmm4 - movdqa xmm4, xmm7 - pmuludq xmm6, xmm1 - pmuludq xmm4, xmm2 - movaps XMMWORD PTR [esp+928], xmm6 - movdqa xmm6, xmm3 - movaps XMMWORD PTR [esp+976], xmm4 - movdqa xmm4, xmm3 - pmuludq xmm6, xmm0 - pmuludq xmm4, xmm2 - movaps XMMWORD PTR [esp+912], xmm6 - movdqa xmm6, xmm3 - movaps XMMWORD PTR [esp+960], xmm4 - pmuludq xmm6, xmm5 - movdqa xmm4, XMMWORD PTR [esp+192] - pmuludq xmm2, xmm4 - movdqa xmm3, xmm4 - pmuludq xmm3, xmm5 - movaps XMMWORD PTR [esp+896], xmm6 - movdqa xmm6, XMMWORD PTR [esp+256] - movaps XMMWORD PTR [esp+864], xmm2 - movdqa xmm2, xmm4 - pmuludq xmm5, xmm6 - pmuludq xmm2, xmm1 - movaps XMMWORD PTR [esp+880], xmm3 - movaps XMMWORD PTR [esp+800], xmm5 - movdqa xmm5, xmm6 - movaps XMMWORD PTR [esp+848], xmm2 - movdqa xmm2, xmm4 - pmuludq xmm5, xmm1 - pmuludq xmm2, xmm0 - movaps XMMWORD PTR [esp+784], xmm5 - movaps XMMWORD PTR [esp+832], xmm2 - movdqa xmm2, xmm6 - pmuludq xmm2, xmm0 - movaps XMMWORD PTR [esp+816], xmm2 - movdqa xmm2, XMMWORD PTR [esp+240] - movdqa xmm5, xmm2 - pmuludq xmm0, xmm2 - pmuludq xmm5, xmm1 - movaps XMMWORD PTR [esp+752], xmm0 - movaps XMMWORD PTR [esp+768], xmm5 - pmuludq xmm1, XMMWORD PTR [esp+224] - movq xmm0, QWORD PTR [eax-64] - movq xmm4, QWORD PTR [eax-56] - movaps XMMWORD PTR [esp+736], xmm1 - movq xmm1, QWORD PTR [eax-48] - movdqa xmm6, XMMWORD PTR [esp+96] - movdqa xmm7, XMMWORD PTR [esp+112] - punpcklqdq xmm0, xmm1 - movq xmm1, QWORD PTR [eax-40] - punpcklqdq xmm4, xmm1 - movdqa xmm1, xmm0 - movdqa xmm2, xmm4 - psrlq xmm1, 26 - movdqa xmm3, xmm4 - psrlq xmm4, 40 - psrlq xmm3, 14 - por xmm4, XMMWORD PTR [esp+1136] - pand xmm3, XMMWORD PTR [esp+1152] - psllq xmm2, 12 - pmuludq xmm6, xmm4 - movdqa xmm5, xmm2 - movdqa xmm2, xmm0 - psrlq xmm2, 52 - por xmm2, xmm5 - pand xmm2, XMMWORD PTR [esp+1152] - movaps XMMWORD PTR [esp+720], xmm6 - movdqa xmm6, xmm7 - pmuludq xmm6, xmm3 - movaps XMMWORD PTR [esp+704], xmm6 - movdqa xmm6, xmm7 - movdqa xmm7, XMMWORD PTR [esp+160] - pmuludq xmm6, xmm4 - movdqa xmm5, xmm7 - pmuludq xmm5, xmm2 - movaps XMMWORD PTR [esp+688], xmm6 - movdqa xmm6, xmm7 - pmuludq xmm6, xmm3 - movaps XMMWORD PTR [esp+640], xmm5 - movaps XMMWORD PTR [esp+672], xmm6 - movdqa xmm6, xmm7 - movdqa xmm7, XMMWORD PTR [esp+1152] - pmuludq xmm6, xmm4 - pand xmm1, xmm7 - pand xmm0, xmm7 - movaps XMMWORD PTR [esp+656], xmm6 - movdqa xmm6, XMMWORD PTR [esp+176] - movdqa xmm5, xmm6 - pmuludq xmm5, xmm4 - movaps XMMWORD PTR [esp+624], xmm5 - movdqa xmm5, xmm6 - pmuludq xmm5, xmm1 - movaps XMMWORD PTR [esp+608], xmm5 - movdqa xmm5, xmm6 - pmuludq xmm5, xmm2 - movaps XMMWORD PTR [esp+592], xmm5 - movdqa xmm5, xmm6 - pmuludq xmm5, xmm3 - movaps XMMWORD PTR [esp+576], xmm5 - movdqa xmm5, XMMWORD PTR [esp+304] - movdqa xmm6, xmm5 - pmuludq xmm4, xmm5 - pmuludq xmm6, xmm3 - movaps XMMWORD PTR [esp+544], xmm4 - movdqa xmm4, xmm5 - movaps XMMWORD PTR [esp+560], xmm6 - movdqa xmm6, xmm5 - pmuludq xmm4, xmm0 - pmuludq xmm6, xmm1 - movaps XMMWORD PTR [esp+528], xmm6 - movdqa xmm6, xmm5 - pmuludq xmm6, xmm2 - movaps XMMWORD PTR [esp+512], xmm6 - movdqa xmm6, XMMWORD PTR [esp+336] - movdqa xmm5, xmm6 - pmuludq xmm3, xmm6 - pmuludq xmm5, xmm2 - movaps XMMWORD PTR [esp+480], xmm3 - movaps XMMWORD PTR [esp+496], xmm5 - movdqa xmm5, xmm6 - pmuludq xmm5, xmm0 - movaps XMMWORD PTR [esp+464], xmm5 - movdqa xmm5, xmm6 - movdqu xmm7, XMMWORD PTR [eax-16] - paddq xmm4, XMMWORD PTR [esp+608] - pmuludq xmm5, xmm1 - movaps XMMWORD PTR [esp+448], xmm5 - movdqa xmm5, XMMWORD PTR [esp+288] - pmuludq xmm2, xmm5 - movdqa xmm3, xmm5 - pmuludq xmm3, xmm1 - movaps XMMWORD PTR [esp+416], xmm2 - movdqa xmm2, xmm5 - pmuludq xmm2, xmm0 - movaps XMMWORD PTR [esp+432], xmm3 - movdqa xmm3, XMMWORD PTR [esp+272] - pmuludq xmm1, xmm3 - movaps XMMWORD PTR [esp+400], xmm2 - movdqa xmm2, xmm3 - pmuludq xmm2, xmm0 - pmuludq xmm0, XMMWORD PTR [esp+320] - movaps XMMWORD PTR [esp+368], xmm0 - movdqu xmm0, XMMWORD PTR [eax-32] - movaps XMMWORD PTR [esp+384], xmm1 - punpckldq xmm0, xmm7 - movdqa xmm1, xmm0 - movdqu xmm0, XMMWORD PTR [eax-32] - movdqa xmm3, xmm1 - punpckhdq xmm0, xmm7 - movdqa xmm5, xmm2 - pxor xmm2, xmm2 - movdqa xmm7, xmm0 - punpckldq xmm3, xmm2 - punpckhdq xmm1, xmm2 - punpckldq xmm7, xmm2 - psllq xmm1, 6 - movdqa xmm6, xmm3 - paddq xmm5, XMMWORD PTR [esp+432] - movdqa xmm3, xmm2 - movdqa xmm2, xmm7 - psllq xmm2, 12 - punpckhdq xmm0, xmm3 - movaps XMMWORD PTR [esp+352], xmm2 - psllq xmm0, 18 - movdqa xmm2, XMMWORD PTR [esp+1008] - paddq xmm2, XMMWORD PTR [esp+1040] - movdqa xmm3, xmm2 - movdqa xmm2, XMMWORD PTR [esp+1120] - paddq xmm2, XMMWORD PTR [esp+1104] - paddq xmm2, xmm3 - movdqa xmm3, XMMWORD PTR [esp+720] - paddq xmm3, XMMWORD PTR [esp+928] - paddq xmm3, xmm2 - movdqa xmm2, XMMWORD PTR [esp+640] - paddq xmm2, XMMWORD PTR [esp+704] - paddq xmm2, xmm3 - paddq xmm2, xmm4 - movdqa xmm4, XMMWORD PTR [esp+816] - paddq xmm4, XMMWORD PTR [esp+880] - paddq xmm2, xmm6 - movdqa xmm3, xmm4 - movdqa xmm7, xmm2 - movdqa xmm4, XMMWORD PTR [esp+1024] - paddq xmm4, XMMWORD PTR [esp+960] - psrlq xmm7, 26 - paddq xmm4, xmm3 - movdqa xmm3, XMMWORD PTR [esp+624] - paddq xmm3, XMMWORD PTR [esp+768] - paddq xmm4, xmm3 - movdqa xmm3, XMMWORD PTR [esp+496] - paddq xmm3, XMMWORD PTR [esp+560] - paddq xmm4, xmm3 - paddq xmm4, xmm5 - movdqa xmm5, xmm7 - paddq xmm4, xmm0 - paddq xmm1, xmm5 - movdqa xmm0, XMMWORD PTR [esp+912] - paddq xmm0, XMMWORD PTR [esp+992] - movdqa xmm7, xmm4 - movdqa xmm6, XMMWORD PTR [esp+1088] - paddq xmm6, XMMWORD PTR [esp+1072] - psrlq xmm7, 26 - movdqa xmm3, xmm7 - paddq xmm3, XMMWORD PTR [esp+1136] - paddq xmm6, xmm0 - movdqa xmm0, XMMWORD PTR [esp+688] - paddq xmm0, XMMWORD PTR [esp+848] - paddq xmm0, xmm6 - movdqa xmm6, XMMWORD PTR [esp+592] - paddq xmm6, XMMWORD PTR [esp+672] - paddq xmm0, xmm6 - movdqa xmm6, XMMWORD PTR [esp+464] - paddq xmm6, XMMWORD PTR [esp+528] - paddq xmm0, xmm6 - movdqa xmm6, XMMWORD PTR [esp+944] - paddq xmm6, XMMWORD PTR [esp+864] - paddq xmm0, xmm1 - movdqa xmm1, XMMWORD PTR [esp+752] - paddq xmm1, XMMWORD PTR [esp+800] - paddq xmm6, xmm1 - movdqa xmm1, XMMWORD PTR [esp+544] - paddq xmm1, XMMWORD PTR [esp+736] - paddq xmm1, xmm6 - movdqa xmm6, XMMWORD PTR [esp+416] - paddq xmm6, XMMWORD PTR [esp+480] - paddq xmm1, xmm6 - movdqa xmm6, XMMWORD PTR [esp+384] - paddq xmm6, XMMWORD PTR [esp+368] - movdqa xmm5, xmm6 - paddq xmm5, xmm1 - movdqa xmm7, xmm5 - movdqa xmm5, XMMWORD PTR [esp+832] - paddq xmm5, XMMWORD PTR [esp+896] - paddq xmm7, xmm3 - movdqa xmm3, XMMWORD PTR [esp+1056] - paddq xmm3, XMMWORD PTR [esp+976] - movdqa xmm6, xmm7 - movdqa xmm7, xmm0 - paddq xmm3, xmm5 - psrlq xmm7, 26 - movdqa xmm1, xmm6 - movdqa xmm5, XMMWORD PTR [esp+656] - paddq xmm5, XMMWORD PTR [esp+784] - psrlq xmm1, 26 - pmuludq xmm1, XMMWORD PTR LC6 - paddq xmm5, xmm3 - movdqa xmm3, XMMWORD PTR [esp+512] - paddq xmm3, XMMWORD PTR [esp+576] - paddq xmm3, xmm5 - movdqa xmm5, XMMWORD PTR [esp+448] - paddq xmm5, XMMWORD PTR [esp+400] - paddq xmm3, xmm5 - movdqa xmm5, XMMWORD PTR [esp+352] - paddq xmm5, xmm7 - movdqa xmm7, XMMWORD PTR [esp+1152] - paddq xmm5, xmm3 - pand xmm2, xmm7 - pand xmm4, xmm7 - pand xmm0, xmm7 - paddq xmm1, xmm2 - movdqa xmm2, xmm5 - pand xmm6, xmm7 - movdqa xmm3, xmm1 - psrlq xmm2, 26 - pand xmm5, xmm7 - psrlq xmm3, 26 - paddq xmm2, xmm4 - pand xmm1, xmm7 - paddq xmm0, xmm3 - movdqa xmm3, xmm2 - pand xmm2, xmm7 - psrlq xmm3, 26 - paddq xmm6, xmm3 - cmp eax, edx - jne L15 - movaps XMMWORD PTR [esp+1120], xmm0 - movdqa xmm4, XMMWORD PTR [esp+16] - movdqa xmm7, xmm1 - and ecx, 63 - movdqa xmm0, XMMWORD PTR [esp] - movdqa xmm1, xmm2 - movdqa xmm2, xmm6 -L14: - cmp ecx, 31 - jbe L16 - movdqa xmm6, XMMWORD PTR [esp+48] - movdqa xmm3, XMMWORD PTR [esp+32] - pmuludq xmm6, xmm2 - movaps XMMWORD PTR [esp+1152], xmm6 - movdqa xmm6, xmm1 - pmuludq xmm6, xmm4 - pmuludq xmm4, xmm2 - movaps XMMWORD PTR [esp+1104], xmm6 - movdqa xmm6, xmm1 - pmuludq xmm6, xmm0 - movaps XMMWORD PTR [esp+1088], xmm4 - movaps XMMWORD PTR [esp+1072], xmm6 - movdqa xmm6, xmm2 - pmuludq xmm6, xmm0 - pmuludq xmm0, xmm5 - movaps XMMWORD PTR [esp+1056], xmm6 - movdqa xmm6, xmm5 - movdqa xmm4, xmm0 - movdqa xmm0, xmm2 - pmuludq xmm6, xmm3 - pmuludq xmm0, xmm3 - movaps XMMWORD PTR [esp+1024], xmm6 - movdqa xmm6, xmm3 - pmuludq xmm6, xmm1 - movaps XMMWORD PTR [esp+1040], xmm0 - movdqa xmm0, XMMWORD PTR [esp+1120] - pmuludq xmm0, xmm3 - movaps XMMWORD PTR [esp+1008], xmm6 - movdqa xmm6, XMMWORD PTR [esp+304] - pmuludq xmm2, xmm6 - movdqa xmm3, xmm6 - paddq xmm0, xmm4 - movdqa xmm4, XMMWORD PTR [esp+1152] - paddq xmm4, XMMWORD PTR [esp+1104] - pmuludq xmm3, xmm1 - paddq xmm0, xmm4 - movdqa xmm4, XMMWORD PTR [esp+1120] - movaps XMMWORD PTR [esp+976], xmm2 - movdqa xmm2, xmm6 - pmuludq xmm2, xmm7 - movaps XMMWORD PTR [esp+992], xmm3 - movdqa xmm3, xmm6 - pmuludq xmm3, XMMWORD PTR [esp+1120] - movdqa xmm6, xmm3 - movdqa xmm3, XMMWORD PTR [esp+304] - paddq xmm6, XMMWORD PTR [esp+1024] - pmuludq xmm3, xmm5 - paddq xmm0, xmm2 - movdqa xmm2, XMMWORD PTR [esp+336] - movaps XMMWORD PTR [esp+1104], xmm0 - movdqa xmm0, xmm2 - pmuludq xmm1, xmm2 - pmuludq xmm0, xmm7 - movaps XMMWORD PTR [esp+960], xmm3 - movdqa xmm3, XMMWORD PTR [esp+336] - pmuludq xmm3, xmm5 - movaps XMMWORD PTR [esp+928], xmm1 - movdqa xmm1, xmm0 - movdqa xmm0, xmm2 - pmuludq xmm0, xmm4 - movdqa xmm4, XMMWORD PTR [esp+1088] - paddq xmm4, XMMWORD PTR [esp+1072] - movaps XMMWORD PTR [esp+944], xmm3 - movdqa xmm3, XMMWORD PTR [esp+288] - paddq xmm6, xmm4 - movdqa xmm4, xmm6 - pmuludq xmm5, xmm3 - movdqa xmm2, xmm3 - movdqa xmm6, XMMWORD PTR [esp+272] - pmuludq xmm2, XMMWORD PTR [esp+1120] - paddq xmm4, xmm1 - movaps XMMWORD PTR [esp+1152], xmm4 - movdqa xmm4, xmm3 - movdqa xmm1, xmm6 - movdqa xmm3, XMMWORD PTR [esp+1120] - pmuludq xmm4, xmm7 - pmuludq xmm1, xmm7 - paddq xmm0, XMMWORD PTR [esp+960] - paddq xmm2, XMMWORD PTR [esp+944] - pmuludq xmm3, xmm6 - movdqa xmm6, XMMWORD PTR [esp+320] - pmuludq xmm6, xmm7 - movdqa xmm7, XMMWORD PTR [esp+1056] - paddq xmm7, XMMWORD PTR [esp+1008] - paddq xmm0, xmm7 - paddq xmm0, xmm4 - paddq xmm3, xmm5 - movdqa xmm4, XMMWORD PTR [esp+1040] - paddq xmm4, XMMWORD PTR [esp+992] - paddq xmm2, xmm4 - paddq xmm2, xmm1 - movdqa xmm1, XMMWORD PTR [esp+976] - paddq xmm1, XMMWORD PTR [esp+928] - paddq xmm3, xmm1 - paddq xmm3, xmm6 - test eax, eax - je L18 - movdqu xmm5, XMMWORD PTR [eax+16] - movdqu xmm1, XMMWORD PTR [eax] - paddq xmm3, XMMWORD PTR [esp+1136] - punpckldq xmm1, xmm5 - movdqa xmm4, xmm1 - movdqu xmm1, XMMWORD PTR [eax] - movdqa xmm6, xmm4 - punpckhdq xmm1, xmm5 - pxor xmm5, xmm5 - punpckldq xmm6, xmm5 - punpckhdq xmm4, xmm5 - movdqa xmm7, xmm6 - psllq xmm4, 6 - movdqa xmm6, xmm1 - punpckldq xmm6, xmm5 - punpckhdq xmm1, xmm5 - movdqa xmm5, XMMWORD PTR [esp+1104] - psllq xmm6, 12 - psllq xmm1, 18 - paddq xmm5, xmm7 - paddq xmm0, xmm6 - movaps XMMWORD PTR [esp+1104], xmm5 - paddq xmm2, xmm1 - movdqa xmm5, XMMWORD PTR [esp+1152] - paddq xmm5, xmm4 - movaps XMMWORD PTR [esp+1152], xmm5 -L18: - movdqa xmm7, XMMWORD PTR [esp+1104] - movdqa xmm4, xmm2 - pand xmm2, XMMWORD PTR LC5 - psrlq xmm4, 26 - movdqa xmm1, xmm7 - paddq xmm4, xmm3 - pand xmm7, XMMWORD PTR LC5 - psrlq xmm1, 26 - movdqa xmm3, xmm4 - paddq xmm1, XMMWORD PTR [esp+1152] - pand xmm4, XMMWORD PTR LC5 - psrlq xmm3, 26 - pmuludq xmm3, XMMWORD PTR LC6 - paddq xmm7, xmm3 - movdqa xmm5, xmm1 - movdqa xmm3, xmm7 - pand xmm1, XMMWORD PTR LC5 - psrlq xmm5, 26 - psrlq xmm3, 26 - pand xmm7, XMMWORD PTR LC5 - paddq xmm0, xmm5 - paddq xmm3, xmm1 - movdqa xmm1, XMMWORD PTR LC5 - movdqa xmm6, xmm0 - movaps XMMWORD PTR [esp+1120], xmm3 - pand xmm0, XMMWORD PTR LC5 - psrlq xmm6, 26 - paddq xmm2, xmm6 - movdqa xmm5, xmm0 - movdqa xmm0, xmm2 - pand xmm1, xmm2 - psrlq xmm0, 26 - paddq xmm4, xmm0 - movdqa xmm2, xmm4 -L16: - test eax, eax - je L19 - pshufd xmm3, xmm5, 8 - pshufd xmm4, xmm7, 8 - pshufd xmm5, xmm1, 8 - pshufd xmm6, XMMWORD PTR [esp+1120], 8 - pshufd xmm0, xmm2, 8 - movdqa xmm1, xmm3 - movdqa xmm2, xmm4 - punpcklqdq xmm2, xmm6 - punpcklqdq xmm1, xmm5 - movq QWORD PTR [esi+32], xmm0 - movups XMMWORD PTR [esi], xmm2 - movups XMMWORD PTR [esi+16], xmm1 - lea esp, [ebp-12] - pop ebx - pop esi - pop edi - pop ebp - ret -// .p2align 4,,10 -// .p2align 3 -L28: - movdqu xmm6, XMMWORD PTR [esi+60] - movdqu xmm0, XMMWORD PTR [esi+60] - punpckldq xmm6, xmm4 - punpckhdq xmm0, xmm4 - movd xmm4, DWORD PTR [esi+76] - punpcklqdq xmm4, xmm3 - movaps XMMWORD PTR [esp+320], xmm4 - jmp L12 -// .p2align 4,,10 -// .p2align 3 -L19: - movdqa xmm3, xmm1 - movdqa xmm6, xmm7 - movdqa xmm0, xmm5 - movdqa xmm4, XMMWORD PTR [esp+1120] - psrldq xmm3, 8 - psrldq xmm6, 8 - psrldq xmm4, 8 - psrldq xmm0, 8 - paddq xmm7, xmm6 - movaps XMMWORD PTR [esp+1136], xmm3 - movdqa xmm3, XMMWORD PTR [esp+1120] - movd edx, xmm7 - movaps XMMWORD PTR [esp+1152], xmm0 - paddq xmm5, XMMWORD PTR [esp+1152] - paddq xmm1, XMMWORD PTR [esp+1136] - mov edi, edx - shr edx, 26 - movdqa xmm0, xmm2 - paddq xmm3, xmm4 - psrldq xmm0, 8 - and edi, 67108863 - movd eax, xmm3 - paddq xmm2, xmm0 - add eax, edx - movd edx, xmm5 - mov ebx, eax - shr eax, 26 - add eax, edx - movd edx, xmm1 - and ebx, 67108863 - mov ecx, eax - shr eax, 26 - add eax, edx - and ecx, 67108863 - movd edx, xmm2 - mov DWORD PTR [esp+1152], ecx - mov ecx, eax - shr eax, 26 - add eax, edx - and ecx, 67108863 - mov edx, eax - shr eax, 26 - lea eax, [eax+eax*4] - and edx, 67108863 - add eax, edi - mov edi, eax - shr eax, 26 - add eax, ebx - and edi, 67108863 - mov ebx, eax - shr eax, 26 - and ebx, 67108863 - mov DWORD PTR [esp+1120], ebx - mov ebx, DWORD PTR [esp+1152] - add eax, ebx - mov ebx, eax - shr eax, 26 - add eax, ecx - and ebx, 67108863 - mov ecx, eax - shr eax, 26 - mov DWORD PTR [esp+1152], ebx - mov ebx, DWORD PTR [esp+1120] - add eax, edx - and ecx, 67108863 - mov DWORD PTR [esp+1136], ecx - mov edx, eax - mov ecx, eax - and edx, 67108863 - shr ecx, 26 - mov DWORD PTR [esp+1104], edx - lea edx, [ecx+ecx*4] - add edx, edi - mov ecx, edx - shr edx, 26 - and ecx, 67108863 - add edx, ebx - or eax, -67108864 - lea ebx, [ecx+5] - mov DWORD PTR [esp+1088], ebx - shr ebx, 26 - lea edi, [ebx+edx] - mov DWORD PTR [esp+1072], edi - shr edi, 26 - mov ebx, edi - mov edi, DWORD PTR [esp+1152] - add ebx, edi - mov edi, DWORD PTR [esp+1136] - mov DWORD PTR [esp+1056], ebx - shr ebx, 26 - add ebx, edi - mov DWORD PTR [esp+1040], ebx - shr ebx, 26 - add eax, ebx - mov edi, eax - shr edi, 31 - mov ebx, edi - mov edi, eax - sar edi, 31 - sub ebx, 1 - and edx, edi - and ecx, edi - and eax, ebx - mov DWORD PTR [esp+1120], edx - mov edx, DWORD PTR [esp+1152] - and edx, edi - mov DWORD PTR [esp+1152], edx - mov edx, DWORD PTR [esp+1136] - and edx, edi - mov DWORD PTR [esp+1136], edx - mov edx, DWORD PTR [esp+1104] - and edx, edi - mov edi, DWORD PTR [esp+1088] - mov DWORD PTR [esp+1104], edx - and edi, ebx - and edi, 67108863 - or edi, ecx - mov ecx, DWORD PTR [esp+1072] - mov DWORD PTR [esi], edi - mov edi, DWORD PTR [esp+1120] - and ecx, ebx - and ecx, 67108863 - or ecx, edi - mov DWORD PTR [esi+4], ecx - mov ecx, DWORD PTR [esp+1056] - mov edi, DWORD PTR [esp+1152] - and ecx, ebx - mov edx, ecx - mov ecx, DWORD PTR [esp+1040] - and edx, 67108863 - or edx, edi - and ecx, ebx - mov edi, DWORD PTR [esp+1136] - mov DWORD PTR [esi+8], edx - mov edx, ecx - and edx, 67108863 - or edx, edi - mov DWORD PTR [esi+12], edx - mov edx, DWORD PTR [esp+1104] - or eax, edx - mov DWORD PTR [esi+16], eax - lea esp, [ebp-12] - pop ebx - pop esi - pop edi - pop ebp -end; - -procedure _Poly1305_finish_SSE2(st : TPoly1305.PPoly1305_state_internal_t; m : PByte; leftover : integer; var mac : TBlock16Byte) cdecl; -label L63, L33, L34, L35, L36, L37, L38, L42, L43, L64, L65, L99; -asm - push ebp - mov ebp, esp - push edi - push esi - push ebx - and esp, -16 - sub esp, 64 - mov ebx, DWORD PTR [ebp+8] - mov edx, DWORD PTR [ebp+16] - mov eax, DWORD PTR [ebx+116] - mov DWORD PTR [esp+28], eax - test edx, edx - je L63 - mov edi, DWORD PTR [ebp+12] - pxor xmm0, xmm0 - lea esi, [esp+32] - movaps XMMWORD PTR [esp+32], xmm0 - mov ecx, esi - movaps XMMWORD PTR [esp+48], xmm0 - sub edi, esi - test BYTE PTR [ebp+16], 16 - je L34 - mov eax, DWORD PTR [ebp+12] - lea ecx, [esp+48] - movdqu xmm4, XMMWORD PTR [eax] - movaps XMMWORD PTR [esp+32], xmm4 -L34: - test BYTE PTR [ebp+16], 8 - je L35 - mov eax, DWORD PTR [ecx+edi] - mov edx, DWORD PTR [ecx+4+edi] - add ecx, 8 - mov DWORD PTR [ecx-8], eax - mov DWORD PTR [ecx-4], edx -L35: - test BYTE PTR [ebp+16], 4 - je L36 - mov eax, DWORD PTR [ecx+edi] - add ecx, 4 - mov DWORD PTR [ecx-4], eax -L36: - test BYTE PTR [ebp+16], 2 - je L37 - movzx eax, WORD PTR [ecx+edi] - add ecx, 2 - mov WORD PTR [ecx-2], ax -L37: - test BYTE PTR [ebp+16], 1 - je L38 - movzx edx, BYTE PTR [ecx+edi] - mov BYTE PTR [ecx], dl -L38: - cmp DWORD PTR [ebp+16], 16 - je L65 - mov eax, DWORD PTR [ebp+16] - mov edx, DWORD PTR [esp+28] - mov BYTE PTR [esp+32+eax], 1 - cmp eax, 15 - jbe L42 - or edx, 4 - mov DWORD PTR [ebx+116], edx - mov DWORD PTR [esp+8], 32 - mov DWORD PTR [esp+4], esi - mov DWORD PTR [esp], ebx - call _Poly1305_Blocks_sse2 - mov eax, DWORD PTR [ebx+116] - mov DWORD PTR [esp+28], eax -L63: - test al, 1 - je L33 - mov edx, DWORD PTR [esp+28] - or edx, 16 -L43: - mov DWORD PTR [ebx+116], edx - mov DWORD PTR [esp+8], 32 - mov DWORD PTR [esp+4], 0 - mov DWORD PTR [esp], ebx - call _Poly1305_Blocks_sse2 -L33: - mov esi, DWORD PTR [ebx+4] - mov edx, DWORD PTR [ebx+8] - mov ecx, DWORD PTR [ebx+12] - mov eax, esi - mov edi, esi - mov esi, edx - shr edi, 6 - sal esi, 20 - or esi, edi - mov edi, edx - mov edx, ecx - shr ecx, 18 - shr edi, 12 - sal edx, 14 - or edx, edi - mov edi, ecx - mov ecx, DWORD PTR [ebx+16] - sal eax, 26 - or eax, DWORD PTR [ebx] - sal ecx, 8 - or ecx, edi - //APP - // # 526 "poly1305-donna-x86-sse2-incremental-source - Kopie.c" 1 - //addl DWORD PTR [ebx+100], eax; -// adcl DWORD PTR [ebx+104], esi; -// adcl DWORD PTR [ebx+108], edx; -// adcl DWORD PTR [ebx+112], ecx; - add DWORD PTR [ebx+100], eax; - adc DWORD PTR [ebx+104], esi; - adc DWORD PTR [ebx+108], edx; - adc DWORD PTR [ebx+112], ecx; - - //# 0 "" 2 - //NO_APP - pxor xmm0, xmm0 - movd xmm3, esi - movd xmm1, edx - movups XMMWORD PTR [ebx], xmm0 - movd xmm2, ecx - movups XMMWORD PTR [ebx+16], xmm0 - punpckldq xmm1, xmm2 - movups XMMWORD PTR [ebx+32], xmm0 - movups XMMWORD PTR [ebx+48], xmm0 - movups XMMWORD PTR [ebx+64], xmm0 - movups XMMWORD PTR [ebx+80], xmm0 - movups XMMWORD PTR [ebx+96], xmm0 - movups XMMWORD PTR [ebx+112], xmm0 - movd xmm0, eax - mov eax, DWORD PTR [ebp+20] - punpckldq xmm0, xmm3 - punpcklqdq xmm0, xmm1 - movups XMMWORD PTR [eax], xmm0 - lea esp, [ebp-12] - pop ebx - pop esi - pop edi - pop ebp - - jmp L99; - -// now that is some kind of epiolog -L65: - mov edx, DWORD PTR [esp+28] - or edx, 4 -L64: - mov DWORD PTR [ebx+116], edx - mov DWORD PTR [esp+8], 32 - mov DWORD PTR [esp+4], esi - mov DWORD PTR [esp], ebx - call _poly1305_blocks_sse2 - mov edx, DWORD PTR [ebx+116] - test dl, 1 - je L33 - or edx, 32 - jmp L43 -// .p2align 4,,10 -// .p2align 3 -L42: - or edx, 8 - jmp L64 - -// return: -L99: -end; - -procedure TPoly1305.Poly1305Emit_SSE(var mac: TBlock16Byte); -begin - FillChar(mac, sizeof(mac), 0); - //nonce is not used - it is already stored - _Poly1305_finish_SSE2( fPPoly1305State, nil, 0, mac) -end; - - -procedure TPoly1305.Poly1305Finalize(var mac: TBlock16Byte); -begin - if fNum > 0 then - begin - fData[fNum] := 1; - inc(fNum); - while fNum < Length(fData) do - begin - fData[fNum] := 0; - inc(fNum); - end; - - Poly1305Blocks(@fData[0], POLY1305_BLOCK_SIZE, 0); - fNum := 0; - end; - - Poly1305Emit(mac, FNonce); -end; - -procedure TPoly1305.Poly1305Init_SSE(initVec: PByte); -begin - FillChar(fPoly1305State[0], Length(fPoly1305State), 0); - - _poly1305_init_ext_sse2( fPPoly1305State, initVec, 0); -end; - -procedure TPoly1305.Poly1305Blocks_SSE(pData: PByteArray; size: integer); -begin - _Poly1305_Blocks_sse2( fPPoly1305State, PByte(pData), size ); -end; - -procedure TPoly1305.U32ToU8(pData: PByteArray; value: UInt32); -begin - pData^[0] := Byte(value); - pData^[1] := Byte(value shr 8); - pData^[2] := Byte(value shr 16); - pData^[3] := Byte(value shr 24); -end; - -function TPoly1305.U8ToU32(pData: PByteArray): UInt32; -begin - Result := (UInt32(pData^[0]) and $ff) or - ((UInt32(pData^[1]) and $ff) shl 8) or - ((UInt32(pData^[2]) and $ff) shl 16) or - ((UInt32(pData^[3]) and $ff) shl 24); -end; - procedure TPoly1305.UpdatePoly(pData: PByteArray; size: integer); var num : integer; rem : integer; @@ -2011,7 +573,9 @@ procedure TPoly1305.UpdatePoly(pData: PByteArray; size: integer); fNum := rem; end; -initialization - TPoly1305.UseSSE := False; +procedure TPoly1305.UpdateWithEncDecBuf(buf: PUInt8Array; Size: Integer); +begin + UpdatePoly(PByteArray(buf), size); +end; end. diff --git a/Source/DECCiphers.pas b/Source/DECCiphers.pas index 6da6e74..cd0172d 100644 --- a/Source/DECCiphers.pas +++ b/Source/DECCiphers.pas @@ -6941,6 +6941,7 @@ procedure TCipher_ChaCha20.DoDecode(Source, Dest: Pointer; Size: Integer); begin // should be the same DoEncode( Source, Dest, size ); + FState := csDecode; end; procedure TCipher_ChaCha20.DoEncode(Source, Dest: Pointer; Size: Integer); @@ -6965,6 +6966,7 @@ procedure TCipher_ChaCha20.DoEncode(Source, Dest: Pointer; Size: Integer); end; end; begin + FState := csEncode; pChaCha := PByte(fOutChaChaMtx); inc(pChaCha, fChaChaIdx); @@ -7011,6 +7013,7 @@ procedure TCipher_ChaCha20.OnAfterInitVectorInitialization( // special care in case of poly1305: if FMode = cmPoly1305 then begin + FBufferSize := 0; // according to RFC7539 (chapter 2.6) we create the R and S (the IV vector) value as: // block counter is 0 key and nonce (96 or 64 bits) // build iv by applying the key/nonce pair on the first "block" which results diff --git a/Unit Tests/DECDUnitTestSuite.dpr b/Unit Tests/DECDUnitTestSuite.dpr index 0c9b25d..201e9f3 100644 --- a/Unit Tests/DECDUnitTestSuite.dpr +++ b/Unit Tests/DECDUnitTestSuite.dpr @@ -70,7 +70,8 @@ uses DECUtilRawByteStringHelper in '..\Source\DECUtilRawByteStringHelper.pas', DECZIPHelper in '..\Source\DECZIPHelper.pas', DECCipherModesGCM in '..\Source\DECCipherModesGCM.pas', - TESTDECChaChaPoly1305 in 'Tests\TESTDECChaChaPoly1305.pas'; + TESTDECChaChaPoly1305 in 'Tests\TESTDECChaChaPoly1305.pas', + poly1305dll in '..\Source\poly1305dll.pas'; {$R *.RES} diff --git a/Unit Tests/DECDUnitTestSuite.dproj b/Unit Tests/DECDUnitTestSuite.dproj index 3611840..3520132 100644 --- a/Unit Tests/DECDUnitTestSuite.dproj +++ b/Unit Tests/DECDUnitTestSuite.dproj @@ -112,6 +112,8 @@ .\..\Compiled\DCU_IDE$(ProductVersion)_$(Platform)_$(Config) 3 /L /LL65535 + ASSEMBLER;$(DCC_Define) + none true @@ -193,6 +195,7 @@ + Base @@ -246,10 +249,8 @@ DECDUnitTestSuite.dpr - Embarcadero C++Builder-Package für Office 2000-Server - Embarcadero C++Builder-Package für Office XP-Server - Microsoft Office 2000 Beispiele für gekapselte Komponenten für Automatisierungsserver - Microsoft Office XP Beispiele für gekapselte Komponenten für Automation Server + Microsoft Office 2000 Beispiele für gekapselte Komponenten für Automatisierungsserver + Microsoft Office XP Beispiele für gekapselte Komponenten für Automation Server diff --git a/Unit Tests/Tests/TestDECChaChaPoly1305.pas b/Unit Tests/Tests/TestDECChaChaPoly1305.pas index 946f7a2..f0cd388 100644 --- a/Unit Tests/Tests/TestDECChaChaPoly1305.pas +++ b/Unit Tests/Tests/TestDECChaChaPoly1305.pas @@ -25,7 +25,7 @@ TestChaCha20Poly1305 = class(TTestCase) end; implementation -uses DECCipherModesPoly1305, System.Diagnostics; +uses DECCipherModesPoly1305, System.Diagnostics, poly1305dll; //// ########################################### @@ -163,6 +163,7 @@ procedure EncodeBuf( var dest : TBytes ); // test decode for cpuMode in [cmPas, cmSSE, cmAVX] do begin + TCipher_ChaCha20.CpuMode := cpuMode; chaCha := TCipher_ChaCha20.Create; try chaCha.Mode := cmECBx; @@ -177,6 +178,9 @@ procedure EncodeBuf( var dest : TBytes ); end; end; +type + THackPly1305 = class(TPoly1305); + procedure TestChaCha20Poly1305.TestPoly1305; const// cKey : Array of Byte = [$85, $d6, $be, $78, $57, $55, $6d, $33, $7f, $44, $52, $fe, $42, $d5, $06, $a8, $01, $0, // $3, $80, $8a, $fb, $0d, $b2, $fd, $4a, $bf, $f6, $af, $41, $49, $f5, $1b ]; @@ -229,9 +233,9 @@ procedure InvData( data : PByte; len : integer ); poly := TPoly1305.Create; try - poly.InitInternal(iv); - poly.UpdatePoly(@msg[0], Length(msg)); - poly.Finalize; + THackPly1305(poly).InitInternal(iv); + THackPly1305(poly).UpdatePoly(@msg[0], Length(msg)); + THackPly1305(poly).Finalize; calcTag := poly.CalculatedAuthenticationTag; finally @@ -252,9 +256,9 @@ procedure InvData( data : PByte; len : integer ); poly := TPoly1305.Create; try - poly.InitInternal(iv); - poly.UpdatePoly(@msg[0], Length(msg)); - poly.Finalize; + THackPly1305(poly).InitInternal(iv); + THackPly1305(poly).UpdatePoly(@msg[0], Length(msg)); + THackPly1305(poly).Finalize; calcTag := poly.CalculatedAuthenticationTag; finally @@ -265,6 +269,7 @@ procedure InvData( data : PByte; len : integer ); Check( CompareMem(@cTag[0], @calcTag[0], Length(calcTag)), 'Polynom calculated tag does not match'); end; + procedure TestChaCha20Poly1305.TestChaCha20_Poly1305_AEAD; const cMsg : AnsiString = 'Ladies and Gentlemen of the class of ''99: If I could offer you only one tip for the future, sunscreen would be it.'; cAAD : TBytes = [$50, $51, $52, $53, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7]; @@ -303,6 +308,7 @@ procedure TestChaCha20Poly1305.TestChaCha20_Poly1305_AEAD; chaCha := TCipher_ChaCha20.Create; try + chaCha.Mode := cmPoly1305; chaCha.DataToAuthenticate := cAAD; chaCha.Init(cKey, cNonce); encr := chaCha.EncodeBytes(msg); @@ -324,6 +330,7 @@ procedure TestChaCha20Poly1305.TestChaCha20_Poly1305_AEAD; // #### Test decode chaCha := TCipher_ChaCha20.Create; try + chaCha.Mode := cmPoly1305; chaCha.ExpectedAuthenticationResult := encrTag; chaCha.DataToAuthenticate := cAAD; From 7375a5436d422e8b06b52a2bc008df95e109c76a Mon Sep 17 00:00:00 2001 From: Michael Rabatscher Date: Sat, 12 Jul 2025 07:18:39 +0200 Subject: [PATCH 10/17] Working GCM and Poly1305 --- Source/DECAuthenticatedCipherModesBase.pas | 2 + Source/DECCipherFormats.pas | 8 +- Source/DECCipherModes.pas | 4 +- Source/DECCipherModesGCM.pas | 25 +++-- Unit Tests/Tests/TestDECCipherModesGCM.pas | 107 +++++++++++++++++++++ 5 files changed, 132 insertions(+), 14 deletions(-) diff --git a/Source/DECAuthenticatedCipherModesBase.pas b/Source/DECAuthenticatedCipherModesBase.pas index b24c14d..1b39283 100644 --- a/Source/DECAuthenticatedCipherModesBase.pas +++ b/Source/DECAuthenticatedCipherModesBase.pas @@ -294,6 +294,7 @@ procedure TAuthenticatedCipherModesBase.Init(EncryptionMethod : TEncodeDecodeMet Assert(Assigned(EncryptionMethod), 'No encryption method specified'); // Clear calculated authentication value + fAuthMethodInit := False; CalcAuthLength := Length(FCalcAuthenticationTag); if (CalcAuthLength > 0) then begin @@ -302,6 +303,7 @@ procedure TAuthenticatedCipherModesBase.Init(EncryptionMethod : TEncodeDecodeMet FillChar(FCalcAuthenticationTag[0], CalcAuthLength, #0); end; + fEncDecLen := 0; FEncryptionMethod := EncryptionMethod; end; diff --git a/Source/DECCipherFormats.pas b/Source/DECCipherFormats.pas index eb3d5ac..42a7da5 100644 --- a/Source/DECCipherFormats.pas +++ b/Source/DECCipherFormats.pas @@ -730,10 +730,10 @@ function TDECFormattedCipher.EncodeBytes(const Source: TBytes): TBytes; begin SetLength(Result, Length(Source)); if Length(Result) > 0 then - Encode(Source[0], Result[0], Length(Source)) - else - if (FMode = cmGCM) then - EncodeWithAuthObj(nil, nil, 0); + Encode(Source[0], Result[0], Length(Source)); + //else +// if (FMode = cmGCM) then +// EncodeWithAuthObj(nil, nil, 0); end; begin diff --git a/Source/DECCipherModes.pas b/Source/DECCipherModes.pas index 4840ce0..5770152 100644 --- a/Source/DECCipherModes.pas +++ b/Source/DECCipherModes.pas @@ -1108,8 +1108,6 @@ destructor TDECCipherModes.Destroy; procedure TDECCipherModes.Done; begin - inherited; - if Assigned(fAuthObj) then begin fAuthObj.FinalizeAEAD; @@ -1119,6 +1117,8 @@ procedure TDECCipherModes.Done; then raise EDECCipherAuthenticationException.CreateRes(@sInvalidAuthenticationValue); end; + + inherited; end; procedure TDECCipherModes.OnAfterInitVectorInitialization(const OriginalInitVector: TBytes); diff --git a/Source/DECCipherModesGCM.pas b/Source/DECCipherModesGCM.pas index 1eb2473..c87009b 100644 --- a/Source/DECCipherModesGCM.pas +++ b/Source/DECCipherModesGCM.pas @@ -520,13 +520,25 @@ procedure TGCM.Init(EncryptionMethod : TEncodeDecodeMethod; end; procedure TGCM.InitAuth; +var authLen : integer; begin + inherited; + FillChar(fData, sizeof(fData), 0); FDataIdx := 0; FGHash := nullbytes; if Length(FDataToAuthenticate) > 0 then - UpdateWithEncDecBuf(@FDataToAuthenticate[0], Length(FDataToAuthenticate)); + begin + authLen := Length(FDataToAuthenticate); + UpdateWithEncDecBuf(@FDataToAuthenticate[0], authLen); + // this block needs to be padded if the authentication buffer is not a multiple of 16 + authLen := 16 - (authLen mod 16); + if authLen <> 16 then + UpdateWithEncDecBuf(@nullbytes, authLen); + end; + + fIsLastBlock := False; end; procedure TGCM.LocEncodeDecode(Source, Dest: Pointer; Size: Integer); @@ -607,16 +619,13 @@ procedure TGCM.UpdateWithEncDecBuf(buf: PUInt8Array; Size: Integer); end; len_d := size - n; - if (len_d > 0) then + if (len_d >= cGCMBlkSize) then begin div_d := len_d div cGCMBlkSize; - if div_d > 0 then + for i := 0 to div_d - 1 do begin - for i := 0 to div_d - 1 do - begin - FGHash := poly_mult_H(XOR_PointerWithT128(@buf^[n], FGHash )); - inc(n, cGCMBlkSize); - end; + FGHash := poly_mult_H(XOR_PointerWithT128(@buf^[n], FGHash )); + inc(n, cGCMBlkSize); end; end; diff --git a/Unit Tests/Tests/TestDECCipherModesGCM.pas b/Unit Tests/Tests/TestDECCipherModesGCM.pas index b7f5f6c..6e233c4 100644 --- a/Unit Tests/Tests/TestDECCipherModesGCM.pas +++ b/Unit Tests/Tests/TestDECCipherModesGCM.pas @@ -163,6 +163,7 @@ TestTDECGCM = class(TTestCase) /// Test for GitHub issue #86 /// procedure TestEncodeConstData_86; + procedure TestStreamGCM_87; end; @@ -463,11 +464,13 @@ procedure TestTDECGCM.TestEncode; i : Integer; EncryptData : TBytes; EncrDataStr : string; + idx : integer; 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); + idx := 0; for TestDataSet in FTestDataList do begin for i := Low(TestDataSet.TestData) to High(TestDataSet.TestData) do @@ -584,6 +587,7 @@ procedure TestTDECGCM.TestEncodeConstData_86; cipher.DataToAuthenticate := hea; cipher.Encode(refPlainText, ciphText, sizeof(refPlainText)); + cipher.Done; tag := cipher.CalculatedAuthenticationResult; finally cipher.Free; @@ -897,6 +901,109 @@ procedure TestTDECGCM.TestSetGetDataToAuthenticate; RawByteString(StringOf(FCipherAES.DataToAuthenticate))); end; +procedure TestTDECGCM.TestStreamGCM_87; +// testing of https://github.com/MHumm/DelphiEncryptionCompendium/issues/87 +const cKey : Array[0..3] of LongWord = ( $55A46905, $AA23072B, $1309087f, $A5020869 ); + cIV : Array[0..2] of LongWord = ($1, $2, $3 ); + +var inp : TBytes; + inpStream : TMemoryStream; + + cipher : TCipher_AES128; + outStream : TMemoryStream; + decodeStream : TMemoryStream; + out1, out2 : TBytes; + i : integer; + hea : TBytes; + tag2, tag1, tag3 : TBytes; +begin + // needs to be larger than the default block size of 8192 (in EncodeStream) + SetLength(inp, 32768); + + for i := 0 to Length(inp) - 1 do + inp[i] := i mod 255; + + inpStream := TMemoryStream.Create; + inpStream.WriteBuffer(inp, Length(inp)); + + // ########################################### + // #### First run -> direct encoding + cipher := TCipher_AES128.Create; + try + cipher.Mode := cmGCM; + cipher.Init( cKey, sizeof(cKey), civ, sizeof(civ) ); + + cipher.AuthenticationResultBitLength := 128; + // some authenticatino data + hea := [1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + cipher.DataToAuthenticate := hea; + + out1 := cipher.EncodeBytes(inp); + + cipher.Done; + tag1 := cipher.CalculatedAuthenticationResult; + finally + cipher.Free; + end; + + + // ########################################### + // #### Second run -> stream + outStream := TMemoryStream.Create; + inpStream.Position := 0; + cipher := TCipher_AES128.Create; + try + cipher.Mode := cmGCM; + cipher.Init( cKey, sizeof(cKey), civ, sizeof(civ) ); + + cipher.AuthenticationResultBitLength := 128; + hea := [1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + cipher.DataToAuthenticate := hea; + + cipher.EncodeStream(inpStream, outStream, inpStream.Size, nil); + cipher.Done; + tag2 := cipher.CalculatedAuthenticationResult; + finally + cipher.Free; + end; + + outStream.Position := 0; + SetLength(out2, outStream.Size); + outStream.ReadBuffer(out2, outStream.Size); + + Check( CompareMem(@out1[0], @out2[0], Length(out1) ), 'AES128 Encryption failed'); + Check( CompareMem(@tag1[0], @tag2[0], Length(tag1) ), 'Authentication Result do not match'); + + + // ########################################### + // #### Decode + decodeStream := TMemoryStream.Create; + inpStream.Position := 0; + outStream.Position := 0; + cipher := TCipher_AES128.Create; + try + cipher.Mode := cmGCM; + cipher.Init( cKey, sizeof(cKey), civ, sizeof(civ) ); + + cipher.AuthenticationResultBitLength := 128; + hea := [1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + cipher.DataToAuthenticate := hea; + + cipher.DecodeStream(outStream, decodeStream, outStream.Size, nil); + cipher.Done; + tag3 := cipher.CalculatedAuthenticationResult; + finally + cipher.Free; + end; + + Check( CompareMem(@tag3[0], @tag2[0], Length(tag1) ), 'Authentication Result do not match'); + Check( CompareMem( inpStream.Memory, decodeStream.Memory, inpStream.Size ), 'Decoding failed'); + + decodeStream.Free; + outStream.Free; + inpStream.Free; +end; + initialization // Register all test cases to be run {$IFDEF DUnitX} From dd312e60c8ffb6237a621c0c29864db7af1aeca3 Mon Sep 17 00:00:00 2001 From: Michael Rabatscher Date: Sun, 13 Jul 2025 20:59:25 +0200 Subject: [PATCH 11/17] Minor: unchecked assembler in inc file. Minor: removed debug idx variable. --- Source/DECOptions.inc | 4 +++- Unit Tests/Tests/TestDECCipherModesGCM.pas | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/DECOptions.inc b/Source/DECOptions.inc index da05d4b..8ed9810 100644 --- a/Source/DECOptions.inc +++ b/Source/DECOptions.inc @@ -73,10 +73,12 @@ // Automatically register all hash classes {.$DEFINE ManualRegisterHashClasses} (* default off *) +{.$DEFINE ASSEMBLER} + // if the compiler does not support assembler turn usage off and even if restrict // it to Windows, as those non Windows platforms which actually do support ASM // in Delphi do not use Intel x86 ASM -{$IFDEF FPC } +{$IFNDEF FPC } {$IF defined(CPUX86_64) or defined(CPUAMD64) or defined(CPUIA64) } {$ifndef CPUX64} {$define CPUX64} diff --git a/Unit Tests/Tests/TestDECCipherModesGCM.pas b/Unit Tests/Tests/TestDECCipherModesGCM.pas index 6e233c4..0d6c072 100644 --- a/Unit Tests/Tests/TestDECCipherModesGCM.pas +++ b/Unit Tests/Tests/TestDECCipherModesGCM.pas @@ -464,13 +464,11 @@ procedure TestTDECGCM.TestEncode; i : Integer; EncryptData : TBytes; EncrDataStr : string; - idx : integer; 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); - idx := 0; for TestDataSet in FTestDataList do begin for i := Low(TestDataSet.TestData) to High(TestDataSet.TestData) do From 07775f50a9b737cbc58c7d8345ca11cd41b3a36f Mon Sep 17 00:00:00 2001 From: Michael Rabatscher Date: Wed, 16 Jul 2025 21:46:47 +0200 Subject: [PATCH 12/17] Implemented XChacha20 according to https://datatracker.ietf.org/doc/html/draft-arciszewski-xchacha-03 --- Source/DECCiphers.pas | 95 ++++++++++++++++++++-- Unit Tests/Tests/TestDECChaChaPoly1305.pas | 80 ++++++++++++++++++ 2 files changed, 170 insertions(+), 5 deletions(-) diff --git a/Source/DECCiphers.pas b/Source/DECCiphers.pas index cd0172d..a437d10 100644 --- a/Source/DECCiphers.pas +++ b/Source/DECCiphers.pas @@ -211,6 +211,11 @@ TCipher_XTEA_DEC52 = class; /// TCipher_ChaCha20 = class; + /// + /// XChaCha20 cipher + /// + TCipher_XChaCha20 = class; + // Definitions needed for Skipjack algorithm PSkipjackTab = ^TSkipjackTab; TSkipjackTab = array[0..255] of Byte; @@ -504,7 +509,7 @@ TCipher_ChaCha20 = class(TDECFormattedCipher) // double the size -> two blocks at once TChaChaEncodeBlkFunc = procedure(ChaChaMtx : PChaChaAVXMtx; Source, Dest: Pointer); register; {$IFDEF FPC} assembler; {$ENDIF} - private + protected fInpChaChaMTX : PChaChaMtx; fOutChaChaMtx : PChaChaAVXMtx; @@ -546,6 +551,18 @@ TCipher_ChaCha20 = class(TDECFormattedCipher) class var CpuMode : TChaChaCpuMode; end; + // based on the draft: https://datatracker.ietf.org/doc/html/draft-arciszewski-xchacha-03 + TCipher_XChaCha20 = class(TCipher_ChaCha20) + private + fHChaCha : Array[0..22] of LongWord; + fPHChaCha : PChaChaMtx; + + procedure HChaCha( iv : TBytes ); + protected + procedure OnAfterInitVectorInitialization(const OriginalInitVector: TBytes); override; + public + end; + TCipher_Square = class(TDECFormattedCipher) protected /// @@ -7040,10 +7057,6 @@ procedure TCipher_ChaCha20.OnAfterInitVectorInitialization( inherited; end; -procedure TCipher_ChaCha20.DoInit(const Key; Size: Integer); -// from chacha-prng.h -const cChaChaConst : Array[0..15] of AnsiChar = 'expand 32-byte k'; - function AlignPtr32( A : Pointer ) : Pointer; begin Result := A; @@ -7051,6 +7064,10 @@ function AlignPtr32( A : Pointer ) : Pointer; Result := Pointer( NativeUint(Result) + $20 - NativeUint(Result) and $1F ); end; + +procedure TCipher_ChaCha20.DoInit(const Key; Size: Integer); +// from chacha-prng.h +const cChaChaConst : Array[0..15] of AnsiChar = 'expand 32-byte k'; begin inherited; @@ -7861,6 +7878,71 @@ function TCipher_ChaCha20.TestChaChaMtx( Result := CompareMem(fOutChaChaMtx, @expectedMtx, sizeof(TChaChaMtx)); end; + +{ TCipher_XChaCha20 } + +procedure TCipher_XChaCha20.HChaCha(iv: TBytes); +var i : integer; +begin + // inpmatrix is initialized with the original key + fPHChaCha := AlignPtr32( @fHChaCha[0] ); + Move( fInpChaChaMTX^, fPHChaCha^, sizeof(fPHChaCha^)); + + // copy over the iv -> 128 bit + // create the HChaChaMatrix in fPHChaCha + Move(iv[0], fPHChaCha^[12], 4*sizeof(LongWord)); + + {$IFNDEF PUREPASCAL} + if cpuMode = cmSSE then + begin + for i := 0 to fNumChaChaRounds - 1 do + SSEChaChaDoubleQuarterRound(fPHChaCha); + end + else + {$ENDIF} + begin + for i := 0 to fNumChaChaRounds - 1 do + PasChaChaDoubleQuarterRound(fPHChaCha); + end; +end; + + +procedure TCipher_XChaCha20.OnAfterInitVectorInitialization( + const OriginalInitVector: TBytes); +begin + // RFC point 2.3 and 2.3.1: + + if length(OriginalInitVector) <> 24 then //192 div 8 + raise Exception.Create('IV vector needs to be 192 bits long'); + + // update iv -> use the first 16 bytes! + HChaCha(OriginalInitVector); + + // extract subkey -> overwrite key + + // subkey is the first row and the last row!! + Move(fPHChaCha^[0], fInpChaChaMTX^[4], 4*sizeof(LongWord)); + Move(fPHChaCha^[12], fInpChaChaMTX^[8], 4*sizeof(LongWord)); + + // update IV -> the last 8 bytes + Move(OriginalInitVector[16], FInitializationVector[4], 2*sizeof(LongWord)); + PLongWord(@FInitializationVector[0])^ := 0; // per definition the these bytes are zero + + // truncate iv length + FInitVectorSize := 3*sizeof(longword); + + // int chacha with these values + // note: the inherted routine may not overwrite the key part and needs + // to initialize the nonce with the "tampered" iv + inherited; + + // restore the iv vector size + FInitVectorSize := 24; + + // burn + FillChar(fHChaCha, sizeof(fHChaCha), 0); +end; + initialization SetDefaultCipherClass(TCipher_Null); @@ -7894,6 +7976,9 @@ initialization TCipher_3Way.RegisterClass(TDECCipher.ClassList); TCipher_Cast128.RegisterClass(TDECCipher.ClassList); TCipher_Gost.RegisterClass(TDECCipher.ClassList); + TCipher_ChaCha20.RegisterClass(TDECCipher.ClassList); + TCipher_XChaCha20.RegisterClass(TDECCipher.ClassList); + // Explicitely not registered, as this is an alias for Gost only // TCipher_Magma.RegisterClass(TDECCipher.ClassList); TCipher_Misty.RegisterClass(TDECCipher.ClassList); diff --git a/Unit Tests/Tests/TestDECChaChaPoly1305.pas b/Unit Tests/Tests/TestDECChaChaPoly1305.pas index f0cd388..ca1e851 100644 --- a/Unit Tests/Tests/TestDECChaChaPoly1305.pas +++ b/Unit Tests/Tests/TestDECChaChaPoly1305.pas @@ -22,6 +22,7 @@ TestChaCha20Poly1305 = class(TTestCase) procedure TestChaCha20_Poly1305_AEAD; procedure TestChaChaEncodeDecodeSpeed; + procedure TestXChaCha_Poly1305_AEAD; end; implementation @@ -270,6 +271,85 @@ procedure InvData( data : PByte; len : integer ); end; +procedure TestChaCha20Poly1305.TestXChaCha_Poly1305_AEAD; +// is actually the same test vector as for chacha20_poly1305 +const cMsg : AnsiString = 'Ladies and Gentlemen of the class of ''99: If I could offer you only one tip for the future, sunscreen would be it.'; + cAAD : TBytes = [$50, $51, $52, $53, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7]; + + cKey : TBytes = [$80, $81, $82, $83, $84, $85, $86, $87, $88, $89, $8a, $8b, $8c, $8d, $8e, $8f, + $90, $91, $92, $93, $94, $95, $96, $97, $98, $99, $9a, $9b, $9c, $9d, $9e, $9f]; + + cNonce : TBytes = [$40, $41, $42, $43, $44, $45, $46, $47, $48, $49, $4a, $4b, $4c, $4d, $4e, $4f, $50, $51, $52, $53, $54, $55, $56, $57]; + + cTag : TBytes = [$c0, $87, $59, $24, $c1, $c7, $98, $79, $47, $de, $af, $d8, $78, $0a, $cf, $49]; + + cCipherText : TBytes = [$bd, $6d, $17, $9d, $3e, $83, $d4, $3b, $95, $76, $57, $94, $93, $c0, $e9, $39, + $57, $2a, $17, $00, $25, $2b, $fa, $cc, $be, $d2, $90, $2c, $21, $39, $6c, $bb, + $73, $1c, $7f, $1b, $0b, $4a, $a6, $44, $0b, $f3, $a8, $2f, $4e, $da, $7e, $39, + $ae, $64, $c6, $70, $8c, $54, $c2, $16, $cb, $96, $b7, $2e, $12, $13, $b4, $52, + $2f, $8c, $9b, $a4, $0d, $b5, $d9, $45, $b1, $1b, $69, $b9, $82, $c1, $bb, $9e, + $3f, $3f, $ac, $2b, $c3, $69, $48, $8f, $76, $b2, $38, $35, $65, $d3, $ff, $f9, + $21, $f9, $66, $4c, $97, $63, $7d, $a9, $76, $88, $12, $f6, $15, $c6, $8b, $13, + $b5, $2e ]; +var chaCha : TCipher_XChaCha20; + msg : TBytes; + encr : TBytes; + encrTag : TBytes; + decr : TBytes; + decodeTag : TBytes; + cpuMode : TChaChaCpuMode; +begin + for cpuMode in [cmPas, cmSSE, cmAVX] do + begin + TCipher_XChaCha20.CpuMode := cpuMode; + + SetLength(msg, Length(cMsg)); + Move( cMsg[1], msg[0], Length(msg)); + + + chaCha := TCipher_XChaCha20.Create; + try + chaCha.Mode := cmPoly1305; + chaCha.DataToAuthenticate := cAAD; + chaCha.Init(cKey, cNonce); + encr := chaCha.EncodeBytes(msg); + chaCha.Done; + + encrTag := chaCha.CalculatedAuthenticationResult; + finally + chaCha.Free; + end; + + Check( Length(cCipherText) = Length(encr), 'Encryption length is wrong'); + Check( CompareMem( @cCipherText[0], @encr[0], Length(encr) ), 'Encryption failed'); + + + Check( Length(cTag) = Length(encrTag), 'Tag length is wrong'); + Check( CompareMem( @encrTag[0], @cTag[0], Length(cTag)), 'Calculated Tag is wrong'); + + // ########################################### + // #### Test decode + chaCha := TCipher_XChaCha20.Create; + try + chaCha.Mode := cmPoly1305; + chaCha.ExpectedAuthenticationResult := encrTag; + chaCha.DataToAuthenticate := cAAD; + + chaCha.Init(cKey, cNonce); + decr := chaCha.DecodeBytes(encr); + chaCha.Done; + + decodeTag := chaCha.CalculatedAuthenticationResult + finally + chaCha.Free; + end; + + Check( Length(cTag) = Length(decodeTag), 'Tag length is wrong'); + Check( CompareMem( @decodeTag[0], @cTag[0], Length(cTag)), 'Calculated Tag is wrong'); + end; +end; + + procedure TestChaCha20Poly1305.TestChaCha20_Poly1305_AEAD; const cMsg : AnsiString = 'Ladies and Gentlemen of the class of ''99: If I could offer you only one tip for the future, sunscreen would be it.'; cAAD : TBytes = [$50, $51, $52, $53, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7]; From a4f52c7d145b30cf4f75f634272aa49cb89ed013 Mon Sep 17 00:00:00 2001 From: Michael Rabatscher Date: Sun, 20 Jul 2025 22:33:24 +0200 Subject: [PATCH 13/17] Removed poly1305 dll reference - (was a test to implement a sse/avx version) --- Unit Tests/Tests/TestDECChaChaPoly1305.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Unit Tests/Tests/TestDECChaChaPoly1305.pas b/Unit Tests/Tests/TestDECChaChaPoly1305.pas index ca1e851..dd4e5c9 100644 --- a/Unit Tests/Tests/TestDECChaChaPoly1305.pas +++ b/Unit Tests/Tests/TestDECChaChaPoly1305.pas @@ -26,7 +26,7 @@ TestChaCha20Poly1305 = class(TTestCase) end; implementation -uses DECCipherModesPoly1305, System.Diagnostics, poly1305dll; +uses DECCipherModesPoly1305, System.Diagnostics; //// ########################################### From 2cb78328acb4bdcb8a10d5dc378c66c2d9f3af25 Mon Sep 17 00:00:00 2001 From: Michael Rabatscher Date: Tue, 29 Jul 2025 17:38:43 +0200 Subject: [PATCH 14/17] Added a json based test suite. Removed dll reference Fixed a crucial bug in the poly1305 generation - the ConstTimeCarray32 had wrong brackets applied. --- Source/DECCipherModesPoly1305.pas | 6 +- Source/DECCiphers.pas | 8 +- Unit Tests/DECDUnitTestSuite.dpr | 3 +- Unit Tests/DECDUnitTestSuite.dproj | 3 +- Unit Tests/Data/chacha20_poly1305_test.json | 3710 +++++++++++++++++++ Unit Tests/Tests/TestDECChaChaPoly1305.pas | 210 +- 6 files changed, 3905 insertions(+), 35 deletions(-) create mode 100644 Unit Tests/Data/chacha20_poly1305_test.json diff --git a/Source/DECCipherModesPoly1305.pas b/Source/DECCipherModesPoly1305.pas index c09ae04..ce2f809 100644 --- a/Source/DECCipherModesPoly1305.pas +++ b/Source/DECCipherModesPoly1305.pas @@ -146,7 +146,7 @@ implementation // ) function ConstTimeCarray32(a, b : UInt32 ) : UInt32; inline; begin - Result := (a xor ( (a xor b) or (a - b) xor b )) shr 31; + Result := (a xor ( (a xor b) or ((a - b) xor b))) shr 31; end; @@ -230,7 +230,7 @@ procedure TPoly1305.Finalize; begin if fNum > 0 then begin - fData[fNum] := 1; + fData[fNum] := 1; // padbit.. inc(fNum); while fNum < Length(fData) do begin @@ -372,7 +372,7 @@ procedure TPoly1305.Poly1305Blocks(pData: PByteArray; size: integer; // h4 += (u32)(d3 >> 32) + padbit; d0 := UInt64(h0) + U8ToU32(pData); h0 := UInt32(d0); - d1 := UInt64(h1) + d0 shr 32 + U8ToU32(@pData^[4]); + d1 := UInt64(h1) + d0 shr 32 + U8ToU32(@pData^[4]); // the delphi compiler seems to be intelligent enough to replace the shift by accessing the high 4 bytes h1 := UInt32(d1); d2 := UInt64(h2) + d1 shr 32 + U8ToU32(@pData^[8]); h2 := UInt32(d2); diff --git a/Source/DECCiphers.pas b/Source/DECCiphers.pas index a437d10..3cdff23 100644 --- a/Source/DECCiphers.pas +++ b/Source/DECCiphers.pas @@ -7022,7 +7022,7 @@ procedure TCipher_ChaCha20.OnAfterInitVectorInitialization( var iv : TBytes; begin if FInitVectorSize <> 12 then - raise Exception.Create('Nonce is not 96 bit.'); + raise EDECException.Create('Nonce is not 96 bit.'); fInpChaChaMTX^[12] := 0; // counter Move( FInitializationVector^, fInpChaChaMTX^[13], 3*sizeof(longword)); @@ -7072,7 +7072,7 @@ procedure TCipher_ChaCha20.DoInit(const Key; Size: Integer); inherited; if size <> 32 then - raise Exception.Create('Given ChaCha key size is not 256 bit'); + raise EDECException.Create('Given ChaCha key size is not 256 bit'); // allocate for the AVX case -> 2 cha cha matrices at once fInpChaChaMtx := AlignPtr32(FAdditionalBuffer); @@ -7695,7 +7695,7 @@ procedure TCipher_ChaCha20.InitChaChaBlk; // is this here for xchacha? if fInpChaChaMTX^[12] <= 1 then - raise Exception.Create('Counter overflow!'); + raise EDECException.Create('Counter overflow!'); // inc(fInpChaChaMTX^[13]); end; end; @@ -7913,7 +7913,7 @@ procedure TCipher_XChaCha20.OnAfterInitVectorInitialization( // RFC point 2.3 and 2.3.1: if length(OriginalInitVector) <> 24 then //192 div 8 - raise Exception.Create('IV vector needs to be 192 bits long'); + raise EDECException.Create('IV vector needs to be 192 bits long'); // update iv -> use the first 16 bytes! HChaCha(OriginalInitVector); diff --git a/Unit Tests/DECDUnitTestSuite.dpr b/Unit Tests/DECDUnitTestSuite.dpr index 201e9f3..2a1a612 100644 --- a/Unit Tests/DECDUnitTestSuite.dpr +++ b/Unit Tests/DECDUnitTestSuite.dpr @@ -70,8 +70,7 @@ uses DECUtilRawByteStringHelper in '..\Source\DECUtilRawByteStringHelper.pas', DECZIPHelper in '..\Source\DECZIPHelper.pas', DECCipherModesGCM in '..\Source\DECCipherModesGCM.pas', - TESTDECChaChaPoly1305 in 'Tests\TESTDECChaChaPoly1305.pas', - poly1305dll in '..\Source\poly1305dll.pas'; + TestDECChaChaPoly1305 in 'Tests\TestDECChaChaPoly1305.pas'; {$R *.RES} diff --git a/Unit Tests/DECDUnitTestSuite.dproj b/Unit Tests/DECDUnitTestSuite.dproj index 3520132..f8ffab9 100644 --- a/Unit Tests/DECDUnitTestSuite.dproj +++ b/Unit Tests/DECDUnitTestSuite.dproj @@ -194,8 +194,7 @@ - - + Base diff --git a/Unit Tests/Data/chacha20_poly1305_test.json b/Unit Tests/Data/chacha20_poly1305_test.json new file mode 100644 index 0000000..e6253fd --- /dev/null +++ b/Unit Tests/Data/chacha20_poly1305_test.json @@ -0,0 +1,3710 @@ +{ + "algorithm" : "CHACHA20-POLY1305", + "numberOfTests" : 300, + "header" : [ + "Test vectors of type AeadTest test authenticated encryption with", + "additional data. The test vectors are intended for testing both", + "encryption and decryption." + ], + "notes" : { + }, + "schema" : "aead_test_schema.json", + "testGroups" : [ + { + "ivSize" : 96, + "keySize" : 256, + "tagSize" : 128, + "type" : "AeadTest", + "source" : { + "name" : "google-wycheproof", + "version" : "0.8r12" + }, + "tests" : [ + { + "tcId" : 1, + "comment" : "RFC 7539", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "070000004041424344454647", + "aad" : "50515253c0c1c2c3c4c5c6c7", + "msg" : "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e", + "ct" : "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116", + "tag" : "1ae10b594f09e26a7e902ecbd0600691", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 2, + "comment" : "", + "key" : "80ba3192c803ce965ea371d5ff073cf0f43b6a2ab576b208426e11409c09b9b0", + "iv" : "4da5bf8dfd5852c1ea12379d", + "aad" : "", + "msg" : "", + "ct" : "", + "tag" : "76acb342cf3166a5b63c0c0ea1383c8d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 3, + "comment" : "", + "key" : "7a4cd759172e02eb204db2c3f5c746227df584fc1345196391dbb9577a250742", + "iv" : "a92ef0ac991dd516a3c6f689", + "aad" : "bd506764f2d2c410", + "msg" : "", + "ct" : "", + "tag" : "906fa6284b52f87b7359cbaa7563c709", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 4, + "comment" : "", + "key" : "cc56b680552eb75008f5484b4cb803fa5063ebd6eab91f6ab6aef4916a766273", + "iv" : "99e23ec48985bccdeeab60f1", + "aad" : "", + "msg" : "2a", + "ct" : "3a", + "tag" : "cac27dec0968801e9f6eded69d807522", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 5, + "comment" : "", + "key" : "46f0254965f769d52bdb4a70b443199f8ef207520d1220c55e4b70f0fda620ee", + "iv" : "ab0dca716ee051d2782f4403", + "aad" : "91ca6c592cbcca53", + "msg" : "51", + "ct" : "c4", + "tag" : "168310ca45b1f7c66cad4e99e43f72b9", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 6, + "comment" : "", + "key" : "2f7f7e4f592bb389194989743507bf3ee9cbde1786b6695fe6c025fd9ba4c100", + "iv" : "461af122e9f2e0347e03f2db", + "aad" : "", + "msg" : "5c60", + "ct" : "4d13", + "tag" : "91e8b61efb39c122195453077b22e5e2", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 7, + "comment" : "", + "key" : "c8833dce5ea9f248aa2030eacfe72bffe69a620caf793344e5718fe0d7ab1a58", + "iv" : "61546ba5f1720590b6040ac6", + "aad" : "88364fc8060518bf", + "msg" : "ddf2", + "ct" : "b60d", + "tag" : "ead0fd4697ec2e5558237719d02437a2", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 8, + "comment" : "", + "key" : "bd8ed7fb0d607522f04d0b12d42c92570bccc5ba2486953d70ba2e8193f6225a", + "iv" : "d2ab0abb50a8e9fba25429e1", + "aad" : "", + "msg" : "201221", + "ct" : "3cf470", + "tag" : "a27a69c9d7ee84586f11388c6884e63a", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 9, + "comment" : "", + "key" : "1c8b59b17a5ceced31bde97d4cefd9aaaa63362e096e863ec1c89580bca79b7a", + "iv" : "94f32a6dff588f2b5a2ead45", + "aad" : "6c8cf2ab3820b695", + "msg" : "453f95", + "ct" : "610925", + "tag" : "a8a7883eb7e40bc40e2e5922ae95ddc3", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 10, + "comment" : "", + "key" : "e4912cb75a1174345f1a457366f18885fe8460b06478e04be2f7fb4ec9c113e5", + "iv" : "7aa5ad8bf5254762171ec869", + "aad" : "", + "msg" : "9e4c1d03", + "ct" : "fe6849aa", + "tag" : "99ad07871b25c27defc31a541bd5c418", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 11, + "comment" : "", + "key" : "e05777ef3d989ace7d2abfba452bfded54801dbd5c66e91c0c2ef00479d85572", + "iv" : "b7f526e3fd71cf5720961aec", + "aad" : "15d93a96d0e6c5a9", + "msg" : "17bfda03", + "ct" : "f4710e51", + "tag" : "b957c6a37b6a4c94996c002186d63b2b", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 12, + "comment" : "", + "key" : "1a4c4f39abe890e62345c947bcf7de7c2e33bd5ceeda0a0abf0e7ef935ddf3ee", + "iv" : "9447bf85d5b97d8aee0f8e51", + "aad" : "", + "msg" : "c15a593bd0", + "ct" : "f711647ff1", + "tag" : "22b12dc38cb79629f84cdbdc2425c09d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 13, + "comment" : "", + "key" : "800e9a24791700c9609736695ba2a8b99b2d57f1c3bfb61ed49db1c6c5219583", + "iv" : "3dbe876bd880ec8ea2017043", + "aad" : "96224835610b782b", + "msg" : "a7bfd041e3", + "ct" : "d171f046ea", + "tag" : "d179b1b9c4184378df009019dbb8c249", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 14, + "comment" : "", + "key" : "208c2c376c9430433db20e1a6b7ba817f8ffbfa6827f26759ccede42e591d3ec", + "iv" : "27fb58ec6a21e84696cb8830", + "aad" : "", + "msg" : "af104b5ccd0e", + "ct" : "9351b1b1b082", + "tag" : "560785509f60f26b681933d9cdbfd29f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 15, + "comment" : "", + "key" : "2eb168e53b07ab04355ea792fe11a6be2ce9c39cfe15a997076b1e38c17ad620", + "iv" : "b5965470c383fd29fe7eaee7", + "aad" : "6d52feb2509f7fbf", + "msg" : "6fdf2927e169", + "ct" : "41abff7b71cc", + "tag" : "9b5174297c03cf8902d1f706fd008902", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 16, + "comment" : "", + "key" : "55568158d3a6483f1f7021eab69b703f614251cadc1af5d34a374fdbfc5adac7", + "iv" : "3c4e654d663fa4596dc55bb7", + "aad" : "", + "msg" : "ab85e9c1571731", + "ct" : "5dfe3440dbb3c3", + "tag" : "ed7a434e2602d394281e0afa9fb7aa42", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 17, + "comment" : "", + "key" : "e3c09e7fab1aefb516da6a33022a1dd4eb272c80d540c5da52a730f34d840d7f", + "iv" : "58389375c69ee398de948396", + "aad" : "84e46be8c0919053", + "msg" : "4ee5cda20d4290", + "ct" : "4bd47212941ce3", + "tag" : "185f1408ee7fbf18f5abad6e2253a1ba", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 18, + "comment" : "", + "key" : "51e4bf2bad92b7aff1a4bc05550ba81df4b96fabf41c12c7b00e60e48db7e152", + "iv" : "4f07afedfdc3b6c2361823d3", + "aad" : "", + "msg" : "be3308f72a2c6aed", + "ct" : "8e9439a56eeec817", + "tag" : "fbe8a6ed8fabb1937539dd6c00e90021", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 19, + "comment" : "", + "key" : "1131c1418577a054de7a4ac551950f1a053f9ae46e5b75fe4abd5608d7cddadd", + "iv" : "b4ea666ee119563366484a78", + "aad" : "66c0ae70076cb14d", + "msg" : "a4c9c2801b71f7df", + "ct" : "b9b910433af052b0", + "tag" : "4530f51aeee024e0a445a6328fa67a18", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 20, + "comment" : "", + "key" : "e1094967f86d893cdfe2e2e6d5c7ee4dfef67da3c9c5d64e6ad7c1577dcb38c5", + "iv" : "8092fc245b3326cddbd1424c", + "aad" : "", + "msg" : "c37aa791ddd6accf91", + "ct" : "d9d897a9c1c5bb9f01", + "tag" : "085a430373058f1a12a0d589fd5be68b", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 21, + "comment" : "", + "key" : "236f9baee4f9da15beeca40ff4af7c760f254a64bc3a3d7f4fad557e61b68586", + "iv" : "f1ca81338629587acf9372bf", + "aad" : "8c32f47a386152ec", + "msg" : "d7f26d5252e1765f5b", + "ct" : "8fdb429d47761cbf8e", + "tag" : "8ef647ed334fdebbc2bef80be02884e0", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 22, + "comment" : "", + "key" : "4de207a3b70c51e5f23048eed5a5da9bb65e917a69aa93e7c8b4a815cd9724de", + "iv" : "4c15a71dc6791a8c005ad502", + "aad" : "", + "msg" : "f2c54b6b5e490da18659", + "ct" : "700d35adf5100a22a1de", + "tag" : "102d992ffaff599b5bddddeb2dfb399b", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 23, + "comment" : "", + "key" : "6d667fd79e5fb725f50343dccc4863227c75ee3f7a578476e3e9f32598d81559", + "iv" : "6220527aba88e27f766658b2", + "aad" : "e1e27ccddb3cb407", + "msg" : "0c8c5a252681f2b5b4c0", + "ct" : "04aad66c60e0bf8ebba9", + "tag" : "c15f69a4d2aef97d7748756ff49d894b", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 24, + "comment" : "", + "key" : "8f4bd94ef73e75d1e068c30b37ead576c5344e093ece1330e9101c82f793cf05", + "iv" : "ec1e2967f0f6979e5f5b07fb", + "aad" : "", + "msg" : "b89812b34d9bced4a0ba07", + "ct" : "1c3d53baaa36eaa1d8ec4d", + "tag" : "4d94ebf960f12433bec43aa86d7e6e6d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 25, + "comment" : "", + "key" : "2aa3bc7033351cac51364cdaf6ffac2c20f64046e1550a7b1c65f41800599019", + "iv" : "28cce57a5db2cd206321e340", + "aad" : "a9bc350eaf2e6e3d", + "msg" : "83016823123484b56095b0", + "ct" : "1c8578f8e75203d0336a52", + "tag" : "5910f7a9d5e4df05d7248bd7a8d65e63", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 26, + "comment" : "", + "key" : "99b62bd5afbe3fb015bde93f0abf483957a1c3eb3ca59cb50b39f7f8a9cc51be", + "iv" : "9a59fce26df0005e07538656", + "aad" : "", + "msg" : "42baae5978feaf5c368d14e0", + "ct" : "ff7dc203b26c467a6b50db33", + "tag" : "578c0f2758c2e14e36d4fc106dcb29b4", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 27, + "comment" : "", + "key" : "85f35b6282cff440bc1020c8136ff27031110fa63ec16f1e825118b006b91257", + "iv" : "58dbd4ad2c4ad35dd906e9ce", + "aad" : "a506e1a5c69093f9", + "msg" : "fdc85b94a4b2a6b759b1a0da", + "ct" : "9f8816de0994e938d9e53f95", + "tag" : "d086fc6c9d8fa915fd8423a7cf05072f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 28, + "comment" : "", + "key" : "faf4bfe8019a891c74901b17f4f48cee5cd065d55fdea60118aaf6c4319a0ea5", + "iv" : "b776c3fddba7c81362ce6e1b", + "aad" : "", + "msg" : "8dadff8d60c8e88f604f274833", + "ct" : "e6b33a74a4ac443bd93f9c1b94", + "tag" : "0c115172bdb02bbad3130fff22790d60", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 29, + "comment" : "", + "key" : "841020d1606edcfc536abfb1a638a7b958e21efc10c386ac45a18493450afd5f", + "iv" : "6d62f159731b140eb18ce074", + "aad" : "5a8e1c7aa39810d5", + "msg" : "d6af138f701b801e60c85ffd5c", + "ct" : "b0a7500aca45bb15f01ece4389", + "tag" : "0160e83adbec7f6a2ee2ff0215f9ef00", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 30, + "comment" : "", + "key" : "470f9ce3d2250bd60cbbefdb2e6a1178c012299b5590639c7797b6024fa703d8", + "iv" : "a9ea4d619fe405d04cba7d7a", + "aad" : "", + "msg" : "6ca67dd023fba6507b9f9a1f667e", + "ct" : "d3017e0bb1705b380b34cc333450", + "tag" : "5708e72ca2bd354f487f82f67fbc3acb", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 31, + "comment" : "", + "key" : "e4b97e91e4c8e85eb7ce0a7f30bf8a0abf4468251e4c6386c0e7aacb8e879aa8", + "iv" : "0e23c942a0c9fb526586eead", + "aad" : "eaaaeab26957f9a1", + "msg" : "b84b3f74cd23064bb426fe2ced2b", + "ct" : "52e9672b416d84d97033796072d0", + "tag" : "e83839dc1fd9b8b9d1444c40e488d493", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 32, + "comment" : "", + "key" : "67119627bd988eda906219e08c0d0d779a07d208ce8a4fe0709af755eeec6dcb", + "iv" : "68ab7fdbf61901dad461d23c", + "aad" : "", + "msg" : "51f8c1f731ea14acdb210a6d973e07", + "ct" : "0b29638e1fbdd6df53970be2210042", + "tag" : "2a9134087d67a46e79178d0a93f5e1d2", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 33, + "comment" : "", + "key" : "e6f1118d41e4b43fb58221b7ed79673834e0d8ac5c4fa60bbc8bc4893a58894d", + "iv" : "d95b3243afaef714c5035b6a", + "aad" : "6453a53384632212", + "msg" : "97469da667d6110f9cbda1d1a20673", + "ct" : "32db66c4a3819d81557455e5980fed", + "tag" : "feae30dec94e6ad3a9eea06a0d703917", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 34, + "comment" : "", + "key" : "59d4eafb4de0cfc7d3db99a8f54b15d7b39f0acc8da69763b019c1699f87674a", + "iv" : "2fcb1b38a99e71b84740ad9b", + "aad" : "", + "msg" : "549b365af913f3b081131ccb6b825588", + "ct" : "e9110e9f56ab3ca483500ceabab67a13", + "tag" : "836ccabf15a6a22a51c1071cfa68fa0c", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 35, + "comment" : "", + "key" : "b907a45075513fe8a8019edee3f2591487b2a030b03c6e1d771c862571d2ea1e", + "iv" : "118a6964c2d3e380071f5266", + "aad" : "034585621af8d7ff", + "msg" : "55a465644f5b650928cbee7c063214d6", + "ct" : "e4b113cb775945f3d3a8ae9ec141c00c", + "tag" : "7c43f16ce096d0dc27c95849dc383b7d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 36, + "comment" : "", + "key" : "3b2458d8176e1621c0cc24c0c0e24c1e80d72f7ee9149a4b166176629616d011", + "iv" : "45aaa3e5d16d2d42dc03445d", + "aad" : "", + "msg" : "3ff1514b1c503915918f0c0c31094a6e1f", + "ct" : "02cc3acb5ee1fcdd12a03bb857976474d3", + "tag" : "d83b7463a2c3800fe958c28eaa290813", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 37, + "comment" : "", + "key" : "f60c6a1b625725f76c7037b48fe3577fa7f7b87b1bd5a982176d182306ffb870", + "iv" : "f0384fb876121410633d993d", + "aad" : "9aaf299eeea78f79", + "msg" : "63858ca3e2ce69887b578a3c167b421c9c", + "ct" : "35766488d2bc7c2b8d17cbbb9abfad9e6d", + "tag" : "1f391e657b2738dda08448cba2811ceb", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 38, + "comment" : "", + "key" : "37ceb574ccb0b701dd11369388ca27101732339f49d8d908ace4b23af0b7ce89", + "iv" : "37270b368f6b1e3e2ca51744", + "aad" : "", + "msg" : "f26991537257378151f4776aad28ae8bd16b", + "ct" : "b621d76a8dacff00b3f840cdf26c894cc5d1", + "tag" : "e0a21716ed94c0382fa9b0903d15bb68", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 39, + "comment" : "", + "key" : "68888361919bc10622f45df168e5f6a03bd8e884c0611bea2f34c1882ed9832b", + "iv" : "bfd6ff40f2df8ca7845980cc", + "aad" : "b8373438ddb2d6c3", + "msg" : "ff97f2eefb3401ac31fc8dc1590d1a92cbc1", + "ct" : "e0a745186c1a7b147f74faff2a715df5c19d", + "tag" : "917baf703e355d4d950e6c05fe8f349f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 40, + "comment" : "", + "key" : "1b35b856b5a86d3403d28fc2103a631d42deca5175cdb0669a5e5d90b2caafc5", + "iv" : "2343de88be6c7196d33b8694", + "aad" : "", + "msg" : "21ef185c3ae9a96fa5eb473878f4d0b242781d", + "ct" : "d6e0ed54fccef30bd605d72da3320e249a9cb5", + "tag" : "c68bc6724ec803c43984ce42f6bd09ff", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 41, + "comment" : "", + "key" : "d6484e3973f6be8c83ed3208d5be5cfa06fda72fbfdc5b19d09be3f4e4eba29d", + "iv" : "1af1d90e877e11a496efa3df", + "aad" : "cc4efd8364fb114a", + "msg" : "7335ab04b03e706109ec3ee835db9a246ea0ad", + "ct" : "29e54d608237c3c3609dba16e6edf43842d72f", + "tag" : "d3365fdcd506aaaa5368661e80e9d99b", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 42, + "comment" : "", + "key" : "422add37849d6e4c3dfd8020dc6a07e8a249788f3d6a83b9cb4d802362c97542", + "iv" : "1e7e67be948de7352ffdb727", + "aad" : "", + "msg" : "d7f5e611dd3a2750fb843fc1b6b93087310dc87d", + "ct" : "7fe606652d858f595ec2e706754fa3d933fcc834", + "tag" : "78d59235aa5d03a4c32590e590c04d22", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 43, + "comment" : "", + "key" : "cdccfe3f46d782ef47df4e72f0c02d9c7f774def970d23486f11a57f54247f17", + "iv" : "376187894605a8d45e30de51", + "aad" : "956846a209e087ed", + "msg" : "e28e0e9f9d22463ac0e42639b530f42102fded75", + "ct" : "14f707c446988a4903775ec7acec6da114d43112", + "tag" : "987d4b147c490d43d376a198cab383f0", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 44, + "comment" : "", + "key" : "e79dfc6d2fc465b8439e1c5baccb5d8ef2853899fc19753b397e6c25b35e977e", + "iv" : "f9d6320d7ce51d8ed0677d3a", + "aad" : "", + "msg" : "4f543e7938d1b878dacaeec81dce4899974816813b", + "ct" : "1003f13ea1329cbb187316f64c3ff3a87cf5b96661", + "tag" : "d2323ad625094bec84790d7958d5583f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 45, + "comment" : "", + "key" : "1d7b8f1d96a1424923aef8a984869d4a777a110990ba465627acf80396c7f376", + "iv" : "50ba1962cdc32a5a2d36e640", + "aad" : "093053e20261daab", + "msg" : "5d3efd5767f3c12efd08af9a44e028ae68c9eff843", + "ct" : "2d48b0834e9ffe3046103ef7a214f02e8e4d33360e", + "tag" : "d533ad089be229ea606ec0f3fa22eb33", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 46, + "comment" : "", + "key" : "dd433e28cfbcb5de4ab36a02bf38686d83208771a0e63dcd08b4df1a07ac47a1", + "iv" : "c9cc0a1afc38ec6c30c38c68", + "aad" : "", + "msg" : "8a3e17aba9606dd49e3b1a4d9e5e42f1742373632489", + "ct" : "e9917ff3e64bbe1783579375e75ea823976b35539949", + "tag" : "074a890669b25105434c75beed3248db", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 47, + "comment" : "", + "key" : "a60924101b42ac24154a88de42142b2334cf599176caf4d1226f712dd9172930", + "iv" : "8ba77644b08d65d5e9f31942", + "aad" : "b2a4e12a19a61c75", + "msg" : "c949957e66439deee4b2ac1d4a6c98a6c527b90f52ab", + "ct" : "db4c700513818972b0dc0e531b1c281ca03e40c60dea", + "tag" : "63f4478bba2af469a7a4dc3b4f141360", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 48, + "comment" : "", + "key" : "1aa42027836965b1e6086fa137f9cf7f1ff48676696829bd281ff81c8ea0a4a9", + "iv" : "4b3dca84ecc407f424f281a9", + "aad" : "", + "msg" : "37252a3eb5c8960f0567e503a9035783b3d0a19a4b9a47", + "ct" : "b5f14617491fc923b683e2cc9562d043dd5986b97dbdbd", + "tag" : "972ce54713c05c4bb4d088c0a30cacd3", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 49, + "comment" : "", + "key" : "5d40db0cc18ef2e42815d3b6245a466a0b30a0f93e318ac10edde3bf8ad98160", + "iv" : "acad618039b317470d21621b", + "aad" : "413036411af75745", + "msg" : "959dde1ef3129b27702c558849e466f2baca1a45bdf4b2", + "ct" : "b7ca3879f95140bf6a97b3212218b7bf864a51e5bb0b3e", + "tag" : "fe558fb570145470ea693eb76eb73171", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 50, + "comment" : "", + "key" : "0212a8de5007ed87b33f1a7090b6114f9e08cefd9607f2c276bdcfdbc5ce9cd7", + "iv" : "e6b1adf2fd58a8762c65f31b", + "aad" : "", + "msg" : "10f1ecf9c60584665d9ae5efe279e7f7377eea6916d2b111", + "ct" : "42f26c56cb4be21d9d8d0c80fc99dde00d75f38074bfe764", + "tag" : "54aa7e13d48fff7d7557039457040a3a", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 51, + "comment" : "", + "key" : "c5bc09565646e7edda954f1f739223dada20b95c44ab033d0fae4b0283d18be3", + "iv" : "6b282ebecc541bcd7834ed55", + "aad" : "3e8bc5ade182ff08", + "msg" : "9222f9018e54fd6de1200806a9ee8e4cc904d29f25cba193", + "ct" : "123032437b4bfd6920e8f7e7e0087ae4889ebe7a0ad0e900", + "tag" : "3cf68f179550da63d3b96c2d55411865", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 52, + "comment" : "", + "key" : "9460b3c44ed86e70f3bda66385e1ca10b0c1677ef4f1360532830d17535f996f", + "iv" : "abfaf42e0dba884efcf07823", + "aad" : "", + "msg" : "5c5cce881b93fb7a1b7939af1ffc5f84d3280ada778cca0953", + "ct" : "1d218c9f1f9f02f248a6f976a7557057f37d9393d9f213c1f3", + "tag" : "bc88344c6fdc898feed394fb28511316", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 53, + "comment" : "", + "key" : "c111d6d5d78a071b15ab37cc8c3819199387ab7c1933aa97b1489f6584ba8e2a", + "iv" : "85f18ad8ff72cafee2452ab8", + "aad" : "84cdff939391c022", + "msg" : "6989c646a10b7c76f4d9f7d574da40e152013cf0dd78f5aa8a", + "ct" : "9715d344e8d3f3a3eaa98a9cea57c0cd717c6ef5076027c9ec", + "tag" : "3056ff5ee0aa8636bb639984edb5236b", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 54, + "comment" : "", + "key" : "8a1b1e699a0c4a3e610b10902daedab1bf1ea0d505c47d7842cbcee0d3b1b6e6", + "iv" : "a6f9a8d335fa84c3b27dcd2a", + "aad" : "", + "msg" : "ee6a15fc183108f0877e7f2b8a9615f4b3fc36e1c83440f66aad", + "ct" : "9089bbdb8bcfd124e227bf75c4bfe1cba2004a274fc31aa32358", + "tag" : "fd2e21c64a019621c68594826cd7b1cd", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 55, + "comment" : "", + "key" : "74b384e6e013ec4172ed7a28a10fb9bb79b4be2a24f6999e3d3caa28e64a8656", + "iv" : "ebc19fc9ecb2339908ea3836", + "aad" : "85073f2edc13d3a1", + "msg" : "3aa9f7372f056e5a0729752d9a37132d6dd07c56792e1c7582a9", + "ct" : "796ffb70ab43e7fa79f95583e384524727bb3e47fc45b969f714", + "tag" : "c3322b4445de5f3c9f18dcc847cc94c3", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 56, + "comment" : "", + "key" : "77d824795d2029f0eb0e0baab5cfeb32f7e93474913a7f95c737a667a3c33314", + "iv" : "f3307430f492d2b8a72d3a81", + "aad" : "", + "msg" : "0c4179a497d8fdd72796fb725692b805d63b7c718359cf10518aee", + "ct" : "49c81d17d67d7ba9954f497d0b0ddc21f3f839c9d2cc198d30bc2c", + "tag" : "50009899e5b2a9726c8f3556cadfbe84", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 57, + "comment" : "", + "key" : "bec5eac68f893951cbd7d1ecd3ee6611130dd9c3f80cddf95111d07d5edd76d1", + "iv" : "342ada4f0c115124b222df80", + "aad" : "73365f6d80edb1d8", + "msg" : "481433d8b1cd38af4a750e13a64b7a4e8507682b3517595938a20e", + "ct" : "4c129fc13cbdd9d3fe81ac755bf4fbea2fdd7e0aca0505a6ee9637", + "tag" : "9cede1d30a03db5d55265d3648bc40d4", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 58, + "comment" : "", + "key" : "a59c1e13064df8f2b8df77a492b0ca2eae921b52a84b305a3a9a51408a9ecb69", + "iv" : "9544d41ece0c92ef01cfac2d", + "aad" : "", + "msg" : "1c35b898821ba55c2617c25df9e6df2a8002b384902186cd69dfd20e", + "ct" : "a6fa8f57ddc81d6099f667dd62402b6a5d5b7d05a329298029113169", + "tag" : "bb24e38b31dbbc3e575b9e3ee076af2a", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 59, + "comment" : "", + "key" : "084b5d7365f1a8fec6365939ed741e6ea5893e0318d82ab47500a97d77aaa041", + "iv" : "829f005e980f0a6e2f983eaa", + "aad" : "770f6e6e89a3fe8e", + "msg" : "7510016efadc385a71ed689ceb590c8ea9cc1e81b793338bddf5f10c", + "ct" : "fd42cb5cf894f879e3cf751662aaa58a2288cc53548802becaf42359", + "tag" : "188329438afe1cd7225d0478aa90c773", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 60, + "comment" : "", + "key" : "5a7f850a1d9aafa77d59ae1b731965e8aaec6352280fc76a7b5e23ef3610cfe4", + "iv" : "4946a0d6adea93b82d4332e5", + "aad" : "", + "msg" : "3c161d791f624fb0388e808f0f69ed790dbe4cbd089ebac46627bcf01d", + "ct" : "402302b56140c4dcc39774732c55883de124ce4bf0a0261cfa1569e2cf", + "tag" : "e830bfe933a96786cff2dd72b82c4bd5", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 61, + "comment" : "", + "key" : "e6d5a4246f6f05618b59c8f9ec3ac8068cc0d3f351c571aa52b09cb251f9c2f6", + "iv" : "2f90a65e9e48725de6ffc727", + "aad" : "f2415377ad283fd8", + "msg" : "964fc9e0e8355947aa1c2caadd7b3dbef82a1024e623606fac436ef573", + "ct" : "d052932bad6e6c4f835f02019e52d7ff807dc2a5aac2040883c79dd3d5", + "tag" : "655f93396b4d755dc4475721665fed91", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 62, + "comment" : "", + "key" : "09e822123adbb1ed89b79a58619c64853992f8371d46338712f6c91ab11a68bb", + "iv" : "a797205a6cacdd7e47a4789d", + "aad" : "", + "msg" : "80b71bbe833629841bd3aeaeb9db6123e51d367b436fe9d2d3454b62cfad", + "ct" : "83f5c77396cabd28dfcc002cba0756d4ea5455e0261d847d5708aac21e8d", + "tag" : "705a05820a21f381d244d40e58d2f16b", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 63, + "comment" : "", + "key" : "625735fe7f8fc81b0c1edc3d08a78b41268f87a3c68488b674222630c1d587a5", + "iv" : "9d8cdf289dddd09afdc1b02f", + "aad" : "200a9c95946ff05c", + "msg" : "67ae1882d0b1c1b2485bec98115ecf53b9b438deb1d0400531705038873a", + "ct" : "209b7539385c8b19ecd0fd8b5011b2996e316f1942064e68edfa363acbcd", + "tag" : "fa2f454b9fa2608f780f7c6f9b780fe1", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 64, + "comment" : "", + "key" : "2eb51c469aa8eb9e6c54a8349bae50a20f0e382711bba1152c424f03b6671d71", + "iv" : "04a9be03508a5f31371a6fd2", + "aad" : "", + "msg" : "b053999286a2824f42cc8c203ab24e2c97a685adcc2ad32662558e55a5c729", + "ct" : "45c7d6b53acad4abb68876a6e96a48fb59524d2c92c9d8a189c9fd2db91746", + "tag" : "566d3ca10e311b695f3eae1551652493", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 65, + "comment" : "", + "key" : "7f5b74c07ed1b40fd14358fe2ff2a740c116c7706510e6a437f19ea49911cec4", + "iv" : "470a339ecb3219b8b81a1f8b", + "aad" : "374618a06ea98a48", + "msg" : "f45206abc25552b2abc9ab7fa243035fedaaddc3b2293956f1ea6e7156e7eb", + "ct" : "46a80c4187024720084627580080dde5a3f4a11093a7076ed6f3d326bc7b70", + "tag" : "534d4aa2835a52e72d14df0e4f47f25f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 66, + "comment" : "", + "key" : "e1731d5854e1b70cb3ffe8b786a2b3ebf0994370954757b9dc8c7bc5354634a3", + "iv" : "72cfd90ef3026ca22b7e6e6a", + "aad" : "", + "msg" : "b9c554cbc36ac18ae897df7beecac1dbeb4eafa156bb60ce2e5d48f05715e678", + "ct" : "ea29afa49d36e8760f5fe19723b9811ed5d519934a440f5081ac430b953b0e21", + "tag" : "222541af46b86533c6b68d2ff108a7ea", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 67, + "comment" : "", + "key" : "27d860631b0485a410702fea61bc873f3442260caded4abde25b786a2d97f145", + "iv" : "262880d475f3dac5340dd1b8", + "aad" : "2333e5ce0f93b059", + "msg" : "6b2604996cd30c14a13a5257ed6cffd3bc5e29d6b97eb1799eb335e281ea451e", + "ct" : "6dad637897544d8bf6be9507ed4d1bb2e954bc427e5de729daf50762846ff2f4", + "tag" : "7b997d93c982189d7095dc794c746232", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 68, + "comment" : "", + "key" : "5155dee9aade1cc61ee7e3f92660f7590f5e5ba82f1b59b850e3fa453d2fa6b3", + "iv" : "c26c4b3bfdb97ee6b0f63ca1", + "aad" : "", + "msg" : "2734e08eff8f5c4f84fa0c207f49c7fd78af1ad5123ff81f83f500edf4eda09edf", + "ct" : "f5982b601c7a18fc72a65b218c44974dc564d8314cbe6f87fcf6c6cfbe618b34b1", + "tag" : "c43632f55760b5d1ed37556a94d049b5", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 69, + "comment" : "", + "key" : "573f08ebbe0cce4ac9618e8c3b224bea0a32f055c6996838a32f527ca3c3b695", + "iv" : "ad8050dc6d122dce3e5639ed", + "aad" : "e99698241c599b5f", + "msg" : "668d5e3f95fe030daf432a5fc5837af3a79c81e94b28d8204c5ee262ab3c9908a7", + "ct" : "eaf6810e6ec1cb7a2918856257d1aa3d51a827879146c6337ecf535e9c89b149c5", + "tag" : "a2950c2f394a3466c345f796323c1aa7", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 70, + "comment" : "", + "key" : "cf0d40a4644e5f51815165d5301b22631f4544c49a1878e3a0a5e8e1aae0f264", + "iv" : "e74a515e7e2102b90bef55d2", + "aad" : "", + "msg" : "973d0c753826bae466cf9abb3493152e9de7819e2bd0c71171346b4d2cebf8041aa3cedc0dfd7b467e26228bc86c9a", + "ct" : "fba78ae4f9d808a62e3da40be2cb7700c3613d9eb2c529c652e76a432c658d27095f0eb8f940c324981ea935e507f9", + "tag" : "8f046956db3a512908bd7afc8f2ab0a9", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 71, + "comment" : "", + "key" : "6cbfd71c645d184cf5d23c402bdb0d25ec54898c8a0273d42eb5be109fdcb2ac", + "iv" : "d4d807341683825b31cd4d95", + "aad" : "b3e4064683b02d84", + "msg" : "a98995504df16f748bfb7785ff91eeb3b660ea9ed3450c3d5e7b0e79ef653659a9978d75542ef91c456762215640b9", + "ct" : "a1ffed80761829ecce242e0e88b138049016bca018da2b6e19986b3e318cae8d806198fb4c527cc39350ebddeac573", + "tag" : "c4cbf0befda0b70242c640d7cd02d7a3", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 72, + "comment" : "", + "key" : "5b1d1035c0b17ee0b0444767f80a25b8c1b741f4b50a4d3052226baa1c6fb701", + "iv" : "d61040a313ed492823cc065b", + "aad" : "", + "msg" : "d096803181beef9e008ff85d5ddc38ddacf0f09ee5f7e07f1e4079cb64d0dc8f5e6711cd4921a7887de76e2678fdc67618f1185586bfea9d4c685d50e4bb9a82", + "ct" : "9a4ef22b181677b5755c08f747c0f8d8e8d4c18a9cc2405c12bb51bb1872c8e8b877678bec442cfcbb0ff464a64b74332cf072898c7e0eddf6232ea6e27efe50", + "tag" : "9ff3427a0f32fa566d9ca0a78aefc013", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 73, + "comment" : "", + "key" : "97d635c4f47574d9998a90875da1d3a284b755b2d39297a5725235190e10a97e", + "iv" : "d31c21aba175b70de4ebb19c", + "aad" : "7193f623663321a2", + "msg" : "94ee166d6d6ecf8832437136b4ae805d428864359586d9193a25016293edba443c58e07e7b7195ec5bd84582a9d56c8d4a108c7d7ce34e6c6f8ea1bec0567317", + "ct" : "5fbbdecc34be201614f636031eeb42f1cace3c79a12cffd871ee8e73820c829749f1abb4294367849fb6c2aa56bda8a3078f723d7c1c852024b017b58973fb1e", + "tag" : "09263da7b4cb921452f97dca40f580ec", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 74, + "comment" : "", + "key" : "fe6e55bdaed1f7284ca5fc0f8c5f2b8df56dc0f49e8ca66a41995e783351f901", + "iv" : "17c86a8abbb7e003acde2799", + "aad" : "", + "msg" : "b429eb80fb8fe8baeda0c85b9c333458e7c2992e558475069d12d45c22217564121588032297eff56783742a5fc22d7410ffb29d66098661d76f126c3c27689e43b37267cac5a3a6d3ab49e391da29cd3054a5692e2807e4c3ea46c8761d50f592", + "ct" : "d0102f6c258bf49742cec34cf2d0fedf23d105fb4c84cf98515e1bc9a64f8ad5be8f0721bde50645d00083c3a263a31053b760245f52ae2866a5ec83b19f61be1d30d5c5d9fecc4cbbe08fd385813a2aa39a00ff9c10f7f23702add1e4b2ffa31c", + "tag" : "41865fc71de12b19612127ce49993bb0", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 75, + "comment" : "", + "key" : "aabc063474e65c4c3e9bdc480dea97b45110c8618846ff6b15bdd2a4a5682c4e", + "iv" : "46362f45d6379e63e5229460", + "aad" : "a11c40b603767330", + "msg" : "ceb534ce50dc23ff638ace3ef63ab2cc2973eeada80785fc165d06c2f5100ff5e8ab2882c475afcd05ccd49f2e7d8f55ef3a72e3dc51d6852b8e6b9e7aece57be6556b0b6d9413e33fc5fc24a9a205ad59574bb39d944a92dc47970d84a6ad3176", + "ct" : "7545391b51de01d5c53dfaca777909063e58edee4bb1227e7110ac4d2620c2aec2f848f56deeb037a8dced75afa8a6c890e2dee42f950bb33d9e2424d08a505d899563973ed38870f3de6ee2adc7fe072c366c14e2cf7ca62fb3d36bee11685461", + "tag" : "b70d44ef8c66c5c7bbf10dcadd7facf6", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 76, + "comment" : "", + "key" : "d7addd3889fadf8c893eee14ba2b7ea5bf56b449904869615bd05d5f114cf377", + "iv" : "8a3ad26b28cd13ba6504e260", + "aad" : "", + "msg" : "c877a76bf595560772167c6e3bcc705305db9c6fcbeb90f4fea85116038bc53c3fa5b4b4ea0de5cc534fbe1cf9ae44824c6c2c0a5c885bd8c3cdc906f12675737e434b983e1e231a52a275db5fb1a0cac6a07b3b7dcb19482a5d3b06a9317a54826cea6b36fce452fa9b5475e2aaf25499499d8a8932a19eb987c903bd8502fe", + "ct" : "294a764c03353f5f4f6e93cd7e977480d6c343071db0b7c1f0db1e95b85e6053f0423168a9c7533268db9a194e7665359d14489bc47172a9f21370e89b0bd0e5ef9661738de282572bcc3e541247626e57e75dec0f91ac5c530bd1a53271842996dcd04d865321b1ecb6e7630114fe780291b8dc3e5d0abc8e65b1c5493e9af0", + "tag" : "f2b974ca0f14fb9f92014bff18573cff", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 77, + "comment" : "", + "key" : "80be86fb6fc49bc73428cab576f6ad72ff6aca04001b8b1c57a7128be73900aa", + "iv" : "903188433c1ce8971aa19b9d", + "aad" : "0587af8530ad0547", + "msg" : "67ce499cd8ed68bd717dfe61c60f27d260b1c163a72e8cc8597253d3d987c2dbe1bff2e44d9bd4765d3e53d9c3f8eb3b90e751f47c7157bdc1142bc33f5833ac1cd1262cbb239066b334a4ed99ae82c74f2b49540f1a614bc239d8fc5add8c178184e41281f6e66c5c3117fd953547f7c829425b5082aa69686847eaf5784692", + "ct" : "2b90b4f3de280c44913d1984bdd5dfa0566c6a14a058659a9b623277b0bb6e82101e79395d12e643f62d9a822bae497907493e4f8213fcf99da8a78fdf867af36bc8b0931c1886b4f0ae5729986494dbd59737e956cd8f226c7c522689d082f023894d54acab0c4d609f3746a67369bb8876008f7fd3dc6681c5fb9d728c5911", + "tag" : "f005ebe1c1ada75a9cee8d630881d5b8", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 78, + "comment" : "", + "key" : "7d00b48095adfa3272050607b264185002ba99957c498be022770f2ce2f3143c", + "iv" : "87345f1055fd9e2102d50656", + "aad" : "02", + "msg" : "e5ccaa441bc814688f8f6e8f28b500b2", + "ct" : "7e72f5a185af16a611921b438f749f0b", + "tag" : "1242c670732334029adfe1c5001651e4", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 79, + "comment" : "", + "key" : "6432717f1db85e41ac7836bce25185a080d5762b9e2b18444b6ec72c3bd8e4dc", + "iv" : "87a3163ec0598ad95b3aa713", + "aad" : "b648", + "msg" : "02cde168fba3f544bbd0332f7adeada8", + "ct" : "85f29a719557cdd14d1f8fffab6d9e60", + "tag" : "732ca32becd515a1ed353f542e999858", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 80, + "comment" : "", + "key" : "7afa0f59dfcb5ad3a76490c5c804327c8d052be737a60fa8bcbf0a2c36630a43", + "iv" : "25b7bdf4a6dcbf7c9a3ec2b3", + "aad" : "8b71ac", + "msg" : "623e6ba6d3166a338bfcc7af90a230c8", + "ct" : "d46e8265a8c6a25393dd956bb44397ad", + "tag" : "e28f3ad9e3ef4a3d94ee07bf538eaafb", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 81, + "comment" : "", + "key" : "2ec25b0ec7ac244224e9c7fc2fa5d3ef17809e19fd6e954158dd0d72738a4cc8", + "iv" : "6fb0d1417cdfff4df37db08c", + "aad" : "3a5ddf40", + "msg" : "a1c933768a6d573ebf68a99e5e18dae8", + "ct" : "2d3cb2d9303491e264f2904f0e0753f4", + "tag" : "6c1db959362d217b2322b466536bfea0", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 82, + "comment" : "", + "key" : "0a2cf52371cf9d9f95b10108fc82b4fd6110a8ba9a88a26083685ad29826891a", + "iv" : "2538fc67afb9eab333f83290", + "aad" : "9eec540bb0", + "msg" : "0d8c691d044a3978d790432dc71d69f8", + "ct" : "a988c03c71b956ff086d0470d706bd34", + "tag" : "b35d7cbf2beb894b0c746e0730429e15", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 83, + "comment" : "", + "key" : "307e886b38bb18b445f8a2c6d6f8932492a9cea8d041ba72eb5efdfa70d0b8d2", + "iv" : "a071be999151e2a1c41c81e9", + "aad" : "56e014d97c74", + "msg" : "9aba22b495cb7ec887ddaa62019aa14d", + "ct" : "32bf95d4c195dbaf58d9af4001c6e57d", + "tag" : "4393808703d67a90870578046cd8b525", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 84, + "comment" : "", + "key" : "dacd51a8a8e4d5905b4cbb947ef4013eb296889353f3c9ee35f5577b26737a51", + "iv" : "3fa378a1befdddd61ae68cf4", + "aad" : "bb5a3812f0aefd", + "msg" : "e148313883a77da121124d06b1c77dca", + "ct" : "2a207ca7e9da6b13a229604304d87eb1", + "tag" : "8a6b6afec87d93ec6e8dbe13d84c0f8c", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 85, + "comment" : "", + "key" : "7b5fbbb202c16108fd13066446853a850d8b34e9da40519580da446a922f9162", + "iv" : "aa077a5ce9161bde8d8edc40", + "aad" : "f94bb92c1c668a695b", + "msg" : "da471cd6935a0ca8307ddedc6b959962", + "ct" : "548a5ca0ae49211cdf30bbdcb1352d31", + "tag" : "204dacb98f8c8908cc5ea22bb23f901f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 86, + "comment" : "", + "key" : "1ffd101eb97531f6faa821ec4d5c5702725dd033d3b830bb760c4ef27ba983df", + "iv" : "598114e8cf7fbdea8ad29683", + "aad" : "2155627ec15a978fbcb2", + "msg" : "28668ca8db535c7e8eb27491ad0fb7cb", + "ct" : "28cedac24f14caa326c7fe401f68a87c", + "tag" : "2bf1b2c43d3039f8f5ce359c1102f879", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 87, + "comment" : "", + "key" : "d2d0a973d5951af352cbee57ac9dab1c284c99af3b992ce015f219506f64888d", + "iv" : "9acd213570ce9bb9d886c6ef", + "aad" : "37ad668d4d4fe889949763", + "msg" : "3f3f0076250352e1b6b5c12cfa12625e", + "ct" : "7256e856872ad3a54b34a2a6bdca8838", + "tag" : "3b12e4586e45223f78a6eea811efb863", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 88, + "comment" : "", + "key" : "adcc520b381382237d05a6400a7dfbcd0771b6aa9edb7966131ddef6af21f1be", + "iv" : "9183cdf3a8ba7397b6b2d5d5", + "aad" : "b334375415f6215c0bf89a9a", + "msg" : "958295619cf1b36f0b474663c0bc79eb", + "ct" : "852c141b4239a31feeda03550d70a2be", + "tag" : "5fc59287b92d3fcf7d66f13defb11b0d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 89, + "comment" : "", + "key" : "bd534f7adeca466844fb3ba34658be807f15c5291ed6026860a24f179b712c89", + "iv" : "412c3e13ee1f7864bd15ce39", + "aad" : "2866afff0bcc6135dc63af88c8", + "msg" : "d92f8ce5d8d0ad2eb5f11af02ef63949", + "ct" : "89d6d089c4a255952aca11b24a01ff95", + "tag" : "f88fa4531204da315e7317970240ce9e", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 90, + "comment" : "", + "key" : "910ade7d324d2c9688439e1f142e0e5f9d130ff832e507fe1985e5a26452a6d0", + "iv" : "9be090dba93deff27adf99ee", + "aad" : "ea2575f123268e936c8e4c8c1bb8", + "msg" : "6e356094ed9d9a7053c7906c48ba3d9f", + "ct" : "01ffb343c757b27843d8a900a36ce39d", + "tag" : "a315541b7d6313c6fddf64b303d71d60", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 91, + "comment" : "", + "key" : "8e34cf73d245a1082a920b86364eb896c4946467bcb3d58929fcb36690e6394f", + "iv" : "6f573aa86baa492ba46596df", + "aad" : "bd4cd02fc7502bbdbdf6c9a3cbe8f0", + "msg" : "16ddd23ff53f3d23c06334487040eb47", + "ct" : "c1b295936d56fadac03e5f742bff73a1", + "tag" : "39c457dbab66382babb3b55800cda5b8", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 92, + "comment" : "", + "key" : "cb5575f5c7c45c91cf320b139fb594237560d0a3e6f865a67d4f633f2c08f016", + "iv" : "1a6518f02ede1da6809266d9", + "aad" : "89cce9fb47441d07e0245a66fe8b778b", + "msg" : "623b7850c321e2cf0c6fbcc8dfd1aff2", + "ct" : "c84c9bb7c61c1bcb17772a1c500c5095", + "tag" : "dbadf7a5138ca03459a2cd65831e092f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 93, + "comment" : "", + "key" : "a5569e729a69b24ba6e0ff15c4627897436824c941e9d00b2e93fddc4ba77657", + "iv" : "564dee49ab00d240fc1068c3", + "aad" : "d19f2d989095f7ab03a5fde84416e00c0e", + "msg" : "87b3a4d7b26d8d3203a0de1d64ef82e3", + "ct" : "94bc80621ed1e71b1fd2b5c3a15e3568", + "tag" : "333511861796978401598b963722f5b3", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 94, + "comment" : "", + "key" : "56207465b4e48e6d04630f4a42f35cfc163ab289c22a2b4784f6f9290330bee0", + "iv" : "df8713e87ec3dbcfad14d53e", + "aad" : "5e6470facd99c1d81e37cd44015fe19480a2a4d3352a4ff560c0640fdbda", + "msg" : "e601b38557797da2f8a4106a089d1da6", + "ct" : "299b5d3f3d03c087209a16e285143111", + "tag" : "4b454ed198de117e83ec49fa8d8508d6", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 95, + "comment" : "", + "key" : "077433022ab34d380fc192fc24c2edc6301fec6f24442f572a1087ff2e05b39a", + "iv" : "28adcbc74364f26dd4b3108b", + "aad" : "e0100eb116cdc5e22a3b9f9b4126c149595e75107f6e237c69e82960052270", + "msg" : "03c874eeaaa6fa9f0da62c758fb0ad04", + "ct" : "1e9687b35fbc8eaa1825ed3847798f76", + "tag" : "0788bf70fd04030ecd1c96d0bc1fcd5d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 96, + "comment" : "", + "key" : "3937986af86dafc1ba0c4672d8abc46c207062682d9c264ab06d6c5807205130", + "iv" : "8df4b15a888c33286a7b7651", + "aad" : "ba446f6f9a0ced22450feb10737d9007fd69abc19b1d4d9049a5551e86ec2b37", + "msg" : "dc9e9eaf11e314182df6a4eba17aec9c", + "ct" : "605bbf90aeb974f6602bc778056f0dca", + "tag" : "38ea23d99054b46b42ffe004129d2204", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 97, + "comment" : "", + "key" : "36372abcdb78e0279646ac3d176b9674e9154eecf0d5469c651ec7e16b4c1199", + "iv" : "be40e5f1a11817a0a8fa8949", + "aad" : "d41a828d5e71829247021905402ea257dccbc3b80fcd5675056b68bb59e62e8873", + "msg" : "81ce84ede9b35859cc8c49a8f6be7dc6", + "ct" : "7b7ce0d824809a70de32562ccf2c2bbd", + "tag" : "15d44a00ce0d19b4231f921e22bc0a43", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 98, + "comment" : "", + "key" : "9f1479ed097d7fe529c11f2f5add9aaff4a1ca0b68997a2cb7f79749bd90aaf4", + "iv" : "84c87dae4eee27730ec35d12", + "aad" : "3f2dd49bbf09d69a78a3d80ea2566614fc379474196c1aae84583da73d7ff85c6f42ca42056a9792cc1b9fb3c7d261", + "msg" : "a66747c89e857af3a18e2c79500087ed", + "ct" : "ca82bff3e2f310ccc976672c4415e69b", + "tag" : "57638c62a5d85ded774f913c813ea032", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 99, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "00000000000000000000000000000000", + "msg" : "65b63bf074b7283992e24b1ac0df0d22b555dbe2254d94a43f1de748d3cc6f0d", + "ct" : "0000000000000000000000000000000000000000000000000000000000000000", + "tag" : "39f4fce3026d83789ffd1ee6f2cd7c4f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 100, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "00000000000000000000000000000000", + "msg" : "65b63bf074b7283992e24b1ac0df0d22b555dbe2254d94a43f1de748d3cc6f0d20c142fe898fbbe668d4324394434c1b18b58ead710aed9c31db1f2a8a1f1bb2", + "ct" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "tag" : "f5eaa804605c3a4785f9d7f13b6f67d6", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 101, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "00000000000000000000000000000000", + "msg" : "65b63bf074b7283992e24b1ac0df0d22b555dbe2254d94a43f1de748d3cc6f0d20c142fe898fbbe668d4324394434c1b18b58ead710aed9c31db1f2a8a1f1bb24405c183af94ee1ad630cd931158a6213d48c8fff10d0a1f9ef760188e658802aad55e41a1d99069a18db55c56af7c10a6f21ecc8af9b7ce0a7ea0b67426e925", + "ct" : "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "tag" : "9b5c43a78d954e8a3c659eebc13d5d55", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 102, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "ffffffffffffffffffffffffffffffff", + "msg" : "9a49c40f8b48d7c66d1db4e53f20f2dd4aaa241ddab26b5bc0e218b72c3390f2", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "37e3399d9ca696799f08f4f72bc0cdd8", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 103, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "ffffffffffffffffffffffffffffffff", + "msg" : "9a49c40f8b48d7c66d1db4e53f20f2dd4aaa241ddab26b5bc0e218b72c3390f2df3ebd0176704419972bcdbc6bbcb3e4e74a71528ef51263ce24e0d575e0e44d", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "3d52710bec86d4ea9fea2ff269549191", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 104, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "ffffffffffffffffffffffffffffffff", + "msg" : "9a49c40f8b48d7c66d1db4e53f20f2dd4aaa241ddab26b5bc0e218b72c3390f2df3ebd0176704419972bcdbc6bbcb3e4e74a71528ef51263ce24e0d575e0e44dbbfa3e7c506b11e529cf326ceea759dec2b737000ef2f5e061089fe7719a77fd552aa1be5e266f965e724aa3a95083ef590de13375064831f5815f498bd916da", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "51356329e280b12d55d3d98f0a580cbe", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 105, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "00000080000000800000008000000080", + "msg" : "65b63b7074b728b992e24b9ac0df0da2b555db62254d94243f1de7c8d3cc6f8d", + "ct" : "0000008000000080000000800000008000000080000000800000008000000080", + "tag" : "c152a4b90c548c71dc479edeaf9211bf", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 106, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "00000080000000800000008000000080", + "msg" : "65b63b7074b728b992e24b9ac0df0da2b555db62254d94243f1de7c8d3cc6f8d20c1427e898fbb6668d432c394434c9b18b58e2d710aed1c31db1faa8a1f1b32", + "ct" : "00000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080", + "tag" : "40ef6383052d91c2e4b4611b0e32c5ff", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 107, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "00000080000000800000008000000080", + "msg" : "65b63b7074b728b992e24b9ac0df0da2b555db62254d94243f1de7c8d3cc6f8d20c1427e898fbb6668d432c394434c9b18b58e2d710aed1c31db1faa8a1f1b324405c103af94ee9ad630cd131158a6a13d48c87ff10d0a9f9ef760988e658882aad55ec1a1d990e9a18db5dc56af7c90a6f21e4c8af9b74e0a7ea0367426e9a5", + "ct" : "0000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080", + "tag" : "ae9b542541e84fc74542eed6be638fee", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 108, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "80000000800000008000000080000000", + "msg" : "e5b63bf0f4b7283912e24b1a40df0d223555dbe2a54d94a4bf1de74853cc6f0d", + "ct" : "8000000080000000800000008000000080000000800000008000000080000000", + "tag" : "10fee3ecfba9cdf797bae37a626ec83b", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 109, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "80000000800000008000000080000000", + "msg" : "e5b63bf0f4b7283912e24b1a40df0d223555dbe2a54d94a4bf1de74853cc6f0da0c142fe098fbbe6e8d4324314434c1b98b58eadf10aed9cb1db1f2a0a1f1bb2", + "ct" : "80000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000", + "tag" : "7490795bdbbbf5d0aecb9a4f65aa379f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 110, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "80000000800000008000000080000000", + "msg" : "e5b63bf0f4b7283912e24b1a40df0d223555dbe2a54d94a4bf1de74853cc6f0da0c142fe098fbbe6e8d4324314434c1b98b58eadf10aed9cb1db1f2a0a1f1bb2c405c1832f94ee1a5630cd939158a621bd48c8ff710d0a1f1ef760180e6588022ad55e4121d99069218db55cd6af7c1026f21ecc0af9b7ce8a7ea0b6f426e925", + "ct" : "8000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000", + "tag" : "1d1096a8ca9e2bda2762c41d5b16f62f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 111, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "ffffff7fffffff7fffffff7fffffff7f", + "msg" : "9a49c48f8b48d7466d1db4653f20f25d4aaa249ddab26bdbc0e218372c339072", + "ct" : "ffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7f", + "tag" : "af8492c792bf8d8062be74ff6efb3869", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 112, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "ffffff7fffffff7fffffff7fffffff7f", + "msg" : "9a49c48f8b48d7466d1db4653f20f25d4aaa249ddab26bdbc0e218372c339072df3ebd8176704499972bcd3c6bbcb364e74a71d28ef512e3ce24e05575e0e4cd", + "ct" : "ffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7f", + "tag" : "f24db68c46b67d6f402fa6c897913368", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 113, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "ffffff7fffffff7fffffff7fffffff7f", + "msg" : "9a49c48f8b48d7466d1db4653f20f25d4aaa249ddab26bdbc0e218372c339072df3ebd8176704499972bcd3c6bbcb364e74a71d28ef512e3ce24e05575e0e4cdbbfa3efc506b116529cf32eceea7595ec2b737800ef2f56061089f67719a777d552aa13e5e266f165e724a23a950836f590de1b3750648b1f5815fc98bd9165a", + "ct" : "ffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7f", + "tag" : "43f651ab2e2eb0f04bf689a40d32da24", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 114, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "7fffffff7fffffff7fffffff7fffffff", + "msg" : "1a49c40f0b48d7c6ed1db4e5bf20f2ddcaaa241d5ab26b5b40e218b7ac3390f2", + "ct" : "7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff", + "tag" : "60d95294a3694cfaa64b2f63bc1f82ec", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 115, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "7fffffff7fffffff7fffffff7fffffff", + "msg" : "1a49c40f0b48d7c6ed1db4e5bf20f2ddcaaa241d5ab26b5b40e218b7ac3390f25f3ebd01f6704419172bcdbcebbcb3e4674a71520ef512634e24e0d5f5e0e44d", + "ct" : "7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff", + "tag" : "beaca0b47027196176186d944019c1c8", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 116, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "7fffffff7fffffff7fffffff7fffffff", + "msg" : "1a49c40f0b48d7c6ed1db4e5bf20f2ddcaaa241d5ab26b5b40e218b7ac3390f25f3ebd01f6704419172bcdbcebbcb3e4674a71520ef512634e24e0d5f5e0e44d3bfa3e7cd06b11e5a9cf326c6ea759de42b737008ef2f5e0e1089fe7f19a77fdd52aa1bede266f96de724aa3295083efd90de133f506483175815f490bd916da", + "ct" : "7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff", + "tag" : "d4811028a577d4dd69d6b35d717f73e3", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 117, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "00000000ffffffff00000000ffffffff", + "msg" : "65b63bf08b48d7c692e24b1a3f20f2ddb555dbe2dab26b5b3f1de7482c3390f2", + "ct" : "00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff", + "tag" : "10fb61272b555bee104f5a71818716d6", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 118, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "00000000ffffffff00000000ffffffff", + "msg" : "65b63bf08b48d7c692e24b1a3f20f2ddb555dbe2dab26b5b3f1de7482c3390f220c142fe7670441968d432436bbcb3e418b58ead8ef5126331db1f2a75e0e44d", + "ct" : "00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff", + "tag" : "4756764e59583504182877d8c33120f0", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 119, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "00000000ffffffff00000000ffffffff", + "msg" : "65b63bf08b48d7c692e24b1a3f20f2ddb555dbe2dab26b5b3f1de7482c3390f220c142fe7670441968d432436bbcb3e418b58ead8ef5126331db1f2a75e0e44d4405c183506b11e5d630cd93eea759de3d48c8ff0ef2f5e09ef76018719a77fdaad55e415e266f96a18db55ca95083efa6f21ecc750648310a7ea0b68bd916da", + "ct" : "00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff", + "tag" : "95a2b12a4a280089d4bd4f904253e754", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 120, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "ffffffff00000000ffffffff00000000", + "msg" : "9a49c40f74b728396d1db4e5c0df0d224aaa241d254d94a4c0e218b7d3cc6f0d", + "ct" : "ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000", + "tag" : "60dcd45974bebe032eb7b86c9d063452", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 121, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "ffffffff00000000ffffffff00000000", + "msg" : "9a49c40f74b728396d1db4e5c0df0d224aaa241d254d94a4c0e218b7d3cc6f0ddf3ebd01898fbbe6972bcdbc94434c1be74a7152710aed9cce24e0d58a1f1bb2", + "ct" : "ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000", + "tag" : "f0e6a3c1f28ad92d0dbc900be291d877", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 122, + "comment" : "", + "key" : "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "iv" : "000102030405060708090a0b", + "aad" : "ffffffff00000000ffffffff00000000", + "msg" : "9a49c40f74b728396d1db4e5c0df0d224aaa241d254d94a4c0e218b7d3cc6f0ddf3ebd01898fbbe6972bcdbc94434c1be74a7152710aed9cce24e0d58a1f1bb2bbfa3e7caf94ee1a29cf326c1158a621c2b73700f10d0a1f61089fe78e658802552aa1bea1d990695e724aa356af7c10590de1338af9b7cef5815f497426e925", + "ct" : "ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000", + "tag" : "57eff4a525eeff2ebd7a28eb894282be", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 123, + "comment" : "Flipped bit 0 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f5409bb729039d0814ac514054323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 124, + "comment" : "Flipped bit 1 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f6409bb729039d0814ac514054323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 125, + "comment" : "Flipped bit 7 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "74409bb729039d0814ac514054323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 126, + "comment" : "Flipped bit 8 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4419bb729039d0814ac514054323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 127, + "comment" : "Flipped bit 31 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409b3729039d0814ac514054323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 128, + "comment" : "Flipped bit 32 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb728039d0814ac514054323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 129, + "comment" : "Flipped bit 33 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb72b039d0814ac514054323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 130, + "comment" : "Flipped bit 63 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb729039d8814ac514054323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 131, + "comment" : "Flipped bit 64 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb729039d0815ac514054323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 132, + "comment" : "Flipped bit 77 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb729039d08148c514054323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 133, + "comment" : "Flipped bit 80 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb729039d0814ac504054323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 134, + "comment" : "Flipped bit 96 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb729039d0814ac514055323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 135, + "comment" : "Flipped bit 97 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb729039d0814ac514056323f44", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 136, + "comment" : "Flipped bit 120 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb729039d0814ac514054323f45", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 137, + "comment" : "Flipped bit 121 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb729039d0814ac514054323f46", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 138, + "comment" : "Flipped bit 126 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb729039d0814ac514054323f04", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 139, + "comment" : "Flipped bit 127 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb729039d0814ac514054323fc4", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 140, + "comment" : "Flipped bit 63 and 127 in tag expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "f4409bb729039d8814ac514054323fc4", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 141, + "comment" : "Tag changed to all zero expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "00000000000000000000000000000000", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 142, + "comment" : "tag change to all 1 expected tag:f4409bb729039d0814ac514054323f44", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "", + "ct" : "", + "tag" : "ffffffffffffffffffffffffffffffff", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 143, + "comment" : "Flipped bit 0 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "28914007a6119dd3f109bba21ce9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 144, + "comment" : "Flipped bit 1 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "2b914007a6119dd3f109bba21ce9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 145, + "comment" : "Flipped bit 7 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "a9914007a6119dd3f109bba21ce9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 146, + "comment" : "Flipped bit 8 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29904007a6119dd3f109bba21ce9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 147, + "comment" : "Flipped bit 31 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914087a6119dd3f109bba21ce9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 148, + "comment" : "Flipped bit 32 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a7119dd3f109bba21ce9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 149, + "comment" : "Flipped bit 33 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a4119dd3f109bba21ce9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 150, + "comment" : "Flipped bit 63 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a6119d53f109bba21ce9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 151, + "comment" : "Flipped bit 64 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a6119dd3f009bba21ce9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 152, + "comment" : "Flipped bit 77 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a6119dd3f129bba21ce9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 153, + "comment" : "Flipped bit 80 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a6119dd3f109baa21ce9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 154, + "comment" : "Flipped bit 96 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a6119dd3f109bba21de9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 155, + "comment" : "Flipped bit 97 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a6119dd3f109bba21ee9a7d6", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 156, + "comment" : "Flipped bit 120 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a6119dd3f109bba21ce9a7d7", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 157, + "comment" : "Flipped bit 121 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a6119dd3f109bba21ce9a7d4", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 158, + "comment" : "Flipped bit 126 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a6119dd3f109bba21ce9a796", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 159, + "comment" : "Flipped bit 127 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a6119dd3f109bba21ce9a756", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 160, + "comment" : "Flipped bit 63 and 127 in tag expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "29914007a6119d53f109bba21ce9a756", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 161, + "comment" : "Tag changed to all zero expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "00000000000000000000000000000000", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 162, + "comment" : "tag change to all 1 expected tag:29914007a6119dd3f109bba21ce9a7d6", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995a", + "tag" : "ffffffffffffffffffffffffffffffff", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 163, + "comment" : "Flipped bit 0 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "67405a16e8b44eba92aa47f5cea52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 164, + "comment" : "Flipped bit 1 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "64405a16e8b44eba92aa47f5cea52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 165, + "comment" : "Flipped bit 7 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "e6405a16e8b44eba92aa47f5cea52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 166, + "comment" : "Flipped bit 8 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66415a16e8b44eba92aa47f5cea52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 167, + "comment" : "Flipped bit 31 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a96e8b44eba92aa47f5cea52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 168, + "comment" : "Flipped bit 32 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16e9b44eba92aa47f5cea52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 169, + "comment" : "Flipped bit 33 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16eab44eba92aa47f5cea52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 170, + "comment" : "Flipped bit 63 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16e8b44e3a92aa47f5cea52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 171, + "comment" : "Flipped bit 64 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16e8b44eba93aa47f5cea52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 172, + "comment" : "Flipped bit 77 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16e8b44eba928a47f5cea52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 173, + "comment" : "Flipped bit 80 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16e8b44eba92aa46f5cea52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 174, + "comment" : "Flipped bit 96 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16e8b44eba92aa47f5cfa52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 175, + "comment" : "Flipped bit 97 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16e8b44eba92aa47f5cca52b7a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 176, + "comment" : "Flipped bit 120 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16e8b44eba92aa47f5cea52b7b", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 177, + "comment" : "Flipped bit 121 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16e8b44eba92aa47f5cea52b78", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 178, + "comment" : "Flipped bit 126 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16e8b44eba92aa47f5cea52b3a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 179, + "comment" : "Flipped bit 127 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16e8b44eba92aa47f5cea52bfa", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 180, + "comment" : "Flipped bit 63 and 127 in tag expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "66405a16e8b44e3a92aa47f5cea52bfa", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 181, + "comment" : "Tag changed to all zero expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "00000000000000000000000000000000", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 182, + "comment" : "tag change to all 1 expected tag:66405a16e8b44eba92aa47f5cea52b7a", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b", + "aad" : "000102", + "msg" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "ct" : "d03bcb3ca52d48d1d203b1e7b1a5995af1a0466a61bb386a2e12d189a2c4ea15e9", + "tag" : "ffffffffffffffffffffffffffffffff", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 183, + "comment" : "edge case for poly1305 key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "dc8ce708bf26aab862d97e1b42f31ef38c382cf07174142ea564920612997b1c2e38aca2438b588d5459493e97e7fa330ff9bc3b9458297ba0967d86ed090b435103478f2869b93ee29c837e95fb6b9903f3b735b7345428eb93b3db1d9b5187cebb889aa177d83e4f63fc9a5c0596eed939883d06aacdfdea44fdecdf5cb7fc", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "c296436246c3a7c4b3ba09ab2a6a0889", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 184, + "comment" : "edge case for poly1305 key:278de313ffffffdfffe9acbf3ea59357c4e16a5bc120d346af4a8cf694a84374", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "0001020304050607051e9373", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "931227274a89d0b3aade7fac62c96262c1e77b8dafd248f10ad37c6ccb69cb7131b041593c8bb8c3db38f39dd8a124c424fce4389dede1d3cb9d46cf95970aea9856b6e313d756197baf4fcb58df275bca8a2188f9e8a1ad04354ede542ddc30e8b735b2f5905f5811799282be94ae842ec126c55d2e667235e9acf1d48798f0", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "99a3b0fff6fdcbcce9dc5820f2a64861", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 185, + "comment" : "edge case for poly1305 key:0050799fe9e74fcffcffffcfd21aa8b5cb5aa2c6ab347b6886eedaca4bfff3c0", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "0001020304050607048c3c5f", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "0df91f31230e8941e700a752fef08c897c511ed618fdf8a378a1f439013b40a48d4634c27d9ada7c0bb6f3fa92e341425903d7ecd0c49bee4c77e84b11f1c721922308642885b813fae364da32eaf120d6a43a74fb1632443667bfea6eef1be73eb1c3c0b5a57cee8dc4feed4a1fb9ae02f7b1695588c3c878451cb6ee0cb3dc", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "eaff8f47ef9268fd0d94e8a9c4b78d24", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 186, + "comment" : "edge case for poly1305 key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "1fde9b9ec8b247d42bbee2016d6715ba428a85431430eada56a2c5dc944b6aa6cef0b056a2eecc51d30838e640615e1458e0943e30f91ba41b4362fa9ed6037b21d14da7b4f76f9f68fa8903138d563ce2590af1201c7cfec2290cfce98a822ebb8d1ed9dc4e20d241755aff91cdfd10fdb69efa0d5c8082692601cbfbb955c7", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "86ed21fda080a7d13981078d86b3e3cd", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 187, + "comment" : "edge case for poly1305 key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "66115e67ecd3d4178c4c60e713ab4e5e66f8d1f971da17437a2b5e04fbca1671e847139a5f4e3f8e92d7a3b71eb4ff0e50354c0c1580af3662d5f8151e3f7e8264a0085c32ddfcbeb01a8be4c34d53319800ac4ef9d4e4014524bc7cd3387242e774f4d1a7a0521e42ec44844d0bd8b9d73fec959212fd7e8eacf4d984996d9b", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "34f9e0faa515eee0e784e6ef2678befa", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 188, + "comment" : "edge case for poly1305 key:0000003059ffce96438a246ff9536787d92bc40eafa0241a2972780ef6ca1ef8", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060726c6961b", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "e97244259af5a379238da0cad2a5f493655ec0e5024fd553bbb3deb66a94036d106c3d513407b2dd1cc5936c4c9c1e4f4b37b54dec261c601dc99e90680e23e2dc5c9a8d503d8bea49a8cdca3706bfd2a3daa0afb19a70fd3d355fc37c13f3f9e5c8d0864a5f80a780b36d4698ec2ce9ccc27b97ecbe672e41628ebd773acb81", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "3c94b9fe60bdb35c6b7b73b765083492", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 189, + "comment" : "edge case for poly1305 key:3fa0ea9c030000a036217d42e775ad189b96e24ee591952e2922ff151334b9ec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "0001020304050607013da060", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "9453aa159c3d87f17e21e88adabc37e553b904d00eefc66b8e0905e23576fbdc9c7bea9777f3b8368481932534b3344d309e6307cddfe7b3549300dd9cda7efe9d43c8a115912a392904079ee92bcd33099f7022ea94c1e7353b89bfc54de3ceb56f529a1a608bb5a970e1359609d1f56806b37f8605f4c27451da6066fc557a", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "2b11cf9f8db8490d409fc62afd7379f3", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 190, + "comment" : "edge case for poly1305 key:a556cb502baf395b020000f03c5108fb1cf76df1b8a8f724e877bd3c588d3285", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060707db33de", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "2e1836640d810c2709fb83ccf1aef3a971085d1bbfb58a425abf75ccec70b3abde0e80539e83a82546e7372a19481547053308dd7842675e9c4f61302426da0d71c1da3102031030ed928152be009b15b52f71b5911991d39f68a8658d99729df2bbef31c8989f9604558df9f2aba4b3766c58aaef3548de545ec1f080225a88", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "c9c8366920f88381407712cec61e6607", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 191, + "comment" : "edge case for poly1305 key:0c327fbcc564555545d4fe75020000d0a65799f363ec51b1c5c427b4a04af190", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060702a11942", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "0ecb4d85c956b5268c9b35a8c63b4e9d3e5cb72b64ef98773841b947bd7d59ef7d0eb0e1c050d49a5424ce7deb527d76087e4746674c958965df32d9e5fb03b46501706128d481217aaeae2f78f9259273358a2954cac0bc2fbfe77447d1d387b9314c6541b69f1270b3438b1042b2b4663e62ba4d49c07ac6f163034afa80af", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "2373cfa2ab24446ad5a236167b8027fe", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 192, + "comment" : "edge case for poly1305 key:415f08302f210340240d0e903e2b01205ba43e106aebd7e2481016b31118b1ae", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506073c0df637", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "2e8e45e903bfab32f2f0d49d9a3e449bef6f4093e2722cdab2cf935c1822b830fb5a4056516d560dfc8638c9a57d2927200a56f0b67153271d498e8f08dc888c61ef634f7ae40f4608f96f92fea5a1e5bd45131120098dc5de0378e58f2ddb46fa4aa5adb38fe006bb19b69146382f77a79e06214def547cfb5ce37a7008b9b6", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "5f93946478d8081e7247f414ad39a515", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 193, + "comment" : "edge case for poly1305 key:feffff1ff6b87403fd6435b09775bc92491a0ae62c5842a30e3b82710cc2dad1", + "key" : "9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf", + "iv" : "00000000101112130bc672c3", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "3619cb470af86dceceb6940f2d9abb34c9a9131476053387445ffebbe240d4f9818377855652f46a8219c7f71c3554f8acef8258de4b7d17c0f3d353ac981cc6a13287be1e6b41dc6d133df4ababebdf43d665ce7a4a5c982a0b139cb8202eebc74173e3224a440e4c37d2b595f384290e939ba016df0d49b36cdb4bd91c39", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "133fe62391744d11ce44594b96c53baf", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 194, + "comment" : "edge case for poly1305 key:bf358f18ffffffbf4b62ed6e1f53790785c4dabdfc72e2a219d377a682c85f38", + "key" : "9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf", + "iv" : "000000001011121303e9b9a4", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "af205bda819f7451be0f28667d4b01b59ff2daa8173cab52046c3c9e0d989889c5e021ef7afd06e9ce6cc30e3a6ebab509134ba10d10e570c55587c13eee53e73be54804c8539ffbf23b35922b1ca37b9e9bc24ee204837ca5a294ce05d12600c7eff6aee32270db2feff47dc5a04176169e15850628e6035f78994f9f5603", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "e3451adb9d23a7710a1aafba26f56387", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 195, + "comment" : "edge case for poly1305 key:d0b7b3a352a4010ffeffffbfe8cc66dc6e5e7451dc61762c5753174fed88e746", + "key" : "9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf", + "iv" : "00000000101112130700b982", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "68c67272036fb652a0182eeb4781358e4704a4a702fd731bf3b3ea994717989e7d9104e0ae81732a8c7e9a82b3d31d541761a366b67c3396f1a6c67e293ddb65a59e42541dda144dc6c78388cfca982e23350958ac5b3d54a1722fd64733577862e1879c9e9445ebdec5315d1706db7ebbedd4c779935e72057e5b0ecde081", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "b0bb8a55ff5f52a5043c6e7795847557", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 196, + "comment" : "edge case for poly1305 key:7bee33931a4157a8cb701becfeffff4fbe7e69f19cd065313bb49a252628dd3d", + "key" : "9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf", + "iv" : "0000000010111213019836bb", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "c483b7334ebe2e879b0c3f9db4fcd9f5219062360d6ce44cdae0f94e04c8345ea7e3ae33855118741dcafe0de4ae98c4e43af7b12b04ee8ab175625823ac040e5abac4403f1d45238adcb8c0cf44bd56917f9f5d93974c82b56951986a9c0450bd9047b5a616e814526ad0580e3ecd8189c9fef2cdb979a22ad3a01930fbd1", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "f4fc25f4c5543a9afee9819e2904fb68", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 197, + "comment" : "edge case for poly1305 key:7cb5fbdffb40ff5f3c7de74f655ffc1fac03013a7fe468440b861ebe0ab1650a", + "key" : "9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf", + "iv" : "00000000101112131d59f288", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "bc7f4f15fd1e4c1399740836670abe39a05707be19956ce169b32321759e0f213ae19ad34aa612b3a29f02c4bbac9f785a55a3adfe419ab891bbe0acee9921322ea21002c9dd3dcdd13a7f8554dddc10f9b529ce94be7050937dab76557b7eb17c685aad8f0797e39d62553988989aab1d9764fe431cc1d4c595062ce93ce9", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "5e67a7b8733e0e4b01ac2178a205ae7e", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 198, + "comment" : "edge case for poly1305 key:00000090e6e328c242cde5c83e3d8262d467f2bcd53d3755c781f3c6a2cb0648", + "key" : "9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf", + "iv" : "00000000101112130552a411", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "eaccaa778935ef249e0900149dd889462d2a061486ba102b8caebe465f3959fb3119ebb5689676ffdd6d851a26739e772b54a2f5f473ea9c7e58ccbc4cfc953e8c420b2175d9dd519265630bb79bd87a601b113231a8b16ce54c331347ec04c2b1c9160f38207aa46e96feb06dee883eb422fa14908df300bb1a1ef758c408", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "177a77fce114a4349c4f8d5ec825d06f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 199, + "comment" : "edge case for poly1305 key:9e98d64e000000505a07183c5c68c63c14c9266dd37ff86aafc22ddbdb355617", + "key" : "9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf", + "iv" : "00000000101112130c807a72", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "a76c330e015060a17e64cb7b6d753f201f75be8759fd7539fb92b22aef54c9d3029dba0c15cbf7c95135888319c6b2e6276da21e0c351fd522b29aabb5883a3291d6f427de773b124390ef6fd96621ffbc42dfbf7a34da272cbc9ccb1a498d078033d1ac3bf7e92715948b06d69d5c5039e9164ba9c3a02219ec5908206b3b", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "623c7d4424f5497aedfd1339cf8cecce", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 200, + "comment" : "edge case for poly1305 key:1048a92e65f5e63102000080d9ae08de4319a7c45fdbe707b9ec1b7e0d635161", + "key" : "9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf", + "iv" : "00000000101112130397a143", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "228a7e15bcce13051de9145f77f7f4ff7921828b4f99efc4ff55ee0d9344955b69ec2d4798b0517f0273c4456ae5ffc5929cbe74ddb0da51d4f2b4df7578a31240c88ae922c3c5eca7b97d72d497062050a587447c562b343d5c71921944872f9fd06b8f34b3eb5d4341f5ff8a907dd7c2e1676b81252726ba54814da51eab", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "1c18b69354b189731a1a83fe8f0d57c9", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 201, + "comment" : "edge case for poly1305 key:01517a2ceb89bbfb5741f7d9000000401a65b132ad661072a00ffe7defbb18a5", + "key" : "9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf", + "iv" : "000000001011121308cb0f3f", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "c7d843188ab193dfef5c4daf583f952cd4b195f240fa2e704d021723023c123371a41e87dfc6e6c3874a42f331cf035988a38c72ba2da854b1208f98bf8cc29948169481ab3a402d5fcc7ff78f9e31925576dc3938074b8c5b27960e3afc750ad686563688b7441787288d5256c1301d563b7744843bd1ab4eff5be6f1653d", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "2045815b8211b9a2995effe0b8ed9868", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 202, + "comment" : "edge case for poly1305 key:bc90156087e0125006d90c30babd0590427bff19de1f2e7d0757a79528731138", + "key" : "9de836aa579585081f330a7c4036e20e38ef15eff3945184d231867f505fffdf", + "iv" : "00000000101112130d8fcf4e", + "aad" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "msg" : "cfc3db8631c81c69023a3c8a9ad66c35053685144c4fa2a9510add72e211dad9ca5b982e4c194591fdb74116280311d1299ad81227258cb52f079bbcb12aff161d278dec33a326d71276b3de01a8327ee7f45f94179dff18a3fe643e56c30cfd03871c8110ab00f6612b9e17a4647360d7847bb63a3122613c2e7cdddd08ae", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "1ae2ed84ea9774d78d782bf8d972a8b8", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 203, + "comment" : "edge case for tag", + "key" : "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", + "iv" : "000102030405060708090a0b", + "aad" : "ffffffffffffffffffffffffffffffff415771fda4fbcc55c377f73203e60226", + "msg" : "e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "000102030405060708090a0b0c0d0e0f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 204, + "comment" : "edge case for tag", + "key" : "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", + "iv" : "000102030405060708090a0b", + "aad" : "f1ffffffffffffffffffffffffffffff615af39eddb5fcd2519190d5507d3b06", + "msg" : "e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "00000000000000000000000000000000", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 205, + "comment" : "edge case for tag", + "key" : "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", + "iv" : "000102030405060708090a0b", + "aad" : "b5ffffffffffffffffffffffffffffff764e5d82ce7da0d44148484fd96a6107", + "msg" : "e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "ffffffffffffffffffffffffffffffff", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 206, + "comment" : "edge case for tag", + "key" : "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", + "iv" : "000102030405060708090a0b", + "aad" : "fdffffffffffffffffffffffffffffff2bdbf16d8ea4d39dab8dcb3d4bc4e104", + "msg" : "e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "00000080000000800000008000000080", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 207, + "comment" : "edge case for tag", + "key" : "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", + "iv" : "000102030405060708090a0b", + "aad" : "a9ffffffffffffffffffffffffffffffaccd5eb31d8fc909e84b0de7de23bb08", + "msg" : "e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "ffffff7fffffff7fffffff7fffffff7f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 208, + "comment" : "edge case for tag", + "key" : "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", + "iv" : "000102030405060708090a0b", + "aad" : "d2ffffffffffffffffffffffffffffffdd4b933e7b1a7ed93cc7c050db71dc03", + "msg" : "e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "01000000010000000100000001000000", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 209, + "comment" : "edge case for tag", + "key" : "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", + "iv" : "000102030405060708090a0b", + "aad" : "ffffffffffffffffffffffffffffffffa08164425d7642e9e90fc8d5c32d2cf6", + "msg" : "e48caf8a76183327c9561a4651c07c822ccd1642c06607d0d4bc0afb4de15915dbfa3b0b422e77e15c64bf6247031f15fdb643117809821870000adf83834da5", + "ct" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "tag" : "ffffffff000000000000000000000000", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 210, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "c68ce708bf26aab862d97e1b42f31ef37bb66f8090c149e452ec7f20327eb2ea2e38aca2438b588d5459493e97e7fa330ff9bc23c897df6b00af86931d6c81555103478f2869b93ee29c837e95fb6b9903f3b72debfba2384baa48ceedfedb91", + "ct" : "e5ffffffffffffffffffffffffffffff0871bc8f1e4aa235087712d9df183609ffffffffffffffffffffffffffffffffffffffe7a33009ef5fc604ea0f9a75e9ffffffffffffffffffffffffffffffffffffffe7a33009ef5fc604ea0f9a75e9", + "tag" : "3572162777262c518eef573b720e8e64", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 211, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "c78ce708bf26aab862d97e1b42f31ef376209eef141691fba5d10eaf581affe62e38aca2438b588d5459493e97e7fa330e73d2dc3bbd954989cb8433b7d6597b5103478f2869b93ee29c837e95fb6b990279d9d218d1e81ac2ce4a6e474403bf", + "ct" : "e4ffffffffffffffffffffffffffffff05e74de09a9d7a2aff4a6356b57c7b05fffffffffffffffffffffffffffffffffe759118501a43cdd6a2064aa520adc7fffffffffffffffffffffffffffffffffe759118501a43cdd6a2064aa520adc7", + "tag" : "347216375f5b7b5c4e6bff4912fd9473", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 212, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "fc8ce708bf26aab862d97e1b42f31ef38b79403dfaabc0d8c18d23a3469c13e62e38aca2438b588d5459493e97e7fa330a4b941e6b66fcc2ed7d8cb3e8cc7ffc5103478f2869b93ee29c837e95fb6b9906419f10480a8191a67842ee185e2538", + "ct" : "dffffffffffffffffffffffffffffffff8be933274202b099b164e5aabfa9705fffffffffffffffffffffffffffffffffa4dd7da00c12a46b2140ecafa3a8b40fffffffffffffffffffffffffffffffffa4dd7da00c12a46b2140ecafa3a8b40", + "tag" : "30721677ff2eb8894e5a9d8492b7b0af", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 213, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "fa8ce708bf26aab862d97e1b42f31ef39bcbb8da477d580d772de4229bba7de22938aca2438b588d5459493e97e7fa331e9dedf9dd64a0681bac2969549425bc5603478f2869b93ee29c837e95fb6b991297e6f7fe08dd3b50a9e734a4067f78", + "ct" : "d9ffffffffffffffffffffffffffffffe80c6bd5c9f6b3dc2db689db76dcf901f8ffffffffffffffffffffffffffffffee9bae3db6c376ec44c5ab104662d100f8ffffffffffffffffffffffffffffffee9bae3db6c376ec44c5ab104662d100", + "tag" : "2b7216c7873744c20ec5e2cdb260d3fa", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 214, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "ee8ce708bf26aab862d97e1b42f31ef3b9f55bd56e0fd74b46063a96354cfbee3238aca2438b588d5459493e97e7fa3320c78886a6f6292d6cc5fbddb546a2b04d03478f2869b93ee29c837e95fb6b992ccd8388859a547e27c0358045d4f874", + "ct" : "cdffffffffffffffffffffffffffffffca3288dae0843c9a1c9d576fd82a7f0de3ffffffffffffffffffffffffffffffd0c1cb42cd51ffa933ac79a4a7b0560ce3ffffffffffffffffffffffffffffffd0c1cb42cd51ffa933ac79a4a7b0560c", + "tag" : "22721657b0130d28cf1ec65153c41182", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 215, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "ef8ce708bf26aab862d97e1b42f31ef3b46fca24d353ff5e49eac51540e840ea3038aca2438b588d5459493e97e7fa333d311e572202011a75e948586fe268b44f03478f2869b93ee29c837e95fb6b99313b1559016e7c493eec86059f703270", + "ct" : "ccffffffffffffffffffffffffffffffc7a8192b5dd8148f1371a8ecad8ec409e1ffffffffffffffffffffffffffffffcd375d9349a5d79e2a80ca217d149c08e1ffffffffffffffffffffffffffffffcd375d9349a5d79e2a80ca217d149c08", + "tag" : "2172166798485c338f9a6d60f3b21891", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 216, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "f59d56151de28bef83505f6d89c0b0f7f75b2fa8e6dce386075db283ec85ee62555baffad423af25f66069bb69fb6f4d", + "ct" : "d6ee4ee25d3bdea81e76de8934cc51fb849cfca7685708575dc6df7a01e36a81849cfca7685708575dc6df7a01e36a81", + "tag" : "831312cbb0f165dc3e8ff52125f48640", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 217, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "f717f8d5b28032d5c8e8061cd44d71e4f2d55de772fe7a91ce85e410db3e2d8d50d5ddb5400136323fb83f285e40aca2", + "ct" : "d464e022f259679255ce87f8694190e881128ee8fc759140941e89e93658a96e81128ee8fc759140941e89e93658a96e", + "tag" : "821312db9826b5e7fe0a9d30c5e28d4f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 218, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "f28ce708bf26aab862d97e1b42f31ef3e68a922c9219d30f07554d7d99f2bde92c38aca2438b588d5459493e97e7fa33e24c07dd98f9b253ab0c318d9b14f6b15303478f2869b93ee29c837e95fb6b99ee460cd3bb95cf00e009ffd06b86ac75", + "ct" : "d1ffffffffffffffffffffffffffffff954d41231c9238de5dce20847494390afdffffffffffffffffffffffffffffff124a4419f35e64d7f465b3f489e2020dfdffffffffffffffffffffffffffffff124a4419f35e64d7f465b3f489e2020d", + "tag" : "c1045769d487d545cef3f0d34b7a8733", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 219, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "dc8ce708bf26aab862d97e1b42f31ef32e6784d857df07543d0dc72f179935fbede8c8baf01ee2044b162cbb343b355acc29d82327cd93f2bfd918034ed5c42a", + "ct" : "ffffffffffffffffffffffffffffffff5da057d7d954ec856796aad6faffb1183c2f9be74c6a4576e0b09a7a5c2330963c2f9be74c6a4576e0b09a7a5c233096", + "tag" : "64e7efd24516a83e2c87e06a76e2dea3", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 220, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "f78ce708bf26aab862d97e1b42f31ef34c6ead26f84a0225d557745d32fc72e72c38aca2438b588d5459493e97e7fa3364db334b69bee579383e61ae742c71bb5303478f2869b93ee29c837e95fb6b9968d138454ad2982a733baff384be2b7f", + "ct" : "d4ffffffffffffffffffffffffffffff3fa97e2976c1e9f48fcc19a4df9af604fdffffffffffffffffffffffffffffff94dd708f021933fd6757e3d766da8507fdffffffffffffffffffffffffffffff94dd708f021933fd6757e3d766da8507", + "tag" : "e6cc6729d79ba558cd73b03cba54d660", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 221, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "f08ce708bf26aab862d97e1b42f31ef34fd8c3757c9f2938dc3b07d85898bfe22a38aca2438b588d5459493e97e7fa336155412415cbdd760142b62c2ec83fbf5503478f2869b93ee29c837e95fb6b996d5f4a2a36a7a0254a477871de5a657b", + "ct" : "d3ffffffffffffffffffffffffffffff3c1f107af214c2e986a06a21b5fe3b01fbffffffffffffffffffffffffffffff915302e07e6c0bf25e2b34553c3ecb03fbffffffffffffffffffffffffffffff915302e07e6c0bf25e2b34553c3ecb03", + "tag" : "e5cc6739bfd0f4638def574b5a43dd6f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 222, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "f28ce708bf26aab862d97e1b42f31ef3df03ca84082f7f70ad8e4004cabd2ce42b38aca2438b588d5459493e97e7fa3328fd413caab1d02bf1c65753aa2ad3b95403478f2869b93ee29c837e95fb6b9924f74a3289ddad78bac3990e5ab8897d", + "ct" : "d1ffffffffffffffffffffffffffffffacc4198b86a494a1f7152dfd27dba807faffffffffffffffffffffffffffffffd8fb02f8c11606afaeafd52ab8dc2705faffffffffffffffffffffffffffffffd8fb02f8c11606afaeafd52ab8dc2705", + "tag" : "0fca702228817d53ee64d142b192e665", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 223, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "f38ce708bf26aab862d97e1b42f31ef31ffc31ae69399394b8c338674c3dfde92938aca2438b588d5459493e97e7fa33477ec8cf3ea3d4d5d76d85ad2b7f0bb85603478f2869b93ee29c837e95fb6b994b74c3c11dcfa9869c684bf0dbed517c", + "ct" : "d0ffffffffffffffffffffffffffffff6c3be2a1e7b27845e258559ea15b790af8ffffffffffffffffffffffffffffffb7788b0b55040251880407d43989ff04f8ffffffffffffffffffffffffffffffb7788b0b55040251880407d43989ff04", + "tag" : "efc3b035ded6b460bfce6f494955e677", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 224, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "2bfd0d56ece98771756d60d9d9106cd0c6fc106936c7ef347c078fd71c54228164fc903b0438a3978d3a54ef992aa3ae", + "ct" : "088e15a1ac30d236e84be13d641c8ddcb53bc366b84c04e5269ce22ef132a662b53bc366b84c04e5269ce22ef132a662", + "tag" : "345fc9fe573c136c1be83730500ce662", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 225, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "f68ce708bf26aab862d97e1b42f31ef37cc2255decdf8e0fe1373591da0e28e42838aca2438b588d5459493e97e7fa33e291fb4838019c51dfb7141515bb53b15703478f2869b93ee29c837e95fb6b99ee9bf0461b6de10294b2da48e5290975", + "ct" : "d5ffffffffffffffffffffffffffffff0f05f652625465debbac58683768ac07f9ffffffffffffffffffffffffffffff1297b88c53a64ad580de966c074da70df9ffffffffffffffffffffffffffffff1297b88c53a64ad580de966c074da70d", + "tag" : "336f97a5faa995a2a03781b591588da8", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 226, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "c68ce708bf26aab862d97e1b42f31ef37ab66f8090c149e452ec7f20327eb2ea0438aca2438b588d5459493e97e7fa338d2613ea0ef8b656b247373ecec015bc7b03478f2869b93ee29c837e95fb6b99812c18e42d94cb05f942f9633e524f78", + "ct" : "e5ffffffffffffffffffffffffffffff0971bc8f1e4aa235087712d9df183609d5ffffffffffffffffffffffffffffff7d20502e655f60d2ed2eb547dc36e100d5ffffffffffffffffffffffffffffff7d20502e655f60d2ed2eb547dc36e100", + "tag" : "9351c680c8a5d34882d42145e89745c4", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 227, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "c68ce708bf26aab862d97e1b42f31ef374b66f8090c149e452ec7f20327eb2ea2e38aca2438b588d5459493e97e7fa33acd9ec859e0866620cc24c8a97d5d9f55103478f2869b93ee29c837e95fb6b99a0d3e78bbd641b3147c782d767478331", + "ct" : "e5ffffffffffffffffffffffffffffff0771bc8f1e4aa235087712d9df183609ffffffffffffffffffffffffffffffff5cdfaf41f5afb0e653abcef385232d49ffffffffffffffffffffffffffffffff5cdfaf41f5afb0e653abcef385232d49", + "tag" : "d79266cd25a784599a0a8e31fc84d604", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 228, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "f78ce708bf26aab862d97e1b42f31ef34251cd29b0aaa960557c9ea2828334e4e4e231db0a27fac9ec9e744886eb0133c5232142ddf48b3f185140f0fc05f043", + "ct" : "d4ffffffffffffffffffffffffffffff31961e263e2142b10fe7f35b6fe5b00735256286b6535dbb4738c289eef304ff35256286b6535dbb4738c289eef304ff", + "tag" : "9d671d407d7660459d5d582d83915efe", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 229, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "f58ce708bf26aab862d97e1b42f31ef373bd9f01bf3331b12e31dd14cf11feee1d38aca2438b588d5459493e97e7fa33625c6965f61a1c36118c747076d5b7b76203478f2869b93ee29c837e95fb6b996e56626bd57661655a89ba2d8647ed73", + "ct" : "d6ffffffffffffffffffffffffffffff007a4c0e31b8da6074aab0ed22777a0dccffffffffffffffffffffffffffffff925a2aa19dbdcab24ee5f6096423430bccffffffffffffffffffffffffffffff925a2aa19dbdcab24ee5f6096423430b", + "tag" : "7b207c2c3278c64f0d6b913fe371fe63", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 230, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "dc8ce708bf26aab862d97e1b42f31ef3ec0933f0bfb91218cea0d74e061f559e2d38aca2438b588d5459493e97e7fa338d5b67e0acee534ce2d9791487b1ecb25203478f2869b93ee29c837e95fb6b9981516cee8f822e1fa9dcb7497723b676", + "ct" : "ffffffffffffffffffffffffffffffff9fcee0ff3132f9c9943bbab7eb79d17dfcffffffffffffffffffffffffffffff7d5d2424c74985c8bdb0fb6d9547180efcffffffffffffffffffffffffffffff7d5d2424c74985c8bdb0fb6d9547180e", + "tag" : "3672162bb1f3ff537ece013f1aca4f68", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 231, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "dc8ce708bf26aab862d97e1b42f31ef3ee83a14f48db696291080edfcc898b882b38aca2438b588d5459493e97e7fa338ad5f6b0283a8b39ebedce92785da9b65403478f2869b93ee29c837e95fb6b9986dffdbe0b56f66aa0e800cf88cff372", + "ct" : "ffffffffffffffffffffffffffffffff9d447240c65082b3cb93632621ef0f6bfaffffffffffffffffffffffffffffff7ad3b574439d5dbdb4844ceb6aab5d0afaffffffffffffffffffffffffffffff7ad3b574439d5dbdb4844ceb6aab5d0a", + "tag" : "3572163b99284f5f3e4aa94dbab85677", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 232, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "dc8ce708bf26aab862d97e1b42f31ef3e87dd08ed4e4e04c5877616cbb02cabb2938aca2438b588d5459493e97e7fa33874f0401d457e336f4311f1152f957ba5603478f2869b93ee29c837e95fb6b998b450f0ff73b9e65bf34d14ca26b0d7e", + "ct" : "ffffffffffffffffffffffffffffffff9bba03815a6f0b9d02ec0c9556644e58f8ffffffffffffffffffffffffffffff774947c5bff035b2ab589d68400fa306f8ffffffffffffffffffffffffffffff774947c5bff035b2ab589d68400fa306", + "tag" : "3472164b815d9e6afec5505c5aa75d86", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 233, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "c88ce708bf26aab862d97e1b42f31ef36be436e346f8f2b32f4cbbaef95150ef0438aca2438b588d5459493e97e7fa332fb76b5132e930f6d0acf70875e977b57b03478f2869b93ee29c837e95fb6b9923bd605f11854da59ba93955857b2d71", + "ct" : "ebffffffffffffffffffffffffffffff1823e5ecc873196275d7d6571437d40cd5ffffffffffffffffffffffffffffffdfb12895594ee6728fc57571671f8309d5ffffffffffffffffffffffffffffffdfb12895594ee6728fc57571671f8309", + "tag" : "3a7216d7ee1da018ce8412f251656b19", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 234, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "c58ce708bf26aab862d97e1b42f31ef3783cf9302c7d22914b38aca2e7d374ef1d38aca2438b588d5459493e97e7fa33228f2d23597640d574f8e20c4f6b6bb56203478f2869b93ee29c837e95fb6b992e85262d7a1a3d863ffd2c51bff93171", + "ct" : "e6ffffffffffffffffffffffffffffff0bfb2a3fa2f6c94011a3c15b0ab5f00cccffffffffffffffffffffffffffffffd2896ee732d196512b9160755d9d9f09ccffffffffffffffffffffffffffffffd2896ee732d196512b9160755d9d9f09", + "tag" : "367216178ff1dc45ce73b02cd21f8755", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 235, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "dc8ce708bf26aab862d97e1b42f31ef35db72f89d1402b1a0373ff0a9c5cd44b6d67af40798f5455501792953248ec234ca6bfd9ae5c25a3a4d8a62d48a61d53", + "ct" : "ffffffffffffffffffffffffffffffff2e70fc865fcbc0cb59e892f3713a50a8bca0fc1dc5fbf327fbb124545a50e9efbca0fc1dc5fbf327fbb124545a50e9ef", + "tag" : "0b4961c9525ea2f2cdad6273e1c7824c", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 236, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "dc8ce708bf26aab862d97e1b42f31ef35f215ec87d62a264cadb519b4ac90a7668d1dd03e56eda6399ac7803e7dd22114910cd9a32bdab956d634cbb9d33d361", + "ct" : "ffffffffffffffffffffffffffffffff2ce68dc7f3e949b590403c62a7af8e95b9168e5e591a7d11320acec28fc527ddb9168e5e591a7d11320acec28fc527dd", + "tag" : "0a4961d93a93f1fd8d290a8281b6895b", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 237, + "comment" : "edge case intermediate sums in poly1305. poly_key:ffffffefeb344f6bc37ba77ea2ee06dfe8c7f4ae10810422124fc5e1bd7fe301", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060710abb165", + "aad" : "ffffffff", + "msg" : "dc8ce708bf26aab862d97e1b42f31ef3d15ad590dd0f40ba18acd168f6ac777a0f38aca2438b588d5459493e97e7fa33932a097f1d39a04ad30f1b6c650260bf7003478f2869b93ee29c837e95fb6b999f2002713e55dd19980ad53195903a7b", + "ct" : "ffffffffffffffffffffffffffffffffa29d069f5384ab6b4237bc911bcaf399deffffffffffffffffffffffffffffff632c4abb769e76ce8c66991577f49403deffffffffffffffffffffffffffffff632c4abb769e76ce8c66991577f49403", + "tag" : "3572161355240943de9406292a64c551", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 238, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "40115e67ecd3d4178c4c60e713ab4e5e390ef93aeb61aa307f141323c38e0685fa47139a5f4e3f8e92d7a3b71eb4ff0e259445f4ffc31bce540190edd6ad207876a0085c32ddfcbeb01a8be4c34d5331eda1a5b6139750f973f0d4841baa2cb8", + "ct" : "d9ffffffffffffffffffffffffffffffa009d73c6544428cfac0b2d8c7bbef0bedffffffffffffffffffffffffffffff8a5ef60715bc4b07c92b9707376da105edffffffffffffffffffffffffffffff8a5ef60715bc4b07c92b9707376da105", + "tag" : "19532d9fa0b5fbd582aaeda830602f1d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 239, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "49115e67ecd3d4178c4c60e713ab4e5ee02b87aeae8c3da8895f8cb0f6b9cc80f447139a5f4e3f8e92d7a3b71eb4ff0ecc4b7b803a5f8f4647df169080fe567a78a0085c32ddfcbeb01a8be4c34d5331047e9bc2d60bc471602e52f94df95aba", + "ct" : "d0ffffffffffffffffffffffffffffff792ca9a820a9d5140c8b2d4bf28c250ee3ffffffffffffffffffffffffffffff6381c873d020df8fdaf5117a613ed707e3ffffffffffffffffffffffffffffff6381c873d020df8fdaf5117a613ed707", + "tag" : "adbd2cafc8c8f0e51250e7b81c9d0a2d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 240, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "43eadae036f733ea9b5b7eb22aee395db6f51a4d10bc2460810c229651556acf384ad82e3e280cad69f0df25b42b83b0", + "ct" : "da047b7825db1802e8e8e1aac6ba88fc2ff2344b9e99ccdc04d8836d556083412ff2344b9e99ccdc04d8836d55608341", + "tag" : "973e270a7afcab75348e14dbe19c5156", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 241, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "66115e67ecd3d4178c4c60e713ab4e5e891b797521ba925b24090aaf6c4482bae847139a5f4e3f8e92d7a3b71eb4ff0e6d50c32d05a946cb8cea57c9f1442cb164a0085c32ddfcbeb01a8be4c34d5331a565236fe9fd0dfcab1b13a03c432071", + "ct" : "ffffffffffffffffffffffffffffffff101c5773af9f7ae7a1ddab5468716b34ffffffffffffffffffffffffffffffffc29a70deefd6160211c050231084adccffffffffffffffffffffffffffffffffc29a70deefd6160211c050231084adcc", + "tag" : "e17c273f31758e752322ae4869c1bfbb", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 242, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "6a115e67ecd3d4178c4c60e713ab4e5e519cccebf72573dbee8c12f74255d18c0add1035861ffc0b7f40079b969f8c63b2af4fa3ccd16cb38f425c3996140def", + "ct" : "f3ffffffffffffffffffffffffffffffc89be2ed79009b676b58b30c466038021d65fc5026ae3c7a12685bd377d48c921d65fc5026ae3c7a12685bd377d48c92", + "tag" : "a22390224c5db0f01696743d870725c5", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 243, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "e235b8c21384557085c3f2eb2a8fa36058cffd2af743dacf96b4ae4d51b4e488d6703f49d9d7f2027e4853feb4ca0df7", + "ct" : "7bdb195a00a87e98f6706df3c6db12c1c1c8d32c7966327313600fb655810d06c1c8d32c7966327313600fb655810d06", + "tag" : "437d1efad21b0865a541b5cab62e2a44", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 244, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "66115e67ecd3d4178c4c60e713ab4e5e8fab58574a322bac6f394474e4ce7eaec347139a5f4e3f8e92d7a3b71eb4ff0e71532dfb0e9141b00983394722829e7c4fa0085c32ddfcbeb01a8be4c34d5331b966cdb9e2c50a872e727d2eef8592bc", + "ct" : "ffffffffffffffffffffffffffffffff16ac7651c417c310eaede58fe0fb9720d4ffffffffffffffffffffffffffffffde999e08e4ee117994a93eadc3421f01d4ffffffffffffffffffffffffffffffde999e08e4ee117994a93eadc3421f01", + "tag" : "acf4ffa20c0d06d61a18e9a8d4c84d1d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 245, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "61115e67ecd3d4178c4c60e713ab4e5e5efe679ba17384c55eb8cc193666fe8d04608c3503d217aa3f90a9b0e1b3b313bc12d3a3491c8712cf92f212e138329f", + "ct" : "f8ffffffffffffffffffffffffffffffc7f9499d2f566c79db6c6de23253170313d86050a363d7db52b8f5f800f8b3e213d86050a363d7db52b8f5f800f8b3e2", + "tag" : "cd466d06e75b7fd18d5fe21d9227d9a7", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 246, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "9064b88a282052a1ee44df05ad213da679f8d1f971da17437a2b5e04fbca167151b2650ec945fec70588bc65a616a5f24f354c0c1580af3662d5f8151e3f7e82dd557ec8a4d63df7274594367bef09cd", + "ct" : "098a19123b0c79499df7401d41758c07e0ffffffffffffffffffffffffffffff460a896b69f43eb668a0e02d475da503e0ffffffffffffffffffffffffffffff460a896b69f43eb668a0e02d475da503", + "tag" : "ce8a3d4d887d95613d829b538ed01196", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 247, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "43115e67ecd3d4178c4c60e713ab4e5eeef67bd4795b74015a3493905d544a86e847139a5f4e3f8e92d7a3b71eb4ff0e3197be28eff843592bd8fc8d578421d664a0085c32ddfcbeb01a8be4c34d5331f9a25e6a03ac086e0c29b8e49a832d16", + "ct" : "daffffffffffffffffffffffffffffff77f155d2f77e9cbddfe0326b5961a308ffffffffffffffffffffffffffffffff9e5d0ddb05871390b6f2fb67b644a0abffffffffffffffffffffffffffffffff9e5d0ddb05871390b6f2fb67b644a0ab", + "tag" : "08289f5199df476fe90475cb95225566", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 248, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "6b115e67ecd3d4178c4c60e713ab4e5e1e34412ab0a056e809d5d4b92be1128a4b2a651a62aeab26cf437fb195407574f3583a8c28603b9e3f41241395cbf4f8", + "ct" : "f2ffffffffffffffffffffffffffffff87336f2c3e85be548c0175422fd4fb045c92897fc21f6b57a26b23f9740b75855c92897fc21f6b57a26b23f9740b7585", + "tag" : "06df93f651ea5cc56911f30d3e58f997", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 249, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "3fe606108f35869df4c7aa0128464a1265f8d1f971da17437a2b5e04fbca1671fdbe843a0ad9be25055992ab6dcbc9f153354c0c1580af3662d5f8151e3f7e8271599ffc674a7d152794baf8b03265ce", + "ct" : "a608a7889c19ad7587743519c412fbb3fcffffffffffffffffffffffffffffffea06685faa687e546871cee38c80c900fcffffffffffffffffffffffffffffffea06685faa687e546871cee38c80c900", + "tag" : "9264fc0f47febb30661254daf9a06189", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 250, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "6e8eb98cf7fffe4cd683568cf892991564f8d1f971da17437a2b5e04fbca1671c70f5d8b30c64bf2e6d1d613f40e0bf052354c0c1580af3662d5f8151e3f7e824be8464d5d5588c2c41cfe4029f7a7cf", + "ct" : "f7601814e4d3d5a4a530c99414c628b4fdffffffffffffffffffffffffffffffd0b7b1ee90778b838bf98a5b15450b01fdffffffffffffffffffffffffffffffd0b7b1ee90778b838bf98a5b15450b01", + "tag" : "69a124fc7f96e220d1a031ced5527279", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 251, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "4f115e67ecd3d4178c4c60e713ab4e5e4156269fe3da101eeb0abf8dda20fe8fff47139a5f4e3f8e92d7a3b71eb4ff0e6aece983e64f97e43ff5295bc884fa7773a0085c32ddfcbeb01a8be4c34d5331a2d909c10a1bdcd318046d320583f6b7", + "ct" : "d6ffffffffffffffffffffffffffffffd85108996dfff8a26ede1e76de151701e8ffffffffffffffffffffffffffffffc5265a700c30c72da2df2eb129447b0ae8ffffffffffffffffffffffffffffffc5265a700c30c72da2df2eb129447b0a", + "tag" : "3ea8f9b2012321e63d5fb5bc2c5d332d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 252, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "66115e67ecd3d4178c4c60e713ab4e5e18f125ef374c1454b680e23427e7dc69e447139a5f4e3f8e92d7a3b71eb4ff0e858b08eb1d581570a7cd1e48593b757568a0085c32ddfcbeb01a8be4c34d53314dbee8a9f10c5e47803c5a21943c79b5", + "ct" : "ffffffffffffffffffffffffffffffff81f60be9b969fce8335443cf23d235e7f3ffffffffffffffffffffffffffffff2a41bb18f72745b93ae719a2b8fbf408f3ffffffffffffffffffffffffffffff2a41bb18f72745b93ae719a2b8fbf408", + "tag" : "dfaf8a3a15d45e7f4c3430048d8589f0", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 253, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "b02ab747a310d6a3bbdb97018a3be8b341f8d1f971da17437a2b5e04fbca1671b7a338bc3423895f0fd96cdb27a787f277354c0c1580af3662d5f8151e3f7e823b44237a59b04a6f2d144488fa5e2bcd", + "ct" : "29c416dfb03cfd4bc8680819666f5912d8ffffffffffffffffffffffffffffffa01bd4d99492492e62f13093c6ec8703d8ffffffffffffffffffffffffffffffa01bd4d99492492e62f13093c6ec8703", + "tag" : "3408eb2b13a9b76befcedf699422d61f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 254, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "40115e67ecd3d4178c4c60e713ab4e5e380ef93aeb61aa307f141323c38e0685f647139a5f4e3f8e92d7a3b71eb4ff0e3f769a30e8951ff2fb365fa780fdde7e7aa0085c32ddfcbeb01a8be4c34d5331f7437a7204c154c5dcc71bce4dfad2be", + "ct" : "d9ffffffffffffffffffffffffffffffa109d73c6544428cfac0b2d8c7bbef0be1ffffffffffffffffffffffffffffff90bc29c302ea4f3b661c584d613d5f03e1ffffffffffffffffffffffffffffff90bc29c302ea4f3b661c584d613d5f03", + "tag" : "09f4f2a3936d7461a67ce022176bb8dd", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 255, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "40115e67ecd3d4178c4c60e713ab4e5e060ef93aeb61aa307f141323c38e0685ee47139a5f4e3f8e92d7a3b71eb4ff0e2bca70bfcdf1171ab611d12bed5d627a62a0085c32ddfcbeb01a8be4c34d5331e3ff90fd21a55c2d91e09542205a6eba", + "ct" : "d9ffffffffffffffffffffffffffffff9f09d73c6544428cfac0b2d8c7bbef0bf9ffffffffffffffffffffffffffffff8400c34c278e47d32b3bd6c10c9de307f9ffffffffffffffffffffffffffffff8400c34c278e47d32b3bd6c10c9de307", + "tag" : "2eb2679aadfd824a5fd8fa2e4a55a65c", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 256, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "56115e67ecd3d4178c4c60e713ab4e5e6c7e1312c6774fae7d1e5d0cc609028ff547139a5f4e3f8e92d7a3b71eb4ff0e81c9e61cbeeed5546b1ce5d8fef21a7a79a0085c32ddfcbeb01a8be4c34d533149fc065e52ba9e634ceda1b133f516ba", + "ct" : "cffffffffffffffffffffffffffffffff5793d144852a712f8cafcf7c23ceb01e2ffffffffffffffffffffffffffffff2e0355ef5491859df636e2321f329b07e2ffffffffffffffffffffffffffffff2e0355ef5491859df636e2321f329b07", + "tag" : "5e89349f6b011cd6e24ee6ac2f590c21", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 257, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "2ea8410b4dca8c9d5369a033d8db61e46cf8d1f971da17437a2b5e04fbca1671f0f58e8bba6cf1a52146273d8fe0c4fc5a354c0c1580af3662d5f8151e3f7e827c12954dd7ff3295038b0f6e521968c3", + "ct" : "b746e0935ee6a77520da3f2b348fd045f5ffffffffffffffffffffffffffffffe74d62ee1add31d44c6e7b756eabc40df5ffffffffffffffffffffffffffffffe74d62ee1add31d44c6e7b756eabc40d", + "tag" : "b24537fcb0dcb6200b0285cafc9c3a7d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 258, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "17059a7c8883a28b90bd94ae44d1543662f8d1f971da17437a2b5e04fbca1671a23018bf8e68e413e99ac2d4ab3f8df154354c0c1580af3662d5f8151e3f7e822ed70379e3fb2723cb57ea8776c621ce", + "ct" : "8eeb3be49baf8963e30e0bb6a885e597fbffffffffffffffffffffffffffffffb588f4da2ed9246284b29e9c4a748d00fbffffffffffffffffffffffffffffffb588f4da2ed9246284b29e9c4a748d00", + "tag" : "43300400ea36e720361153ce0c5d637d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 259, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "aaa1b258fd4b54b497b520806a66d7aa68f8d1f971da17437a2b5e04fbca167199132a234a8c789bf8544547940ec3f35e354c0c1580af3662d5f8151e3f7e8215f431e5271fbbabda996d1449f76fcc", + "ct" : "334f13c0ee677f5ce406bf988632660bf1ffffffffffffffffffffffffffffff8eabc646ea3db8ea957c190f7545c302f1ffffffffffffffffffffffffffffff8eabc646ea3db8ea957c190f7545c302", + "tag" : "d79a0310124adc30c6b64cdef8993e8d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 260, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "4c115e67ecd3d4178c4c60e713ab4e5ebb5357ed314ad740b9910fad6f01d781f047139a5f4e3f8e92d7a3b71eb4ff0ec8042b414fdd1bba3a6c936b7ed678797ca0085c32ddfcbeb01a8be4c34d53310031cb03a389508d1d9dd702b3d174b9", + "ct" : "d5ffffffffffffffffffffffffffffff225479ebbf6f3ffc3c45ae566b343e0fe7ffffffffffffffffffffffffffffff67ce98b2a5a24b73a74694819f16f904e7ffffffffffffffffffffffffffffff67ce98b2a5a24b73a74694819f16f904", + "tag" : "e6022cc3ba20e3f9065fdfcc43a9dc40", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 261, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "66115e67ecd3d4178c4c60e713ab4e5ef64296975af7fced168181f76c6508e1c947139a5f4e3f8e92d7a3b71eb4ff0e4975060f7ddef4a098699333b30fbf7c45a0085c32ddfcbeb01a8be4c34d53318140e64d918abf97bf98d75a7e08b3bc", + "ct" : "ffffffffffffffffffffffffffffffff6f45b891d4d214519355200c6850e16fdeffffffffffffffffffffffffffffffe6bfb5fc97a1a469054394d952cf3e01deffffffffffffffffffffffffffffffe6bfb5fc97a1a469054394d952cf3e01", + "tag" : "353e304fd8553286b26e0d59942fe7cd", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 262, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "9841cfc927a57dc491ab35427ff935e66ef8d1f971da17437a2b5e04fbca1671a683c8f9f9e6780fda4940ddedd76bf258354c0c1580af3662d5f8151e3f7e822a64d33f9475bb3ff884688e302ec7cd", + "ct" : "01af6e513489562ce218aa5a93ad8447f7ffffffffffffffffffffffffffffffb13b249c5957b87eb7611c950c9c6b03f7ffffffffffffffffffffffffffffffb13b249c5957b87eb7611c950c9c6b03", + "tag" : "0aeb04ecf7def40c42025bbae5509169", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 263, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "42115e67ecd3d4178c4c60e713ab4e5e0b61bf9b7caf83cc34da625593514289e847139a5f4e3f8e92d7a3b71eb4ff0e696a5c7fb9da9cd4a39c8591086db42d64a0085c32ddfcbeb01a8be4c34d5331a15fbc3d558ed7e3846dc1f8c56ab8ed", + "ct" : "dbffffffffffffffffffffffffffffff9266919df28a6b70b10ec3ae9764ab07ffffffffffffffffffffffffffffffffc6a0ef8c53a5cc1d3eb6827be9ad3550ffffffffffffffffffffffffffffffffc6a0ef8c53a5cc1d3eb6827be9ad3550", + "tag" : "8fc4f77a6ee052a4c314780b8df9a2d0", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 264, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "4b115e67ecd3d4178c4c60e713ab4e5ef28e4d0f20ca1644470c9cdac6000887ed47139a5f4e3f8e92d7a3b71eb4ff0e1464775bacd5c69fe26e1a74968ea27e61a0085c32ddfcbeb01a8be4c34d5331dc51971940818da8c59f5e1d5b89aebe", + "ct" : "d2ffffffffffffffffffffffffffffff6b896309aeeffef8c2d83d21c235e109faffffffffffffffffffffffffffffffbbaec4a846aa96567f441d9e774e2303faffffffffffffffffffffffffffffffbbaec4a846aa96567f441d9e774e2303", + "tag" : "232ff78a96f347b453ba711b79367ee0", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 265, + "comment" : "edge case intermediate sums in poly1305. poly_key:946aff9f2a13f56f92a5f9cfee3cdb1fef6d98d5a55ab563cb28620cd57f19d2", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "00010203040506072dd4cd40", + "aad" : "ffffffff", + "msg" : "4d115e67ecd3d4178c4c60e713ab4e5e6ee628fc4b5830184cd293364a213e84fe47139a5f4e3f8e92d7a3b71eb4ff0e29db953ad5458fea61f013ea1854fe7572a0085c32ddfcbeb01a8be4c34d5331e1ee75783911c4dd46015783d553f2b5", + "ct" : "d4fffffffffffffffffffffffffffffff7e106fac57dd8a4c90632cd4e14d70ae9ffffffffffffffffffffffffffffff861126c93f3adf23fcda1400f9947f08e9ffffffffffffffffffffffffffffff861126c93f3adf23fcda1400f9947f08", + "tag" : "e00d2e8bae5d09c28e9bf59409545d09", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 266, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "19de9b9ec8b247d42bbee2016d6715babc286fd979807951b183a188930ad15edcf0b056a2eecc51d30838e640615e14890e659fd3028c904e65018fdfd6038333d14da7b4f76f9f68fa8903138d563c33b7fb50c3e7ebca970f6f89a88a82d6", + "ct" : "f9ffffffffffffffffffffffffffffff015d1565924f6c7418de9babf8be4407edffffffffffffffffffffffffffffff2e110e5e1c0468cbaad99c8abeffff07edffffffffffffffffffffffffffffff2e110e5e1c0468cbaad99c8abeffff07", + "tag" : "47e5d4294239db73b836c04070ff5b2d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 267, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "1fde9b9ec8b247d42bbee2016d6715ba839f811ad0310c77052f45320b0d9560c4f0b056a2eecc51d30838e640615e1470d6b14fd209fedf261fd1d250d3478d2bd14da7b4f76f9f68fa8903138d563cca6f2f80c2ec9985ff75bfd4278fc6d8", + "ct" : "ffffffffffffffffffffffffffffffff3eeafba63bfe1952ac727f1160b90039f5ffffffffffffffffffffffffffffffd7c9da8e1d0f1a84c2a34cd731fabb09f5ffffffffffffffffffffffffffffffd7c9da8e1d0f1a84c2a34cd731fabb09", + "tag" : "232c882f7a1a2f808ccf26496cff5b3d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 268, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "97311cd6e2d25a7b4eaa16f0a61ca6246b8a85431430eada56a2c5dc944b6aa695136310b6b6b5c17c9f8c02ba7d0aeb71e0943e30f91ba41b4362fa9ed6037b7a329ee1a0af160fc76d3de7e99102c3", + "ct" : "771078b7d59fe2509aeb0b0e34844c61d6ffffffffffffffffffffffffffffffa41c2cb9eba7866f50684b1b05e3ab00d6ffffffffffffffffffffffffffffffa41c2cb9eba7866f50684b1b05e3ab00", + "tag" : "d71bc70d5adc74e7dfd89406fc15f044", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 269, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "34de9b9ec8b247d42bbee2016d6715ba74cf7e9d82b7e8ed9ec965f6ea310951dc104940e08a4222556828eba459f65a4a006d28729d95d79d2372f77aeeab35", + "ct" : "d4ffffffffffffffffffffffffffffffc9ba04216978fdc837945fd581859c08ed1f06e9bd9b718c799feff21bc757b1ed1f06e9bd9b718c799feff21bc757b1", + "tag" : "21e63987d494673f3040ae9de2bc0da0", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 270, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "e72b83514e5e50509070359c1cac7e1c428a85431430eada56a2c5dc944b6aa6dad35950d8a9b55a472f9bb8860a526358e0943e30f91ba41b4362fa9ed6037b35f2a4a1ceb01694fcdd2a5dd5e65a4b", + "ct" : "070ae7307913e87b443128628e349459ffffffffffffffffffffffffffffffffebdc16f985b886f46bd85ca13994f388ffffffffffffffffffffffffffffffffebdc16f985b886f46bd85ca13994f388", + "tag" : "e4fb945d6a2d0b947834317cc415f024", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 271, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "8c6165f445443588041b6e044fb6baae728a85431430eada56a2c5dc944b6aa6881a54c09516a1f1cae7b9dd71130ee168e0943e30f91ba41b4362fa9ed6037b673ba931830f023f7115083822ff06c9", + "ct" : "6c40019572098da3d05a73fadd2e50ebcfffffffffffffffffffffffffffffffb9151b69c807925fe6107ec4ce8daf0acfffffffffffffffffffffffffffffffb9151b69c807925fe6107ec4ce8daf0a", + "tag" : "c0424863a20e5fa04ccd9784c015f034", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 272, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "18e36174545fa7ec9ea9f05d7057c5ca638a85431430eada56a2c5dc944b6aa6434e1c5e71005b690ca5cb8d580b89ed79e0943e30f91ba41b4362fa9ed6037bac6fe1af6719f8a7b7577a680be781c5", + "ct" : "f8c2051563121fc74ae8eda3e2cf2f8fdeffffffffffffffffffffffffffffff724153f72c1168c720520c94e7952806deffffffffffffffffffffffffffffff724153f72c1168c720520c94e7952806", + "tag" : "aa7293ffe5db30a31f2581e0e7ae56ed", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 273, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "12de9b9ec8b247d42bbee2016d6715ba54305dff6b61c40b775c352d025c1a56d7f0b056a2eecc51d30838e640615e14bce574e9e11afedbdca021e53bb9188338d14da7b4f76f9f68fa8903138d563c065cea26f1ff998105ca4fe34ce599d6", + "ct" : "f2ffffffffffffffffffffffffffffffe945274380aed12ede010f0e69e88f0fe6ffffffffffffffffffffffffffffff1bfa1f282e1c1a80381cbce05a90e407e6ffffffffffffffffffffffffffffff1bfa1f282e1c1a80381cbce05a90e407", + "tag" : "42e5d43d1e808e79f017144d4498c235", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 274, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "1fde9b9ec8b247d42bbee2016d6715badf0599194b0ce890cc1d8eb383b57f38dcf0b056a2eecc51d30838e640615e1435df81077d068077ce805ea592f6f88833d14da7b4f76f9f68fa8903138d563c8f661fc86de3e72d17ea30a3e5aa79dd", + "ct" : "ffffffffffffffffffffffffffffffff6270e3a5a0c3fdb56540b490e801ea61edffffffffffffffffffffffffffffff92c0eac6b200642c2a3cc3a0f3df040cedffffffffffffffffffffffffffffff92c0eac6b200642c2a3cc3a0f3df040c", + "tag" : "6cf2f9230af8679e7ecb19421362fce3", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 275, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "39de9b9ec8b247d42bbee2016d6715ba4092e1f9a22c8b18184d805c128ade57c7f0b056a2eecc51d30838e640615e1464fe8b9bdd215a620973affefe93398528d14da7b4f76f9f68fa8903138d563cde471554cdc43d38d019c1f889cfb8d0", + "ct" : "d9fffffffffffffffffffffffffffffffde79b4549e39e3db110ba7f793e4b0ef6ffffffffffffffffffffffffffffffc3e1e05a1227be39edcf32fb9fbac501f6ffffffffffffffffffffffffffffffc3e1e05a1227be39edcf32fb9fbac501", + "tag" : "6d46d2230a9848d518f9d94bb2c49caa", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 276, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "12de9b9ec8b247d42bbee2016d6715ba327f3a1befb4287c17450391ed0eb854d6f0b056a2eecc51d30838e640615e141460d3545c29ddc790711b8e7533698539d14da7b4f76f9f68fa8903138d563caed94d9b4cccba9d491b7588026fe8d0", + "ct" : "f2ffffffffffffffffffffffffffffff8f0a40a7047b3d59be1839b286ba2d0de7ffffffffffffffffffffffffffffffb37fb895932f399c74cd868b141a9501e7ffffffffffffffffffffffffffffffb37fb895932f399c74cd868b141a9501", + "tag" : "74dda12e0558877bc0e40c3eace0af29", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 277, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "1bde9b9ec8b247d42bbee2016d6715ba85b67664ee49fa347fbfd2dd92007c57def0b056a2eecc51d30838e640615e14fb27ee075b3c0f0f682babdde63dad8731d14da7b4f76f9f68fa8903138d563c419e70c84bd96855b141c5db91612cd2", + "ct" : "fbffffffffffffffffffffffffffffff38c30cd80586ef11d6e2e8fef9b4e90eefffffffffffffffffffffffffffffff5c3885c6943aeb548c9736d887145103efffffffffffffffffffffffffffffff5c3885c6943aeb548c9736d887145103", + "tag" : "502455343d39db87947d7346a8e0af39", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 278, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "36de9b9ec8b247d42bbee2016d6715ba1132811b2f18321ba99b12432c7f865aa3352cd2d7ac70b4c6f5419767926e20352508ba45bba7410ebe1b8bb925334f", + "ct" : "d6ffffffffffffffffffffffffffffffac47fba7c4d7273e00c6286047cb1303923a637b8abd431aea02868ed80ccfcb923a637b8abd431aea02868ed80ccfcb", + "tag" : "14fba149d1c0edc8aa665851126b5afd", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 279, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "1fde9b9ec8b247d42bbee2016d6715baf999461058f6d7733e5cd0d1639d9025cbf0b056a2eecc51d30838e640615e14520a0da50439db00e289e1791342068e24d14da7b4f76f9f68fa8903138d563ce8b3936a14dcbc5a3be38f7f641e87db", + "ct" : "ffffffffffffffffffffffffffffffff44ec3cacb339c2569701eaf20829057cfafffffffffffffffffffffffffffffff5156664cb3f3f5b06357c7c726bfa0afafffffffffffffffffffffffffffffff5156664cb3f3f5b06357c7c726bfa0a", + "tag" : "bf7fbd422cbf0e700fd1605be8fd212f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 280, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "15de9b9ec8b247d42bbee2016d6715bacc1629a40cd11eafdf04138b45afe458eff0b056a2eecc51d30838e640615e14340ac9b45a5896a418a8cee8032e078f00d14da7b4f76f9f68fa8903138d563c8eb3577b4abdf1fec1c2a0ee747286da", + "ct" : "f5ffffffffffffffffffffffffffffff71635318e71e0b8a765929a82e1b7101deffffffffffffffffffffffffffffff9315a275955e72fffc1453ed6207fb0bdeffffffffffffffffffffffffffffff9315a275955e72fffc1453ed6207fb0b", + "tag" : "c6f23204865b0adde0070037d6538dd3", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 281, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "31de9b9ec8b247d42bbee2016d6715baff746ef53ec3357cbc3c3ce4ab1d2d51ed9eb456dc9d9b59f656a5d2d974d26a7b8e903e4e8a4cac3e1dffce07c38f05", + "ct" : "d1ffffffffffffffffffffffffffffff42011449d50c2059156106c7c0a9b808dc91fbff818ca8f7daa162cb66ea7381dc91fbff818ca8f7daa162cb66ea7381", + "tag" : "8cff61b7b3919ed6bde72b36e0d31326", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 282, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "19de9b9ec8b247d42bbee2016d6715babf286fd979807951b183a188930ad15ecef0b056a2eecc51d30838e640615e1464413d71939b9cb0a4d32ef115da9e1021d14da7b4f76f9f68fa8903138d563cdef8a3be837efbea7db940f762861f45", + "ct" : "f9ffffffffffffffffffffffffffffff025d1565924f6c7418de9babf8be4407ffffffffffffffffffffffffffffffffc35e56b05c9d78eb406fb3f474f36294ffffffffffffffffffffffffffffffffc35e56b05c9d78eb406fb3f474f36294", + "tag" : "369cf17011cae47539e2723f010cf980", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 283, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "19de9b9ec8b247d42bbee2016d6715babd286fd979807951b183a188930ad15ee3f0b056a2eecc51d30838e640615e14f25e78fe1b53ae416d1fbc698522618f0cd14da7b4f76f9f68fa8903138d563c48e7e6310bb6c91bb475d26ff27ee0da", + "ct" : "f9ffffffffffffffffffffffffffffff005d1565924f6c7418de9babf8be4407d2ffffffffffffffffffffffffffffff5541133fd4554a1a89a3216ce40b9d0bd2ffffffffffffffffffffffffffffff5541133fd4554a1a89a3216ce40b9d0b", + "tag" : "532eb8e272a8d171378b0d42dff2bed9", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 284, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "32de9b9ec8b247d42bbee2016d6715ba258d5d3e441683f546beba2e23755f5ccef0b056a2eecc51d30838e640615e149d13fdf8fa899836fa5c410d4ccd25ea21d14da7b4f76f9f68fa8903138d563c27aa6337ea6cff6c23362f0b3b91a4bf", + "ct" : "d2ffffffffffffffffffffffffffffff98f82782afd996d0efe3800d48c1ca05ffffffffffffffffffffffffffffffff3a0c9639358f7c6d1ee0dc082de4d96effffffffffffffffffffffffffffffff3a0c9639358f7c6d1ee0dc082de4d96e", + "tag" : "d1be7426cd12446fe52e8d45331e0835", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 285, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "1fde9b9ec8b247d42bbee2016d6715bad64add2aa3c5a30a31d9e65e90f93ad1cbf0b056a2eecc51d30838e640615e14de9aeab86144d5464811b2373ba4cc8324d14da7b4f76f9f68fa8903138d563c6423747771a1b21c917bdc314cf84dd6", + "ct" : "ffffffffffffffffffffffffffffffff6b3fa796480ab62f9884dc7dfb4daf88faffffffffffffffffffffffffffffff79858179ae42311dacad2f325a8d3007faffffffffffffffffffffffffffffff79858179ae42311dacad2f325a8d3007", + "tag" : "62630c18de8c10876adb9f30f300963f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 286, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "1fde9b9ec8b247d42bbee2016d6715bacc3492272b8a4b112a4e7d7ccf092692cef0b056a2eecc51d30838e640615e1430ce678e9375b2af0b82c2d2fbd7928c21d14da7b4f76f9f68fa8903138d563c8a77f9418390d5f5d2e8acd48c8b13d9", + "ct" : "ffffffffffffffffffffffffffffffff7141e89bc0455e348313475fa4bdb3cbffffffffffffffffffffffffffffffff97d10c4f5c7356f4ef3e5fd79afe6e08ffffffffffffffffffffffffffffffff97d10c4f5c7356f4ef3e5fd79afe6e08", + "tag" : "feb6412b9031f076eddcd9426fff5b31", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 287, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "34de9b9ec8b247d42bbee2016d6715ba722b6549c9df0f4b04b5f7432203fa54cef0b056a2eecc51d30838e640615e1487de186cd28e43544c73de628fd1d60e21d14da7b4f76f9f68fa8903138d563c3d6786a3c26b240e9519b064f88d575b", + "ct" : "d4ffffffffffffffffffffffffffffffcf5e1ff522101a6eade8cd6049b76f0dffffffffffffffffffffffffffffffff20c173ad1d88a70fa8cf4367eef82a8affffffffffffffffffffffffffffffff20c173ad1d88a70fa8cf4367eef82a8a", + "tag" : "dafdf430c8124483c175404b6bff5b41", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 288, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "3dde9b9ec8b247d42bbee2016d6715bac5629699cfd4d9036cef478ed705be5650f575882c3800f757ea6e0f8c6d47acc6e551e0be2fd7029fa1341352da1ac3", + "ct" : "ddffffffffffffffffffffffffffffff7817ec25241bcc26c5b27dadbcb12b0f61fa3a21712933597b1da91633f3e64761fa3a21712933597b1da91633f3e647", + "tag" : "f8800c5b6283dddfc41f935c01bd0d24", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 289, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "1fde9b9ec8b247d42bbee2016d6715ba66d624f288f52941ca24865ce96f0d9736ff33a27c23f4976fc74f1fcd82f5cca0ef17caee342362a78c15031335a8a3", + "ct" : "ffffffffffffffffffffffffffffffffdba35e4e633a3c646379bc7f82db98ce07f07c0b2132c73943308806721c542707f07c0b2132c73943308806721c5427", + "tag" : "38bfb8318c627d86c34bab1f1ebd0db0", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 290, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "f4ebbe3fca96bc4885b35582c43e0eb3588a85431430eada56a2c5dc944b6aa6b4570e8446e886bcbff82a24f49be5ed42e0943e30f91ba41b4362fa9ed6037b5b76f37550f12572040a9bc1a777edc5", + "ct" : "14cada5efddb046351f2487c56a6e4f6e5ffffffffffffffffffffffffffffff8558412d1bf9b512930fed3d4b054406e5ffffffffffffffffffffffffffffff8558412d1bf9b512930fed3d4b054406", + "tag" : "af7293eb09957d9de7432dd41316f0e4", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 291, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "1ade9b9ec8b247d42bbee2016d6715ba571a3fca3cda7def4c93d4a382ca3a57eaf0b056a2eecc51d30838e640615e1476cddbee2f185776174f6df3bbe5b38105d14da7b4f76f9f68fa8903138d563ccc7445213ffd302cce2503f5ccb932d4", + "ct" : "faffffffffffffffffffffffffffffffea6f4576d71568cae5ceee80e97eaf0edbffffffffffffffffffffffffffffffd1d2b02fe01eb32df3f3f0f6dacc4f05dbffffffffffffffffffffffffffffffd1d2b02fe01eb32df3f3f0f6dacc4f05", + "tag" : "e178b0d5eb9bc551fa645c49f9f17667", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 292, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "1fde9b9ec8b247d42bbee2016d6715babe31a501536a7c91e4a102cc27cdfe09d2f0b056a2eecc51d30838e640615e14dd9416a12e2f81bdee023d462feef7833dd14da7b4f76f9f68fa8903138d563c672d886e3ecae6e73768534058b276d6", + "ct" : "ffffffffffffffffffffffffffffffff0344dfbdb8a569b44dfc38ef4c796b50e3ffffffffffffffffffffffffffffff7a8b7d60e12965e60abea0434ec70b07e3ffffffffffffffffffffffffffffff7a8b7d60e12965e60abea0434ec70b07", + "tag" : "bdbf63db237d195ecefdc251f5f17677", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 293, + "comment" : "edge case intermediate sums in poly1305. poly_key:dc46b3c53be153ccd4986678ffffffafe484c316c93f64195da65a2742fd3fec", + "key" : "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "iv" : "000102030405060703e76f6f", + "aad" : "ffffffff", + "msg" : "3ede9b9ec8b247d42bbee2016d6715ba8567a7fde812a3aa2f552a33c1718c58e2f0b056a2eecc51d30838e640615e14bb8729fd148f23b2a916b7f40f2f29810dd14da7b4f76f9f68fa8903138d563c013eb732046a44e8707cd9f27873a8d4", + "ct" : "deffffffffffffffffffffffffffffff3812dd4103ddb68f86081010aac51901d3ffffffffffffffffffffffffffffff1c98423cdb89c7e94daa2af16e06d505d3ffffffffffffffffffffffffffffff1c98423cdb89c7e94daa2af16e06d505", + "tag" : "b4ccb422bc5f7264aff73f3675ff5b19", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "ivSize" : 0, + "keySize" : 256, + "tagSize" : 128, + "type" : "AeadTest", + "source" : { + "name" : "google-wycheproof", + "version" : "0.8r12" + }, + "tests" : [ + { + "tcId" : 294, + "comment" : "invalid nonce size", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "", + "aad" : "", + "msg" : "", + "ct" : "", + "tag" : "", + "result" : "invalid", + "flags" : [] + } + ] + }, + { + "ivSize" : 64, + "keySize" : 256, + "tagSize" : 128, + "type" : "AeadTest", + "source" : { + "name" : "google-wycheproof", + "version" : "0.8r12" + }, + "tests" : [ + { + "tcId" : 295, + "comment" : "invalid nonce size", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "0001020304050607", + "aad" : "", + "msg" : "", + "ct" : "", + "tag" : "", + "result" : "invalid", + "flags" : [] + } + ] + }, + { + "ivSize" : 88, + "keySize" : 256, + "tagSize" : 128, + "type" : "AeadTest", + "source" : { + "name" : "google-wycheproof", + "version" : "0.8r12" + }, + "tests" : [ + { + "tcId" : 296, + "comment" : "invalid nonce size", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a", + "aad" : "", + "msg" : "", + "ct" : "", + "tag" : "", + "result" : "invalid", + "flags" : [] + } + ] + }, + { + "ivSize" : 104, + "keySize" : 256, + "tagSize" : 128, + "type" : "AeadTest", + "source" : { + "name" : "google-wycheproof", + "version" : "0.8r12" + }, + "tests" : [ + { + "tcId" : 297, + "comment" : "invalid nonce size", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b0c", + "aad" : "", + "msg" : "", + "ct" : "", + "tag" : "", + "result" : "invalid", + "flags" : [] + } + ] + }, + { + "ivSize" : 112, + "keySize" : 256, + "tagSize" : 128, + "type" : "AeadTest", + "source" : { + "name" : "google-wycheproof", + "version" : "0.8r12" + }, + "tests" : [ + { + "tcId" : 298, + "comment" : "invalid nonce size", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b0c0d", + "aad" : "", + "msg" : "", + "ct" : "", + "tag" : "", + "result" : "invalid", + "flags" : [] + } + ] + }, + { + "ivSize" : 128, + "keySize" : 256, + "tagSize" : 128, + "type" : "AeadTest", + "source" : { + "name" : "google-wycheproof", + "version" : "0.8r12" + }, + "tests" : [ + { + "tcId" : 299, + "comment" : "invalid nonce size", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b0c0d0e0f", + "aad" : "", + "msg" : "", + "ct" : "", + "tag" : "", + "result" : "invalid", + "flags" : [] + } + ] + }, + { + "ivSize" : 160, + "keySize" : 256, + "tagSize" : 128, + "type" : "AeadTest", + "source" : { + "name" : "google-wycheproof", + "version" : "0.8r12" + }, + "tests" : [ + { + "tcId" : 300, + "comment" : "invalid nonce size", + "key" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "iv" : "000102030405060708090a0b0c0d0e0f10111213", + "aad" : "", + "msg" : "", + "ct" : "", + "tag" : "", + "result" : "invalid", + "flags" : [] + } + ] + } + ] +} diff --git a/Unit Tests/Tests/TestDECChaChaPoly1305.pas b/Unit Tests/Tests/TestDECChaChaPoly1305.pas index dd4e5c9..f70aecb 100644 --- a/Unit Tests/Tests/TestDECChaChaPoly1305.pas +++ b/Unit Tests/Tests/TestDECChaChaPoly1305.pas @@ -8,7 +8,7 @@ interface TestFramework, {$ENDIF} System.SysUtils, Generics.Collections, System.Math, - DECBaseClass, + DECBaseClass, System.JSON, DECCipherBase, DECCipherModes, DECCipherFormats, DECCiphers; @@ -16,6 +16,30 @@ interface // Testmethods for class TDECCipher {$IFDEF DUnitX} [TestFixture] {$ENDIF} TestChaCha20Poly1305 = class(TTestCase) + private + type + TJsonTestCase = record + key : TBytes; + iv : TBytes; + msg : TBytes; + aad : TBytes; + tag : TBytes; + enc : TBytes; + isValid : boolean; + end; + TTestEnumerator = class(TEnumerable) + private + fTests : TList; + protected + function DoGetEnumerator: TEnumerator; override; + public + constructor Create(const aTestFile : string); + destructor Destroy; override; + end; + private + fTests : TTestEnumerator; + + function IterTests : TTestEnumerator; published procedure TestPoly1305; procedure TestChaCha20_Poly1305_KeySetup; @@ -23,32 +47,16 @@ TestChaCha20Poly1305 = class(TTestCase) procedure TestChaChaEncodeDecodeSpeed; procedure TestXChaCha_Poly1305_AEAD; + // test suite code + procedure TestEncode; + procedure TestDecode; + + destructor Destroy; override; end; implementation -uses DECCipherModesPoly1305, System.Diagnostics; - - -//// ########################################### -//// #### OpenSSL reference implementation -//// ########################################### -// -// -//const POLY1305_KEY_SIZE = 32; -// POLY1305_DIGEST_SIZE = 16; -// -//type -// PPoly1305Ctx = PByte; -// TPoly1305Key = Array[0..POLY1305_KEY_SIZE-1] of byte; -// TPoly1305Mac = Array[0..POLY1305_DIGEST_SIZE-1] of byte; -// size_t = Integer; -// -// -//function Poly1305_ctx_size : size_t; cdecl; external 'libcrypto-3.dll'; -//procedure Poly1305_Init(ctx : PPoly1305Ctx; const key : TPoly1305Key); cdecl; external 'libcrypto-3.dll'; -//procedure Poly1305_Update(ctx : PPoly1305Ctx; inp : PByte; len : size_t); cdecl; external 'libcrypto-3.dll'; -//procedure Poly1305_Final(ctx : PPoly1305Ctx; var mac : TPoly1305Mac); cdecl; external 'libcrypto-3.dll'; - +uses DECCipherModesPoly1305, System.Diagnostics, classes, DECFormat, DECTypes, + System.JSON.Readers; // ########################################### @@ -179,6 +187,88 @@ procedure EncodeBuf( var dest : TBytes ); end; end; +procedure TestChaCha20Poly1305.TestDecode; +var aTest : TJsonTestCase; + chacha : TCipher_ChaCha20; + decode : TBytes; + isValid : boolean; +begin + isValid := False; // satisfy compiler + for aTest in IterTests do + begin + try + chacha := TCipher_ChaCha20.Create; + try + chacha.DataToAuthenticate := aTest.aad; + chacha.ExpectedAuthenticationResult := aTest.tag; + chaCha.Init( aTest.key, aTest.iv ); + + decode := chaCha.DecodeBytes(aTest.enc); + chaCha.Done; + isValid := True; + finally + chacha.Free; + end; + + Check( Length(decode) = Length(aTest.msg), 'Decoding length test failed'); + if Length(decode) <> 0 then + Check( Comparemem(@decode[0], @aTest.msg[0], Length(decode) ), 'Decoding failed' ); + except + // tests marked as "invalid" (or not "valid") raise an exception + // -> the test does not fail if the result is different to "valid" + on E : EDECException do + begin + isValid := False; + end; + else + raise; + end; + + Check( not (isValid xor aTest.isValid), 'Test failed'); + end; +end; + +procedure TestChaCha20Poly1305.TestEncode; +var aTest : TJsonTestCase; + chacha : TCipher_ChaCha20; + encode : TBytes; + isValid : boolean; +begin + isValid := False; // satisfy compiler + for aTest in IterTests do + begin + try + chacha := TCipher_ChaCha20.Create; + try + chacha.DataToAuthenticate := aTest.aad; + chacha.ExpectedAuthenticationResult := aTest.tag; + chaCha.Init( aTest.key, aTest.iv ); + + encode := chaCha.EncodeBytes(aTest.msg); + chaCha.Done; + isValid := True; + finally + chacha.Free; + end; + + Check( Length(encode) = Length(aTest.enc), 'Decoding length test failed'); + if Length(encode) <> 0 then + Check( Comparemem(@encode[0], @aTest.enc[0], Length(encode) ), 'Encoding failed' ); + except + // tests marked as "invalid" (or not "valid") raise an exception + // -> the test does not fail if the result is different to "valid" + on E : EDECException do + begin + isValid := False; + end; + else + raise; + end; + + Check( not (isValid xor aTest.isValid), 'Test failed'); + end; +end; + type THackPly1305 = class(TPoly1305); @@ -271,6 +361,14 @@ procedure InvData( data : PByte; len : integer ); end; +function TestChaCha20Poly1305.IterTests: TTestEnumerator; +begin + if not Assigned(fTests) then + fTests := TTestEnumerator.Create('..\..\Unit Tests\Data\chacha20_poly1305_test.json'); + + Result := fTests; +end; + procedure TestChaCha20Poly1305.TestXChaCha_Poly1305_AEAD; // is actually the same test vector as for chacha20_poly1305 const cMsg : AnsiString = 'Ladies and Gentlemen of the class of ''99: If I could offer you only one tip for the future, sunscreen would be it.'; @@ -350,6 +448,13 @@ procedure TestChaCha20Poly1305.TestXChaCha_Poly1305_AEAD; end; +destructor TestChaCha20Poly1305.Destroy; +begin + fTests.Free; + + inherited; +end; + procedure TestChaCha20Poly1305.TestChaCha20_Poly1305_AEAD; const cMsg : AnsiString = 'Ladies and Gentlemen of the class of ''99: If I could offer you only one tip for the future, sunscreen would be it.'; cAAD : TBytes = [$50, $51, $52, $53, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7]; @@ -429,6 +534,63 @@ procedure TestChaCha20Poly1305.TestChaCha20_Poly1305_AEAD; end; +{ TestChaCha20Poly1305.TTestEnumerator } + +constructor TestChaCha20Poly1305.TTestEnumerator.Create( + const aTestFile: string); +var groups : TJSONArray; + tests : TJsonValue; + aTest : TJsonValue; + testFile : TJSonObject; + testRec : TJsonTestCase; +begin + inherited Create; + + fTests := TList.Create; + with TSTringList.create do + try + Loadfromfile('..\..\Unit Tests\Data\chacha20_poly1305_test.json'); + testFile := TJSONObject.ParseJSONValue(Text) as TJSONObject; + finally + Free; + end; + + // ########################################### + // #### Build list of tests + try + groups := testFile.GetValue('testGroups') as TJsonArray; + + for tests in groups do + begin + for aTest in ((tests as TJsonObject).GetValue('tests') as TJsonArray) do + begin + testRec.aad := BytesOf(TFormat_HexL.Decode(RawByteString(aTest.GetValue('aad')))); + testRec.iv := BytesOf(TFormat_HexL.Decode(RawByteString(aTest.GetValue('iv')))); + testRec.key := BytesOf(TFormat_HexL.Decode(RawByteString(aTest.GetValue('key')))); + testRec.msg := BytesOf(TFormat_HexL.Decode(RawByteString(aTest.GetValue('msg')))); + testRec.tag := BytesOf(TFormat_HexL.Decode(RawByteString(aTest.GetValue('tag')))); + testRec.Enc := BytesOf(TFormat_HexL.Decode(RawByteString(aTest.GetValue('ct')))); + testRec.isValid := SameText( aTest.GetValue('result'), 'Valid'); + fTests.Add(testRec); + end; + end; + finally + testFile.Free; + end; +end; + +destructor TestChaCha20Poly1305.TTestEnumerator.Destroy; +begin + fTests.Free; + + inherited; +end; + +function TestChaCha20Poly1305.TTestEnumerator.DoGetEnumerator: TEnumerator; +begin + Result := fTests.GetEnumerator; +end; + initialization // Register all test cases to be run {$IFDEF DUnitX} From e8ffbb5264853596bb6e7f9d325f8719ca691a6c Mon Sep 17 00:00:00 2001 From: Michael Rabatscher Date: Tue, 29 Jul 2025 23:03:36 +0200 Subject: [PATCH 15/17] Fix: the destructor was actually a test - moved to public section --- Unit Tests/Tests/TestDECChaChaPoly1305.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Unit Tests/Tests/TestDECChaChaPoly1305.pas b/Unit Tests/Tests/TestDECChaChaPoly1305.pas index f70aecb..f3e7c1d 100644 --- a/Unit Tests/Tests/TestDECChaChaPoly1305.pas +++ b/Unit Tests/Tests/TestDECChaChaPoly1305.pas @@ -40,6 +40,8 @@ TTestEnumerator = class(TEnumerable) fTests : TTestEnumerator; function IterTests : TTestEnumerator; + public + destructor Destroy; override; published procedure TestPoly1305; procedure TestChaCha20_Poly1305_KeySetup; @@ -50,8 +52,6 @@ TTestEnumerator = class(TEnumerable) // test suite code procedure TestEncode; procedure TestDecode; - - destructor Destroy; override; end; implementation From a025e6c2b4871f1db435c7ef66fb2d620ba570f8 Mon Sep 17 00:00:00 2001 From: Michael Rabatscher Date: Sat, 2 Aug 2025 22:38:50 +0200 Subject: [PATCH 16/17] Extended intel CPU support (added unit to detect features AVX, SSE and also AES HW support). --- Source/DEC60.dpr | 4 +- Source/DECCPUSupport.pas | 117 +++++++++++++++++++++++++++++ Source/DECCipherModesPoly1305.pas | 1 + Source/DECCiphers.pas | 21 +++++- Unit Tests/DECDUnitTestSuite.dpr | 3 +- Unit Tests/DECDUnitTestSuite.dproj | 19 +++++ 6 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 Source/DECCPUSupport.pas diff --git a/Source/DEC60.dpr b/Source/DEC60.dpr index 10cdafa..bca0efc 100644 --- a/Source/DEC60.dpr +++ b/Source/DEC60.dpr @@ -49,7 +49,9 @@ uses DECZIPHelper in 'DECZIPHelper.pas', DECCipherPaddings in 'DECCipherPaddings.pas', DECCipherModesCCM in 'DECCipherModesCCM.pas', - DECAuthenticatedCipherModesBase in 'DECAuthenticatedCipherModesBase.pas'; + DECAuthenticatedCipherModesBase in 'DECAuthenticatedCipherModesBase.pas', + DECCipherModesPoly1305 in 'DECCipherModesPoly1305.pas', + DECCPUSupport in 'DECCPUSupport.pas'; begin try diff --git a/Source/DECCPUSupport.pas b/Source/DECCPUSupport.pas new file mode 100644 index 0000000..1a81ce1 --- /dev/null +++ b/Source/DECCPUSupport.pas @@ -0,0 +1,117 @@ +{***************************************************************************** + The DEC team (see file NOTICE.txt) licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. A copy of this licence is found in the root directory + of this project in the file LICENCE.txt or alternatively at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*****************************************************************************} + +/// +/// x86 and x64 CPU instruction support functions. +/// this class only works for intel cpus - arm will result. +/// The functions defined here return true if the feature is availabe +/// for the cpu +/// + +unit DECCPUSupport; + +interface + +type + TDEC_CPUSupport = class(TObject) + public + // flags are set in initialization + class var AES : boolean; + class var AVX : boolean; + class var AVX2 : boolean; + + class var SSE : boolean; + class var SSE2 : boolean; + class var SSE3 : boolean; + class var SSE41 : boolean; + class var SSE42 : boolean; + + class var RDRand : boolean; + class var RDSeed : boolean; + end; + +implementation + +{$IFDEF CPUX64} +{$DEFINE x64} +{$ENDIF} +{$IFDEF cpux86_64} +{$DEFINE x64} +{$ENDIF} + +{$IFDEF CPU86} +{$DEFINE x86} +{$ENDIF} +{$IFDEF CPUX86} +{$DEFINE x86} +{$ENDIF} + +{$IFDEF CPU386} +{$DEFINE x86} +{$ENDIF} + + +{$IF Defined(x64) or Defined(x86)} +{$WARN SYMBOL_PLATFORM OFF} +procedure InitFlags; +var reg : TCPUIDRec; + nids : LongWord; +begin + reg := GetCPUID(0, 0); + nids := reg.EAX; + + + if nids >= 1 then + begin + reg := GetCPUID(1, 0); + TDEC_CPUSupport.SSE := (reg.EDX and (1 shl 25)) <> 0; + TDEC_CPUSupport.SSE2 := (reg.EDX and (1 shl 26)) <> 0; + TDEC_CPUSupport.SSE3 := (reg.ECX and (1 shl 9)) <> 0; + TDEC_CPUSupport.SSE41 := (reg.ECX and (1 shl 19)) <> 0; + TDEC_CPUSupport.SSE42 := (reg.ECX and (1 shl 20)) <> 0; + TDEC_CPUSupport.AES := (reg.ECX and (1 shl 25)) <> 0; + + TDEC_CPUSupport.AVX := (reg.ECX and (1 shl 28)) <> 0; + TDEC_CPUSupport.RDRAND := (reg.ECX and (1 shl 30)) <> 0; + end; + + if nids >= 7 then + begin + reg := GetCPUID($7); + TDEC_CPUSupport.AVX2 := (reg.EBX and (1 shl 5)) <> 0; + TDEC_CPUSupport.RDSEED := (reg.EBX and (1 shl 18)) <> 0; + end; +end; +{$ENDIF} + +initialization + TDEC_CPUSupport.AES := False; + TDEC_CPUSupport.AVX := False; + TDEC_CPUSupport.AVX2 := False; + + TDEC_CPUSupport.SSE := False; + TDEC_CPUSupport.SSE2 := False; + TDEC_CPUSupport.SSE3 := False; + TDEC_CPUSupport.SSE41 := False; + TDEC_CPUSupport.SSE42 := False; + + TDEC_CPUSupport.RDRand := False; + TDEC_CPUSupport.RDSeed := False; +{$IF Defined(x64) or Defined(x86)} + InitFlags; +{$IFEND} +end. diff --git a/Source/DECCipherModesPoly1305.pas b/Source/DECCipherModesPoly1305.pas index ce2f809..90307f3 100644 --- a/Source/DECCipherModesPoly1305.pas +++ b/Source/DECCipherModesPoly1305.pas @@ -216,6 +216,7 @@ constructor TPoly1305.Create; begin inherited Create; + FCalcAuthenticationTagLength := sizeof(TBlock16Byte); FH := @fPoly1305State[0]; FR := @fPoly1305State[sizeof(THarr)]; diff --git a/Source/DECCiphers.pas b/Source/DECCiphers.pas index 3cdff23..9d70719 100644 --- a/Source/DECCiphers.pas +++ b/Source/DECCiphers.pas @@ -1106,7 +1106,7 @@ implementation {$IFOPT R+}{$DEFINE RESTORE_RANGECHECKS}{$R-}{$ENDIF} uses - DECData, DECDataCipher; + DECData, DECDataCipher, DECCPUSupport; { TCipher_Null } @@ -7609,9 +7609,13 @@ procedure TCipher_ChaCha20.AfterConstruction; {$IFDEF PUREPASCAL} fFullBlockFunc := FullBlockPas; {$ELSE} + fFullBlockFunc := FullBlockPas; + case CpuMode of - cmSSE: fFullBlockFunc := FullBlockSSE; - cmAVX: fFullBlockFunc := FullBlockAVX; + cmSSE: if TDEC_CPUSupport.SSE3 then + fFullBlockFunc := FullBlockSSE; + cmAVX: if TDEC_CPUSupport.AVX2 then + fFullBlockFunc := FullBlockAVX; else fFullBlockFunc := FullBlockPas; end; @@ -8002,6 +8006,17 @@ initialization {$ENDIF} {$ENDIF} + {$IFDEF CPUx86} + if TDEC_CPUSupport.AVX2 + then + TCipher_ChaCha20.CpuMode := cmAVX + else if TDEC_CPUSupport.SSE3 + then + TCipher_ChaCha20.CpuMode := cmSSE + else + TCipher_ChaCha20.CpuMode := cmPas; + {$ENDIF} + finalization end. diff --git a/Unit Tests/DECDUnitTestSuite.dpr b/Unit Tests/DECDUnitTestSuite.dpr index 2a1a612..fdf107d 100644 --- a/Unit Tests/DECDUnitTestSuite.dpr +++ b/Unit Tests/DECDUnitTestSuite.dpr @@ -70,7 +70,8 @@ uses DECUtilRawByteStringHelper in '..\Source\DECUtilRawByteStringHelper.pas', DECZIPHelper in '..\Source\DECZIPHelper.pas', DECCipherModesGCM in '..\Source\DECCipherModesGCM.pas', - TestDECChaChaPoly1305 in 'Tests\TestDECChaChaPoly1305.pas'; + TestDECChaChaPoly1305 in 'Tests\TestDECChaChaPoly1305.pas', + DECCPUSupport in '..\Source\DECCPUSupport.pas'; {$R *.RES} diff --git a/Unit Tests/DECDUnitTestSuite.dproj b/Unit Tests/DECDUnitTestSuite.dproj index f8ffab9..b08d731 100644 --- a/Unit Tests/DECDUnitTestSuite.dproj +++ b/Unit Tests/DECDUnitTestSuite.dproj @@ -195,6 +195,7 @@ + Base @@ -260,6 +261,18 @@ true + + + DECDUnitTestSuite.exe + true + + + + + DECDUnitTestSuite.rsm + true + + @@ -267,6 +280,12 @@ true + + + .\ + true + + 1 From 7e1b4431557af090205d1d3bb41d774c6191294f Mon Sep 17 00:00:00 2001 From: Michael Rabatscher Date: Wed, 20 Aug 2025 11:50:10 +0200 Subject: [PATCH 17/17] * New AES assembler routines based on the Intel Whitepaper * extended additional buffer size in AES to allow alignment * Moved align code to DECUtil.pas * Fixed an old assembler routine for 64 bit byte swapping * Fixed include file - it only worked for 32bits environments but not for 64 bits * Updated chacha defines so they work with the DEC internal defines (removed my original ifdefs) * Fixed bug appearing in the gcm testcases -> wrong blocksize should throw an exception --- Source/DECCipherModes.pas | 2 +- Source/DECCiphers.pas | 687 ++++++++++++++++++++++++++--- Source/DECOptions.inc | 48 +- Source/DECUtil.pas | 19 + Source/x86_64/DECUtil.inc | 5 +- Unit Tests/Tests/TestDECCipher.pas | 4 +- 6 files changed, 683 insertions(+), 82 deletions(-) diff --git a/Source/DECCipherModes.pas b/Source/DECCipherModes.pas index 5770152..e687f63 100644 --- a/Source/DECCipherModes.pas +++ b/Source/DECCipherModes.pas @@ -751,7 +751,7 @@ procedure TDECCipherModes.InitMode; cmPoly1305: fAuthObj := TPoly1305.Create; end; end - else if Context.BlockSize < 16 + else if (Context.BlockSize < 16) and (TCipherMode.cmPoly1305 = FMode) then fAuthObj := TPoly1305.Create else diff --git a/Source/DECCiphers.pas b/Source/DECCiphers.pas index 9d70719..6237f0d 100644 --- a/Source/DECCiphers.pas +++ b/Source/DECCiphers.pas @@ -380,8 +380,19 @@ TCipher_RC6 = class(TDECFormattedCipher) end; TCipher_Rijndael = class(TDECFormattedCipher) + protected + + /// + /// Blocksize in LongWords + /// + const Rijndael_Blocks = 4; // don't change this! + /// + /// Maximum number of rounds allowed. + /// + Rijndael_Rounds = 14; private FRounds: Integer; + /// /// Calculates the key used for encoding. Implemented is the "new AES /// conform key scheduling". @@ -409,6 +420,7 @@ TCipher_Rijndael = class(TDECFormattedCipher) procedure DoEncode(Source, Dest: Pointer; Size: Integer); override; procedure DoDecode(Source, Dest: Pointer; Size: Integer); override; public + class var UseAESAsm : boolean; class function Context: TCipherContext; override; /// /// Gets the number of rounds/times the algorithm is being applied to the @@ -2829,21 +2841,549 @@ procedure TCipher_RC6.DoDecode(Source, Dest: Pointer; Size: Integer); { TCipher_Rijndael } class function TCipher_Rijndael.Context: TCipherContext; -const - // don't change this! - Rijndael_Blocks = 4; - Rijndael_Rounds = 14; begin Result.KeySize := 32; Result.BlockSize := Rijndael_Blocks * 4; Result.BufferSize := Rijndael_Blocks * 4; - Result.AdditionalBufferSize := (Rijndael_Rounds + 1) * Rijndael_Blocks * SizeOf(UInt32) * 2; + Result.AdditionalBufferSize := (Rijndael_Rounds + 1) * Rijndael_Blocks * SizeOf(UInt32) * 2 + $20; // add additional spare memory for alignment Result.NeedsAdditionalBufferBackup := False; Result.MinRounds := 1; Result.MaxRounds := 1; Result.CipherType := [ctSymmetric, ctBlock]; end; +{$IF defined(X86ASM) or defined(X64ASM)} + +// ########################################### +// #### AES assembler functions +// #### Routines are based on the intel whitepaper: +// #### https://www.intel.com/content/dam/develop/external/us/en/documents/aes-wp-2012-09-22-v01-165683.pdf +// ########################################### + +// assumes registerd preloaded - use only in within BuildASMKey128 +procedure KeyExpand128; register; +asm + pshufd xmm2, xmm2, $ff; + movapd xmm3, xmm1; + pslldq xmm3, $04; + pxor xmm1, xmm3; + movapd xmm3, xmm1; + pslldq xmm3, $04; + pxor xmm1, xmm3; + movapd xmm3, xmm1; + pslldq xmm3, $04; + pxor xmm1, xmm3; + pxor xmm1, xmm2; + {$IFDEF X64ASM} + movdqu [rdx], xmm1; + add rdx, $10; + {$ELSE} + movdqu [edx], xmm1; + add edx, $10; + {$ENDIF} + +end; + +procedure BuildAsmKey128( key : PByte; dest : PLongWord ); register; +// eax : key, edx: dest +// rcx : key, rdx : dest +asm + xorpd xmm2, xmm2; + // key is 128bit + // 10 rounds to be filled + {$IFDEF X64ASM} + movdqu xmm1, [rcx]; + movdqu [rdx], xmm1; + add rdx, 16; + {$ELSE} + movdqu xmm1, [eax]; + movdqu [edx], xmm1; + add edx, 16; + {$ENDIF} + + aeskeygenassist xmm2, xmm1, $1; + call KeyExpand128; + aeskeygenassist xmm2, xmm1, $2; + call KeyExpand128 + aeskeygenassist xmm2, xmm1, $4 + call KeyExpand128 + aeskeygenassist xmm2, xmm1, $8 + call KeyExpand128 + aeskeygenassist xmm2, xmm1, $10 + call KeyExpand128 + aeskeygenassist xmm2, xmm1, $20 + call KeyExpand128 + aeskeygenassist xmm2, xmm1, $40 + call KeyExpand128 + aeskeygenassist xmm2, xmm1, $80 + call KeyExpand128 + aeskeygenassist xmm2, xmm1, $1b + call KeyExpand128 + aeskeygenassist xmm2, xmm1, $36 + call KeyExpand128 +end; + + +// only call with BuildasmKey192 - it assumes preloaded registers xmm0, xmm1, xmm2 +// xmm3 is used for intermediate results +procedure KeyExpand192; register; +asm + pshufd xmm1, xmm1, $55; + movapd xmm3, xmm0; + pslldq xmm3, $04; + pxor xmm0, xmm3; + + pslldq xmm3, $04; + pxor xmm0, xmm3; + + pslldq xmm3, $04; + pxor xmm0, xmm3; + + pxor xmm0, xmm1; + pshufd xmm1, xmm0, $FF; + + movapd xmm3, xmm2; + pslldq xmm3, $04; + + pxor xmm2, xmm3; + pxor xmm2, xmm1; +end; + +procedure BuildAsmKey196( key : PByte; dest : PLongWord); register; +// eax: key, edx : dest +// rcx: key, rdx : dest; +asm + // load key + xorpd xmm2, xmm2; + + {$IFDEF X64ASM} + movupd xmm0, [rcx]; + movsd xmm2, [rcx + 16]; + movupd [rdx], xmm0; + {$ELSE} + movupd xmm0, [eax]; + movsd xmm2, [eax + 16]; + movupd [edx], xmm0; + {$ENDIF} + + movapd xmm4, xmm2; + + aeskeygenassist xmm1, xmm2, $01; + call KeyExpand192; + + shufpd xmm4, xmm0, 0; + {$IFDEF X64ASM} + movupd [rdx + 16], xmm4; + {$ELSE} + movupd [edx + 16], xmm4; + {$ENDIF} + movapd xmm4, xmm0; + shufpd xmm4, xmm2, 1; + {$IFDEF X64ASM} + movupd [rdx + 32], xmm4; + {$ELSE} + movupd [edx + 32], xmm4; + {$ENDIF} + + aeskeygenassist xmm1, xmm2, $02; + call KeyExpand192; + {$IFDEF X64ASM} + movupd [rdx + 48], xmm0; + {$ELSE} + movupd [edx + 48], xmm0; + {$ENDIF} + movapd xmm4, xmm2; + aeskeygenassist xmm1, xmm2, $04; + call KeyExpand192; + shufpd xmm4, xmm0, 0; + {$IFDEF X64ASM} + movupd [rdx + 64], xmm4; + {$ELSE} + movupd [edx + 64], xmm4; + {$ENDIF} + + movapd xmm4, xmm0; + shufpd xmm4, xmm2, 1; + {$IFDEF X64ASM} + movupd [rdx + 80], xmm4; + {$ELSE} + movupd [edx + 80], xmm4; + {$ENDIF} + + aeskeygenassist xmm1, xmm2, $08; + call KeyExpand192; + {$IFDEF X64ASM} + movupd [rdx + 96], xmm0; + {$ELSE} + movupd [edx + 96], xmm0; + {$ENDIF} + movapd xmm4, xmm2; + + aeskeygenassist xmm1, xmm2, $10; + call KeyExpand192; + shufpd xmm4, xmm0, 0; + {$IFDEF X64ASM} + movupd [rdx + 112], xmm4; + {$ELSE} + movupd [edx + 112], xmm4; + {$ENDIF} + + movapd xmm4, xmm0; + shufpd xmm4, xmm2, 1; + {$IFDEF X64ASM} + movupd [rdx + 128], xmm4; + {$ELSE} + movupd [edx + 128], xmm4; + {$ENDIF} + + aeskeygenassist xmm1, xmm2, $20; + call KeyExpand192; + {$IFDEF X64ASM} + movupd [rdx + 144], xmm0; + {$ELSE} + movupd [edx + 144], xmm0; + {$ENDIF} + movapd xmm4, xmm2; + + aeskeygenassist xmm1, xmm2, $40; + call KeyExpand192; + + shufpd xmm4, xmm0, 0; + {$IFDEF X64ASM} + movupd [rdx + 160], xmm4; + {$ELSE} + movupd [edx + 160], xmm4; + {$ENDIF} + + movapd xmm4, xmm0; + shufpd xmm4, xmm2, 1; + {$IFDEF X64ASM} + movupd [rdx + 176], xmm4; + {$ELSE} + movupd [edx + 176], xmm4; + {$ENDIF} + + aeskeygenassist xmm1, xmm2, $80; + call KeyExpand192; + {$IFDEF X64ASM} + movupd [rdx + 192], xmm0; + {$ELSE} + movupd [edx + 192], xmm0; + {$ENDIF} +end; + +// for the 256 bit key expand functions we assume that xmm0 - xmm2 are +// prefilled and are used (aka not used as parameters) +// xmm3 and xmm4 are used as intermediate registers +procedure KeyExpand256_1; register; +asm + pshufd xmm1, xmm1, $FF; + movapd xmm3, xmm0; + pslldq xmm3, $04; + pxor xmm0, xmm3; + + pslldq xmm3, $04; + pxor xmm0, xmm3; + + pslldq xmm3, $04; + pxor xmm0, xmm3; + pxor xmm0, xmm1; +end; + +// no parameter given here -> xmm01 to xmm3 represent temp1 to temp4 +procedure KeyExpand256_2; register; +asm + aeskeygenassist xmm3, xmm0, $00; + pshufd xmm1, xmm3, $AA; + movapd xmm3, xmm2; + pslldq xmm3, $04; + pxor xmm2, xmm3; + pslldq xmm3, $04; + + pxor xmm2, xmm3; + pslldq xmm3, $04; + + pxor xmm2, xmm3; + pxor xmm2, xmm1; +end; + +procedure BuildAsmKey256( key : PByte; dest : PLongWord); register; +asm +// eax = key, edx = dest +// rcx = key, rdx = dest + + // load key + {$IFDEF X64ASM} + movupd xmm0, [rcx]; + movupd xmm2, [rcx + 16]; + + movupd [rdx], xmm0; + movupd [rdx + 16], xmm2; + + {$ELSE} + movupd xmm0, [eax]; + movupd xmm2, [eax + 16]; + + movupd [edx], xmm0; + movupd [edx + 16], xmm2; + {$ENDIF} + + aeskeygenassist xmm1, xmm2, $1; + call KeyExpand256_1; + {$IFDEF X64ASM} + movupd [rdx + 32], xmm0; + {$ELSE} + movupd [edx + 32], xmm0; + {$ENDIF} + call KeyExpand256_2; + {$IFDEF X64ASM} + movupd [rdx + 48], xmm2; + {$ELSE} + movupd [edx + 48], xmm2; + {$ENDIF} + + aeskeygenassist xmm1, xmm2, $2; + call KeyExpand256_1; + {$IFDEF X64ASM} + movupd [rdx + 64], xmm0; + {$ELSE} + movupd [edx + 64], xmm0; + {$ENDIF} + call KeyExpand256_2; + {$IFDEF X64ASM} + movupd [rdx + 80], xmm2; + {$ELSE} + movupd [edx + 80], xmm2; + {$ENDIF} + + aeskeygenassist xmm1, xmm2, $4; + call KeyExpand256_1; + {$IFDEF X64ASM} + movupd [rdx + 96], xmm0; + {$ELSE} + movupd [edx + 96], xmm0; + {$ENDIF} + call KeyExpand256_2; + {$IFDEF X64ASM} + movupd [rdx + 112], xmm2; + {$ELSE} + movupd [edx + 112], xmm2; + {$ENDIF} + + aeskeygenassist xmm1, xmm2, $8; + call KeyExpand256_1; + {$IFDEF X64ASM} + movupd [rdx + 128], xmm0; + {$ELSE} + movupd [edx + 128], xmm0; + {$ENDIF} + call KeyExpand256_2; + {$IFDEF X64ASM} + movupd [rdx + 144], xmm2; + {$ELSE} + movupd [edx + 144], xmm2; + {$ENDIF} + + aeskeygenassist xmm1, xmm2, $10; + call KeyExpand256_1; + {$IFDEF X64ASM} + movupd [rdx + 160], xmm0; + {$ELSE} + movupd [edx + 160], xmm0; + {$ENDIF} + call KeyExpand256_2; + {$IFDEF X64ASM} + movupd [rdx + 176], xmm2; + {$ELSE} + movupd [edx + 176], xmm2; + {$ENDIF} + + aeskeygenassist xmm1, xmm2, $20; + call KeyExpand256_1; + {$IFDEF X64ASM} + movupd [rdx + 192], xmm0; + {$ELSE} + movupd [edx + 192], xmm0; + {$ENDIF} + call KeyExpand256_2; + {$IFDEF X64ASM} + movupd [rdx + 208], xmm2; + {$ELSE} + movupd [edx + 208], xmm2; + {$ENDIF} + + aeskeygenassist xmm1, xmm2, $40; + call KeyExpand256_1; + {$IFDEF X64ASM} + movupd [rdx + 224], xmm0; + {$ELSE} + movupd [edx + 224], xmm0; + {$ENDIF} + call KeyExpand256_2; + {$IFDEF X64ASM} + movupd [rdx + 240], xmm2; + {$ELSE} + movupd [edx + 240], xmm2; + {$ENDIF} +end; + +procedure BuildASMDecodeKey( encodeKey : PLongWord; decodeKey : PLongWord; numRounds : integer); register; +// eax = encodeKey, edx = decodeKey, ecx = numRounds +// rcx = encodeKey, rdx = decodeKey, r8 = numRounds +asm +// store in reverse order for iterative access in the decode routine! + {$IFDEF X64ASM} + movdqu xmm0, [rcx]; + lea rdx, [rdx + 8*r8]; // mult by 16 is not possible -> do it twice + lea rdx, [rdx + 8*r8]; + movdqu [rdx], xmm0; + + add rcx, 16; + sub rdx, 16; + dec r8d; + + @decLoop: + movdqu xmm0, [rcx]; + aesimc xmm0, xmm0; + movdqu [rdx], xmm0; + + add rcx, 16; + sub rdx, 16; + dec r8d; + jg @decLoop; + + movdqu xmm0, [rcx]; + movdqu [rdx], xmm0; + {$ELSE} + movdqu xmm0, [eax]; + lea edx, [edx + 8*ecx]; // mult by 16 is not possible -> do it twice + lea edx, [edx + 8*ecx]; + movdqu [edx], xmm0; + + add eax, 16; + sub edx, 16; + dec ecx; + + @decLoop: + movdqu xmm0, [eax]; + aesimc xmm0, xmm0; + movdqu [edx], xmm0; + + add eax, 16; + sub edx, 16; + + // decrement ecx and test + loop @decLoop; + + movdqu xmm0, [eax]; + movdqu [edx], xmm0; + {$ENDIF} +end; + + +// ########################################### +// #### AES assembler encode/decode +// ########################################### + +{$REGION 'ASM encode/decode'} +procedure AESEncode( Source, Dest : Pointer; numRounds : integer; key : PLongWord ); register; +asm + {$IFDEF X64ASM} + movupd xmm1, [rcx]; + movdqa xmm2, [r9]; + pxor xmm1, xmm2; + add r9, 16; + + dec r8d; + + @aesloop: + movdqa xmm2, [r9]; + aesenc xmm1, xmm2; + add r9, 16; + + dec r8d; + jg @aesloop; + + movdqa xmm2, [r9]; + aesenclast xmm1, xmm2; + movupd [rdx], xmm1; + {$ELSE} + push edi; + + movupd xmm1, [eax]; + mov edi, key; + movdqa xmm2, [edi]; + pxor xmm1, xmm2; + add edi, 16; + + dec ecx; + + @aesloop: + movdqa xmm2, [edi]; + aesenc xmm1, xmm2; + add edi, 16; + + dec ecx; + jg @aesloop; + + movdqa xmm2, [edi]; + aesenclast xmm1, xmm2; + movupd [edx], xmm1; + pop edi; + {$ENDIF} +end; + +procedure AESDecode( Source, Dest : Pointer; numRounds : integer; key : PLongWord ); register; +asm + {$IFDEF X64ASM} + movupd xmm1, [rcx]; + movdqa xmm2, [r9]; + pxor xmm1, xmm2; + add r9, 16; + + dec r8d; + + @aesloop: + movdqa xmm2, [r9]; + aesdec xmm1, xmm2; + add r9, 16; + + dec r8d; + jg @aesloop; + + movdqa xmm2, [r9]; + aesdeclast xmm1, xmm2; + movupd [rdx], xmm1; + {$ELSE} + push edi; + + movupd xmm1, [eax]; + mov edi, key; + movdqa xmm2, [edi]; + pxor xmm1, xmm2; + add edi, 16; + + dec ecx; + + @aesloop: + movdqa xmm2, [edi]; + aesdec xmm1, xmm2; + add edi, 16; + + dec ecx; + jg @aesloop; + + movdqa xmm2, [edi]; + aesdeclast xmm1, xmm2; + movupd [edx], xmm1; + pop edi; + {$ENDIF} +end; + +{$ENDREGION} + +{$IFEND} + procedure TCipher_Rijndael.DoInit(const Key; Size: Integer); {$REGION OldKeyShedule} { @@ -2934,6 +3474,9 @@ procedure TCipher_Rijndael.DoInit(const Key; Size: Integer); end; } {$ENDREGION} +{$IF defined(X86ASM) or defined(X64ASM)} +var pBuf : PUInt32Array; +{$IFEND} begin if Size <= 16 then FRounds := 10 @@ -2942,10 +3485,29 @@ procedure TCipher_Rijndael.DoInit(const Key; Size: Integer); FRounds := 12 else FRounds := 14; - FillChar(FAdditionalBuffer^, 32, 0); - Move(Key, FAdditionalBuffer^, Size); - BuildEncodeKey(Size); - BuildDecodeKey; + + {$IF defined(X86ASM) or defined(X64ASM)} + if UseAESAsm and TDEC_CPUSupport.AES then + begin + pBuf := AlignPtr32( FAdditionalBuffer ); + case Size of + 16: BuildAsmKey128(@key, PLongWord(pBuf)); + 24: BuildAsmKey196(@key, PLongWord(pBuf)); + 32: BuildAsmKey256(@key, PLongWord(pBuf)); + end; + + // build inverse decode key - utilize the aesimc opcode + BuildASMDecodeKey( PLongWord(pBuf), @(pBuf^[Rijndael_Rounds*Rijndael_Blocks]), fRounds); + end + else + {$IFEND} + begin + FillChar(FAdditionalBuffer^, 32, 0); + Move(Key, FAdditionalBuffer^, Size); + + BuildEncodeKey(Size); + BuildDecodeKey; + end; inherited; end; @@ -3046,12 +3608,50 @@ procedure TCipher_Rijndael.DoEncode(Source, Dest: Pointer; Size: Integer); begin Assert(Size = Context.BlockSize); + {$IF defined(X86ASM) or defined(X64ASM)} + if UseAESAsm and (TDEC_CPUSupport.AES) then + begin + AESEncode(Source, Dest, fRounds, AlignPtr32( FAdditionalBuffer ) ); + exit; + end; + {$IFEND} + P := FAdditionalBuffer; A1 := PUInt32Array(Source)[0]; B1 := PUInt32Array(Source)[1]; C1 := PUInt32Array(Source)[2]; D1 := PUInt32Array(Source)[3]; + //i := frounds; +// asmP := @fAsmEncKey[0]; +// asm +// push edx; +// push ecx; +// mov ecx, source; +// movupd xmm1, [ecx]; +// mov ecx, asmP; +// movupd xmm2, [ecx]; +// pxor xmm1, xmm2; +// add ecx, 16; +// +// mov edx, i; +// dec edx; +// +// aesloop: +// movupd xmm2, [ecx]; +// aesenc xmm1, xmm2; +// add ecx, 16; +// +// dec edx; +// jg aesloop; +// +// movupd xmm2, [ecx]; +// aesenclast xmm1, xmm2; +// movupd pp, xmm1; +// pop ecx; +// pop edx; +// end; + for I := 2 to FRounds do begin A2 := A1 xor P[0]; @@ -3111,6 +3711,14 @@ procedure TCipher_Rijndael.DoDecode(Source, Dest: Pointer; Size: Integer); begin Assert(Size = Context.BlockSize); + {$IF defined(X86ASM) or defined(X64ASM)} + if UseAESAsm and TDEC_CPUSupport.AES then + begin + AESDecode(Source, Dest, fRounds, @(PUInt32Array(AlignPtr32( FAdditionalBuffer ))^[Rijndael_Rounds*Rijndael_Blocks]) ); + exit; + end; + {$IFEND} + P := Pointer(PByte(FAdditionalBuffer) + FAdditionalBufferSize shr 1 + FRounds * 16); // for Pointer Math A1 := PUInt32Array(Source)[0]; B1 := PUInt32Array(Source)[1]; @@ -6869,28 +7477,7 @@ procedure TCipher_XTEA_DEC52.DoDecode(Source, Dest: Pointer; Size: Integer); { TCipher_ChaCha20 } -{$IFDEF FPC} {$ASMMODE intel} {$S-} {$ENDIF} - -{$IFDEF CPUX64} -{$DEFINE x64} -{$ENDIF} -{$IFDEF cpux86_64} -{$DEFINE x64} -{$ENDIF} - -{$IFDEF CPU86} -{$DEFINE x86} -{$ENDIF} -{$IFDEF CPUX86} -{$DEFINE x86} -{$ENDIF} - -{$IFDEF CPU386} -{$DEFINE x86} -{$ENDIF} - - -{$IF defined(MRMATH_NOASM)} +{$IFDEF PUREPASCAL} function rol(value: LongWord; Bits: Byte): LongWord; begin Result := (value shl Bits) or (value shr (32 - bits)); @@ -6901,19 +7488,19 @@ function rol(value: LongWord; Bits: Byte): LongWord; assembler; begin {$ENDIF} asm - {$IF defined(x64)} + {$IFdef X64ASM} mov eax, ecx; mov cl, dl; rol eax, cl; {$ELSE} xchg cl,dl rol eax, cl - {$IFEND} + {$ENDIF} end; {$IFDEF FPC} end; {$ENDIF} -{$IFEND} +{$ENDIF} procedure TCipher_ChaCha20.ChaChaQuarterRound(var a, b, c, d: LongWord); begin @@ -7057,14 +7644,6 @@ procedure TCipher_ChaCha20.OnAfterInitVectorInitialization( inherited; end; -function AlignPtr32( A : Pointer ) : Pointer; -begin - Result := A; - if (NativeUint(A) and $1F) <> 0 then - Result := Pointer( NativeUint(Result) + $20 - NativeUint(Result) and $1F ); -end; - - procedure TCipher_ChaCha20.DoInit(const Key; Size: Integer); // from chacha-prng.h const cChaChaConst : Array[0..15] of AnsiChar = 'expand 32-byte k'; @@ -7118,7 +7697,7 @@ procedure TCipher_ChaCha20.SetChaChaMode(const Value: TChaChaMode); {$IFNDEF PUREPASCAL} -{$IFDEF x86} +{$IFDEF X86ASM} procedure FullBlockSSE(ChaChaMtx : PChaChaAVXMtx; Source, Dest: Pointer); register; {$IFDEF FPC}assembler;{$ENDIF} // eax = ChaChaMtx, edx = source, ecx = dest @@ -7192,7 +7771,7 @@ procedure FullBlockAVX(ChaChaMtx : PChaChaAVXMtx; Source, Dest: Pointer); regist {$ENDIF} -{$IFDEF x64} +{$IFDEF X64ASM} procedure FullBlockSSE(ChaChaMtx : PChaChaAVXMtx; Source, Dest: Pointer); // rcx = ChaChaMtx, rdx = source, r8 = dest asm @@ -7293,7 +7872,7 @@ procedure FullBlockAVX(ChaChaMtx : PChaChaAVXMtx; Source, Dest: Pointer); 11, 8, 9, 10, 15, 12, 13, 14); -{$IFDEF x86} +{$IFDEF X86ASM} procedure AVXChaChaDoubleQuarterRound( chachaMtx : PChaChaAVXMtx ); {$IFDEF FPC} assembler; {$ELSE} register; {$ENDIF} asm @@ -7421,7 +8000,7 @@ procedure AVXRealingAndAddMtx( chaChaMtx : PChaChaAVXMtx; inpChaCha : PChaChaMtx end; {$ENDIF} -{$IFDEF x64} +{$IFDEF X64ASM} procedure AVXChaChaDoubleQuarterRound( chachaMtx : PChaChaAVXMtx ); var dYMM4, dYMM5 : Array[0..4] of int64; @@ -7436,7 +8015,6 @@ procedure AVXChaChaDoubleQuarterRound( chachaMtx : PChaChaAVXMtx ); // in our case only rdi to rcx mov rcx, rdi; {$ENDIF} - {$IFDEF x64} {$IFDEF AVXSUP}vmovupd dYMM4, ymm4; {$ELSE}db $C5,$FD,$11,$65,$D8;{$ENDIF} {$IFDEF AVXSUP}vmovupd dYMM5, ymm5; {$ELSE}db $C5,$FD,$11,$6D,$B8;{$ENDIF} @@ -7530,7 +8108,6 @@ procedure AVXChaChaDoubleQuarterRound( chachaMtx : PChaChaAVXMtx ); {$IFDEF AVXSUP}vmovupd ymm4, dYMM4; {$ELSE}db $C5,$FD,$10,$65,$D8;{$ENDIF} {$IFDEF AVXSUP}vmovupd ymm5, dYMM5; {$ELSE}db $C5,$FD,$10,$6D,$B8;{$ENDIF} {$IFDEF AVXSUP}vzeroupper; {$ELSE}db $C5,$F8,$77;{$ENDIF} -{$ENDIF} end; {$IFDEF FPC} end; @@ -7597,7 +8174,7 @@ procedure AVXRealingAndAddMtx( chaChaMtx : PChaChaAVXMtx; inpChaCha : PChaChaMtx {$ENDIF} -{$ENDIF} +{$ENDIF} // PUREPASCAL procedure TCipher_ChaCha20.AfterConstruction; begin @@ -7609,8 +8186,6 @@ procedure TCipher_ChaCha20.AfterConstruction; {$IFDEF PUREPASCAL} fFullBlockFunc := FullBlockPas; {$ELSE} - fFullBlockFunc := FullBlockPas; - case CpuMode of cmSSE: if TDEC_CPUSupport.SSE3 then fFullBlockFunc := FullBlockSSE; @@ -7743,11 +8318,11 @@ procedure TCipher_ChaCha20.PasChaChaDoubleQuarterRound(mtx : PChaChaMtx); procedure TCipher_ChaCha20.SSEChaChaDoubleQuarterRound(mtx : PChaChaMtx); // 32Bit: ecx = self, edx = mtx // 64bit: rcx = self, rdx = mtx -{$IFDEF x64} +{$IFDEF CPUX64} var dXMM4, dXMM5 : Array[0..1] of Int64; {$ENDIF} asm - {$IFDEF x64} + {$IFDEF CPUX64} // rcx seems to have "self" as reference {$IFDEF UNIX} // Linux uses a diffrent ABI -> copy over the registers so they meet with winABI @@ -7854,7 +8429,7 @@ procedure TCipher_ChaCha20.SSEChaChaDoubleQuarterRound(mtx : PChaChaMtx); palignr xmm3, xmm4, 4; // move back - {$IFDEF x64} + {$IFDEF CPUX64} movdqa [rdx], xmm0; movdqa [rdx + 16], xmm1; movdqa [rdx + 32], xmm2; @@ -8006,7 +8581,6 @@ initialization {$ENDIF} {$ENDIF} - {$IFDEF CPUx86} if TDEC_CPUSupport.AVX2 then TCipher_ChaCha20.CpuMode := cmAVX @@ -8015,7 +8589,8 @@ initialization TCipher_ChaCha20.CpuMode := cmSSE else TCipher_ChaCha20.CpuMode := cmPas; - {$ENDIF} + + TCipher_Rijndael.UseAESAsm := TDEC_CPUSupport.AES; finalization diff --git a/Source/DECOptions.inc b/Source/DECOptions.inc index 8ed9810..a76838b 100644 --- a/Source/DECOptions.inc +++ b/Source/DECOptions.inc @@ -73,7 +73,7 @@ // Automatically register all hash classes {.$DEFINE ManualRegisterHashClasses} (* default off *) -{.$DEFINE ASSEMBLER} +{$DEFINE ASSEMBLER} // if the compiler does not support assembler turn usage off and even if restrict // it to Windows, as those non Windows platforms which actually do support ASM @@ -83,21 +83,29 @@ {$ifndef CPUX64} {$define CPUX64} {$endif} - {$else}{$if defined(CPU386) or defined(CPUI386) } + {$IFEND} + + {$if defined(CPU386) or defined(CPUI386) } {$ifndef CPUX32} {$define CPUX32} {$endif} - {$ELSE} - {$IFNDEF ASSEMBLER } - {$DEFINE NO_ASM} (* default ON *) - {$ELSE} - {$IFDEF WINDOWS} - {.$DEFINE NO_ASM} (* default OFF *) - {$ELSE} - {$DEFINE NO_ASM} (* default ON *) - {$ENDIF} - {$ENDIF} - {$ENDIF}{$endif} + {$IFEND} + + {$IFNDEF ASSEMBLER} + {$DEFINE NO_ASM} + {$ENDIF} + {$IFDEF UNIX} + {$DEFINE NO_ASM} + {$ENDIF} + // {$IFNDEF ASSEMBLER } +// {$DEFINE NO_ASM} (* default ON *) +// {$ELSE} +// {$IFDEF WINDOWS} +// {.$DEFINE NO_ASM} (* default OFF *) +// {$ELSE} +// {$DEFINE NO_ASM} (* default ON *) +// {$ENDIF} +// {$ENDIF} {$ELSE} // Turn ASM off for FPC as we do not know enough about ASM support on FPC {$DEFINE NO_ASM} @@ -195,16 +203,16 @@ {$IFNDEF PUREPASCAL} // ignored by FPC (already in PurePascal mode as defined in FPC block above) + {$DEFINE PUREPASCAL} {$IFDEF CPUX86} {$DEFINE X86ASM} - {$ELSE !CPUX86} - {$IFDEF CPUX64} - {$DEFINE X64ASM} - {$ELSE !CPUX64} - {$DEFINE PUREPASCAL} - {$ENDIF !CPUX64} + {$UNDEF PUREPASCAL} {$ENDIF} -{$ENDIF !PUREPASCAL} + {$IFDEF CPUX64} + {$DEFINE X64ASM} + {$UNDEF PUREPASCAL} + {$ENDIF} +{$ENDIF} //------------------------------------------------------------------------------ // Delphi and C++ Builder diff --git a/Source/DECUtil.pas b/Source/DECUtil.pas index a47882c..14cb71a 100644 --- a/Source/DECUtil.pas +++ b/Source/DECUtil.pas @@ -281,6 +281,17 @@ function StringToBytes(const Str: string): TBytes; inline; /// function IsEqual(const a, b : TBytes ):Boolean; +/// +/// Returns the 32Byte aligned address of pointer A +/// +/// /// +/// Pointer to input memory block +/// +/// /// +/// Returns the fir +/// +function AlignPtr32( A : Pointer ) : Pointer; + implementation uses @@ -680,4 +691,12 @@ function IsEqual(const a, b : TBytes):Boolean; Result := true; end; +function AlignPtr32( A : Pointer ) : Pointer; +begin + Result := A; + if (NativeUint(A) and $1F) <> 0 then + Result := Pointer( NativeUint(Result) + $20 - NativeUint(Result) and $1F ); +end; + + end. diff --git a/Source/x86_64/DECUtil.inc b/Source/x86_64/DECUtil.inc index 838a134..0cbecc7 100644 --- a/Source/x86_64/DECUtil.inc +++ b/Source/x86_64/DECUtil.inc @@ -6,9 +6,8 @@ {$define SwapUInt32_asm} function SwapUInt32(Source: UInt32): UInt32; asm - MOV RAX, Source - BSWAP RAX - SHR RAX, 32 + MOV EAX, Source + BSWAP EAX end; {$endif} diff --git a/Unit Tests/Tests/TestDECCipher.pas b/Unit Tests/Tests/TestDECCipher.pas index d72456f..b8550f0 100644 --- a/Unit Tests/Tests/TestDECCipher.pas +++ b/Unit Tests/Tests/TestDECCipher.pas @@ -4587,7 +4587,7 @@ procedure TestTCipher_AES.TestContext; CheckEquals( 32, ReturnValue.KeySize); CheckEquals( 16, ReturnValue.BlockSize); CheckEquals( 16, ReturnValue.BufferSize); - CheckEquals( 480, ReturnValue.AdditionalBufferSize); + CheckEquals( 512, ReturnValue.AdditionalBufferSize); CheckEquals( 1, ReturnValue.MinRounds); CheckEquals( 1, ReturnValue.MaxRounds); CheckEquals(false, ReturnValue.NeedsAdditionalBufferBackup); @@ -4675,7 +4675,7 @@ procedure TestTCipher_Rijndael.TestContext; CheckEquals( 32, ReturnValue.KeySize); CheckEquals( 16, ReturnValue.BlockSize); CheckEquals( 16, ReturnValue.BufferSize); - CheckEquals( 480, ReturnValue.AdditionalBufferSize); + CheckEquals( 512, ReturnValue.AdditionalBufferSize); CheckEquals( 1, ReturnValue.MinRounds); CheckEquals( 1, ReturnValue.MaxRounds); CheckEquals(false, ReturnValue.NeedsAdditionalBufferBackup);