Einzelnen Beitrag anzeigen

Kas Ob.

Registriert seit: 3. Sep 2023
436 Beiträge
 
#10

AW: Umfrage/Architekturfrage zur DEC

  Alt 20. Mai 2025, 09:51
Well here ChaCha implementation to save you both some time, it is correct and working but i mixed the following code form two different project in haste, also while i tested it for many edge case, it is far from optimized for speed
Delphi-Quellcode:
unit ChaCha;

interface

uses
  SysUtils;

type
  TChaChaRounds = (cr8, cr12, cr20);
  TChaChaKey = array[0..31] of Byte; // 256-bit key
  TChaChaNonce = array[0..11] of Byte; // 96-bit nonce
  TChaChaBlock = array[0..63] of Byte; // 512-bit block

  TChaCha = class
  private
    FState: array[0..15] of Cardinal; // Internal state
    procedure QuarterRound(var a, b, c, d: Cardinal);
    procedure InnerBlock;
    procedure SetupState(const Key: TChaChaKey; const Nonce: TChaChaNonce; Counter: Cardinal);
    {$IFDEF CHACHA_DEBUG}
    procedure DebugState(const DbgStr: string);
    {$ENDIF}
  public
    procedure Encrypt(const Key: TChaChaKey; const Nonce: TChaChaNonce;
      Counter: Cardinal; Rounds: TChaChaRounds; const Input: TBytes; var Output: TBytes);
    class procedure SelfTest;
  end;

implementation

const
  SIGMA: array[0..3] of Cardinal = ($61707865, $3320646e, $79622d32, $6b206574); // "expand 32-byte k"

{ TChaCha }

{$IFDEF CHACHA_DEBUG}
procedure TChaCha.DebugState(const DbgStr: string);
var
  I: Integer;
begin
  WriteLn(DbgStr);
  for I := 0 to 15 do
  begin
    Write(Format('%08x ', [FState[I]]));
    if (I + 1) mod 4 = 0 then WriteLn;
  end;
  WriteLn;
end;
{$ENDIF}

procedure TChaCha.QuarterRound(var a, b, c, d: Cardinal);
begin
{$IFOPT R+}{$DEFINE RND_HasRangeChecks}{$ENDIF}
{$IFOPT Q+}{$DEFINE RND_HasOverflowChecks}{$ENDIF}
{$RANGECHECKS OFF}
{$OVERFLOWCHECKS OFF}
  a := a + b; d := d xor a; d := (d shl 16) or (d shr 16);
  c := c + d; b := b xor c; b := (b shl 12) or (b shr 20);
  a := a + b; d := d xor a; d := (d shl 8) or (d shr 24);
  c := c + d; b := b xor c; b := (b shl 7) or (b shr 25);
{$IFDEF RND_HasRangeChecks}{$RANGECHECKS ON}{$ENDIF}
{$IFDEF RND_HasOverflowChecks}{$OVERFLOWCHECKS ON}{$ENDIF}
end;

procedure TChaCha.InnerBlock;
var
  x: array[0..15] of Cardinal;
begin
  Move(FState, x, SizeOf(x));
  QuarterRound(x[0], x[4], x[8], x[12]);
  QuarterRound(x[1], x[5], x[9], x[13]);
  QuarterRound(x[2], x[6], x[10], x[14]);
  QuarterRound(x[3], x[7], x[11], x[15]);
  QuarterRound(x[0], x[5], x[10], x[15]);
  QuarterRound(x[1], x[6], x[11], x[12]);
  QuarterRound(x[2], x[7], x[8], x[13]);
  QuarterRound(x[3], x[4], x[9], x[14]);
  Move(x, FState, SizeOf(FState));
end;

procedure TChaCha.SetupState(const Key: TChaChaKey; const Nonce: TChaChaNonce; Counter: Cardinal);
begin
  FState[0] := SIGMA[0];
  FState[1] := SIGMA[1];
  FState[2] := SIGMA[2];
  FState[3] := SIGMA[3];
  Move(Key[0], FState[4], 32);
  FState[12] := Counter;
  Move(Nonce[0], FState[13], 12);
end;

procedure ChaChaBlockSIMD(State: Pointer; Rounds: Integer);
{$IF Defined(CPUX86) or Defined(CPUX64)}
asm
  // Save state
  movdqu xmm0, dqword [State+00] // Use movdqa for aligned access or switch to movdqu for unaligned
  movdqu xmm1, dqword [State+16]
  movdqu xmm2, dqword [State+32]
  movdqu xmm3, dqword [State+48]

  // Main loop
@ROUND:
  // Column round
  paddd xmm0, xmm1
  pxor xmm3, xmm0
  pshuflw xmm3, xmm3, 0B1h
  pshufhw xmm3, xmm3, 0B1h
  paddd xmm2, xmm3
  pxor xmm1, xmm2
  movdqa xmm4, xmm1
  pslld xmm1, 12
  psrld xmm4, 20
  por xmm1, xmm4
  paddd xmm0, xmm1
  pxor xmm3, xmm0
  movdqa xmm4, xmm3
  pslld xmm3, 8
  psrld xmm4, 24
  por xmm3, xmm4
  paddd xmm2, xmm3
  pxor xmm1, xmm2
  movdqa xmm4, xmm1
  pslld xmm1, 7
  psrld xmm4, 25
  por xmm1, xmm4

  // Shuffle for diagonal
  pshufd xmm1, xmm1, 39h
  pshufd xmm2, xmm2, 4Eh
  pshufd xmm3, xmm3, 93h

  // Diagonal round
  paddd xmm0, xmm1
  pxor xmm3, xmm0
  pshuflw xmm3, xmm3, 0B1h
  pshufhw xmm3, xmm3, 0B1h
  paddd xmm2, xmm3
  pxor xmm1, xmm2
  movdqa xmm4, xmm1
  pslld xmm1, 12
  psrld xmm4, 20
  por xmm1, xmm4
  paddd xmm0, xmm1
  pxor xmm3, xmm0
  movdqa xmm4, xmm3
  pslld xmm3, 8
  psrld xmm4, 24
  por xmm3, xmm4
  paddd xmm2, xmm3
  pxor xmm1, xmm2
  movdqa xmm4, xmm1
  pslld xmm1, 7
  psrld xmm4, 25
  por xmm1, xmm4

  // Shuffle back
  pshufd xmm1, xmm1, 93h
  pshufd xmm2, xmm2, 4Eh
  pshufd xmm3, xmm3, 39h

  dec Rounds
  jnz @ROUND

  {// Add original state
  paddd xmm0, dqword [State+00]    // Use dqword for aligned access, paddd must have memory aligned !
  paddd xmm1, dqword [State+16]
  paddd xmm2, dqword [State+32]
  paddd xmm3, dqword [State+48]  }

  movdqu xmm4, dqword [State+00] // unaligned memory state addition
  paddd xmm0, xmm4
  movdqu xmm4, dqword [State+16]
  paddd xmm1, xmm4
  movdqu xmm4, dqword [State+32]
  paddd xmm2, xmm4
  movdqu xmm4, dqword [State+48]
  paddd xmm3, xmm4

  // Store result
  movdqu dqword [State+00], xmm0 // Use movdqa for aligned store or switch to movdqu for unaligned
  movdqu dqword [State+16], xmm1
  movdqu dqword [State+32], xmm2
  movdqu dqword [State+48], xmm3

end;
{$ELSE}
begin
  RaiseError(ERR_SIMD_NOT_SUPPORTED);
end;
{$ENDIF}

const SIMDEnabled = true;

procedure TChaCha.Encrypt(const Key: TChaChaKey; const Nonce: TChaChaNonce;
  Counter: Cardinal; Rounds: TChaChaRounds; const Input: TBytes; var Output: TBytes);
var
  I, J, RoundCount: Integer;
  OrigState: array[0..15] of Cardinal;
  Keystream: TChaChaBlock;
  PKeystream: PByte;
begin
  SetLength(Output, Length(Input));
  if Length(Input) = 0 then
    Exit;

  case Rounds of
    cr8: RoundCount := 8;
    cr12: RoundCount := 12;
    cr20: RoundCount := 20;
  else
    RoundCount := 20;
  end;

  for I := 0 to (Length(Input) - 1) div 64 do
  begin
    SetupState(Key, Nonce, Counter + Cardinal(I));
    Move(FState, OrigState, SizeOf(FState));
    {$IFDEF CHACHA_DEBUG}
    DebugState('Before InnerBlock:');
    {$ENDIF}

    if SIMDEnabled then
      begin
        ChaChaBlockSIMD(@FState[0],RoundCount shr 1);
      end else
      begin
        for J := 1 to RoundCount shr 1 do
        begin
          InnerBlock;
          {$IFDEF CHACHA_DEBUG}
          DebugState(Format('After InnerBlock %d:', [J]));
          {$ENDIF}
        end;
        for J := 0 to 15 do
          FState[J] := Cardinal(UInt64(FState[J]) + UInt64(OrigState[J]));
      end;

    {$IFDEF CHACHA_DEBUG}
    DebugState('After adding OrigState:');
    {$ENDIF}
    Move(FState, Keystream, SizeOf(Keystream));
    {$IFDEF CHACHA_DEBUG}
    Write('Keystream: ');
    for J := 0 to 63 do
      Write(Format('%02x ', [Keystream[J]]));
    WriteLn;
    {$ENDIF}
    PKeystream := @Keystream;
    for J := 0 to 63 do
    begin
      if (I * 64 + J) >= Length(Input) then Break;
      Output[I * 64 + J] := Input[I * 64 + J] xor PKeystream^;
      Inc(PKeystream);
    end;
  end;

  // erase memory
  FillChar(FState, SizeOf(FState), 0);
  FillChar(Keystream, SizeOf(Keystream), 0);
  FillChar(OrigState, SizeOf(OrigState), 0);
end;

class procedure TChaCha.SelfTest;
const
  KEY1: TChaChaKey = (
    $00, $01, $02, $03, $04, $05, $06, $07,
    $08, $09, $0a, $0b, $0c, $0d, $0e, $0f,
    $10, $11, $12, $13, $14, $15, $16, $17,
    $18, $19, $1a, $1b, $1c, $1d, $1e, $1f
  );
  NONCE1: TChaChaNonce = (
    $00, $00, $00, $00, $00, $00, $00, $4a, $00, $00, $00, $00
  );
  PLAINTEXT1: array[0..113] of Byte = (
    $4c, $61, $64, $69, $65, $73, $20, $61, $6e, $64, $20, $47, $65, $6e, $74, $6c,
    $65, $6d, $65, $6e, $20, $6f, $66, $20, $74, $68, $65, $20, $63, $6c, $61, $73,
    $73, $20, $6f, $66, $20, $27, $39, $39, $3a, $20, $49, $66, $20, $49, $20, $63,
    $6f, $75, $6c, $64, $20, $6f, $66, $66, $65, $72, $20, $79, $6f, $75, $20, $6f,
    $6e, $6c, $79, $20, $6f, $6e, $65, $20, $74, $69, $70, $20, $66, $6f, $72, $20,
    $74, $68, $65, $20, $66, $75, $74, $75, $72, $65, $2c, $20, $73, $75, $6e, $73,
    $63, $72, $65, $65, $6e, $20, $77, $6f, $75, $6c, $64, $20, $62, $65, $20, $69,
    $74, $2e
  );
  EXPECTED_CIPHERTEXT1: array[0..113] of Byte = (
    $6e, $2e, $35, $9a, $25, $68, $f9, $80, $41, $ba, $07, $28, $dd, $0d, $69, $81,
    $e9, $7e, $7a, $ec, $1d, $43, $60, $c2, $0a, $27, $af, $cc, $fd, $9f, $ae, $0b,
    $f9, $1b, $65, $c5, $52, $47, $33, $ab, $8f, $59, $3d, $ab, $cd, $62, $b3, $57,
    $16, $39, $d6, $24, $e6, $51, $52, $ab, $8f, $53, $0c, $35, $9f, $08, $61, $d8,
    $07, $ca, $0d, $bf, $50, $0d, $6a, $61, $56, $a3, $8e, $08, $8a, $22, $b6, $5e,
    $52, $bc, $51, $4d, $16, $cc, $f8, $06, $81, $8c, $e9, $1a, $b7, $79, $37, $36,
    $5a, $f9, $0b, $bf, $74, $a3, $5b, $e6, $b4, $0b, $8e, $ed, $f2, $78, $5e, $42,
    $87, $4d
  );
  KEY2: TChaChaKey = (
    $00, $00, $00, $00, $00, $00, $00, $00,
    $00, $00, $00, $00, $00, $00, $00, $00,
    $00, $00, $00, $00, $00, $00, $00, $00,
    $00, $00, $00, $00, $00, $00, $00, $01
  );
  NONCE2: TChaChaNonce = (
    $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $02
  );
  PLAINTEXT2: array[0..374] of Byte = (
    $41, $6e, $79, $20, $73, $75, $62, $6d, $69, $73, $73, $69, $6f, $6e, $20, $74,
    $6f, $20, $74, $68, $65, $20, $49, $45, $54, $46, $20, $69, $6e, $74, $65, $6e,
    $64, $65, $64, $20, $62, $79, $20, $74, $68, $65, $20, $43, $6f, $6e, $74, $72,
    $69, $62, $75, $74, $6f, $72, $20, $66, $6f, $72, $20, $70, $75, $62, $6c, $69,
    $63, $61, $74, $69, $6f, $6e, $20, $61, $73, $20, $61, $6c, $6c, $20, $6f, $72,
    $20, $70, $61, $72, $74, $20, $6f, $66, $20, $61, $6e, $20, $49, $45, $54, $46,
    $20, $49, $6e, $74, $65, $72, $6e, $65, $74, $2d, $44, $72, $61, $66, $74, $20,
    $6f, $72, $20, $52, $46, $43, $20, $61, $6e, $64, $20, $61, $6e, $79, $20, $73,
    $74, $61, $74, $65, $6d, $65, $6e, $74, $20, $6d, $61, $64, $65, $20, $77, $69,
    $74, $68, $69, $6e, $20, $74, $68, $65, $20, $63, $6f, $6e, $74, $65, $78, $74,
    $20, $6f, $66, $20, $61, $6e, $20, $49, $45, $54, $46, $20, $61, $63, $74, $69,
    $76, $69, $74, $79, $20, $69, $73, $20, $63, $6f, $6e, $73, $69, $64, $65, $72,
    $65, $64, $20, $61, $6e, $20, $22, $49, $45, $54, $46, $20, $43, $6f, $6e, $74,
    $72, $69, $62, $75, $74, $69, $6f, $6e, $22, $2e, $20, $53, $75, $63, $68, $20,
    $73, $74, $61, $74, $65, $6d, $65, $6e, $74, $73, $20, $69, $6e, $63, $6c, $75,
    $64, $65, $20, $6f, $72, $61, $6c, $20, $73, $74, $61, $74, $65, $6d, $65, $6e,
    $74, $73, $20, $69, $6e, $20, $49, $45, $54, $46, $20, $73, $65, $73, $73, $69,
    $6f, $6e, $73, $2c, $20, $61, $73, $20, $77, $65, $6c, $6c, $20, $61, $73, $20,
    $77, $72, $69, $74, $74, $65, $6e, $20, $61, $6e, $64, $20, $65, $6c, $65, $63,
    $74, $72, $6f, $6e, $69, $63, $20, $63, $6f, $6d, $6d, $75, $6e, $69, $63, $61,
    $74, $69, $6f, $6e, $73, $20, $6d, $61, $64, $65, $20, $61, $74, $20, $61, $6e,
    $79, $20, $74, $69, $6d, $65, $20, $6f, $72, $20, $70, $6c, $61, $63, $65, $2c,
    $20, $77, $68, $69, $63, $68, $20, $61, $72, $65, $20, $61, $64, $64, $72, $65,
    $73, $73, $65, $64, $20, $74, $6f
  );
  EXPECTED_CIPHERTEXT2: array[0..374] of Byte = (
    $a3, $fb, $f0, $7d, $f3, $fa, $2f, $de, $4f, $37, $6c, $a2, $3e, $82, $73, $70,
    $41, $60, $5d, $9f, $4f, $4f, $57, $bd, $8c, $ff, $2c, $1d, $4b, $79, $55, $ec,
    $2a, $97, $94, $8b, $d3, $72, $29, $15, $c8, $f3, $d3, $37, $f7, $d3, $70, $05,
    $0e, $9e, $96, $d6, $47, $b7, $c3, $9f, $56, $e0, $31, $ca, $5e, $b6, $25, $0d,
    $40, $42, $e0, $27, $85, $ec, $ec, $fa, $4b, $4b, $b5, $e8, $ea, $d0, $44, $0e,
    $20, $b6, $e8, $db, $09, $d8, $81, $a7, $c6, $13, $2f, $42, $0e, $52, $79, $50,
    $42, $bd, $fa, $77, $73, $d8, $a9, $05, $14, $47, $b3, $29, $1c, $e1, $41, $1c,
    $68, $04, $65, $55, $2a, $a6, $c4, $05, $b7, $76, $4d, $5e, $87, $be, $a8, $5a,
    $d0, $0f, $84, $49, $ed, $8f, $72, $d0, $d6, $62, $ab, $05, $26, $91, $ca, $66,
    $42, $4b, $c8, $6d, $2d, $f8, $0e, $a4, $1f, $43, $ab, $f9, $37, $d3, $25, $9d,
    $c4, $b2, $d0, $df, $b4, $8a, $6c, $91, $39, $dd, $d7, $f7, $69, $66, $e9, $28,
    $e6, $35, $55, $3b, $a7, $6c, $5c, $87, $9d, $7b, $35, $d4, $9e, $b2, $e6, $2b,
    $08, $71, $cd, $ac, $63, $89, $39, $e2, $5e, $8a, $1e, $0e, $f9, $d5, $28, $0f,
    $a8, $ca, $32, $8b, $35, $1c, $3c, $76, $59, $89, $cb, $cf, $3d, $aa, $8b, $6c,
    $cc, $3a, $af, $9f, $39, $79, $c9, $2b, $37, $20, $fc, $88, $dc, $95, $ed, $84,
    $a1, $be, $05, $9c, $64, $99, $b9, $fd, $a2, $36, $e7, $e8, $18, $b0, $4b, $0b,
    $c3, $9c, $1e, $87, $6b, $19, $3b, $fe, $55, $69, $75, $3f, $88, $12, $8c, $c0,
    $8a, $aa, $9b, $63, $d1, $a1, $6f, $80, $ef, $25, $54, $d7, $18, $9c, $41, $1f,
    $58, $69, $ca, $52, $c5, $b8, $3f, $a3, $6f, $f2, $16, $b9, $c1, $d3, $00, $62,
    $be, $bc, $fd, $2d, $c5, $bc, $e0, $91, $19, $34, $fd, $a7, $9a, $86, $f6, $e6,
    $98, $ce, $d7, $59, $c3, $ff, $9b, $64, $77, $33, $8f, $3d, $a4, $f9, $cd, $85,
    $14, $ea, $99, $82, $cc, $af, $b3, $41, $b2, $38, $4d, $d9, $02, $f3, $d1, $ab,
    $7a, $c6, $1d, $d2, $9c, $6f, $21, $ba, $5b, $86, $2f, $37, $30, $e3, $7c, $fd,
    $c4, $fd, $80, $6c, $22, $f2, $21
  );
var
  ChaCha: TChaCha;
  Input, Output: TBytes;
  I: Integer;
  Pass: Boolean;
begin
  ChaCha := TChaCha.Create;
  try
    SetLength(Input, Length(PLAINTEXT1));
    Move(PLAINTEXT1, Input[0], Length(PLAINTEXT1));
    ChaCha.Encrypt(KEY1, NONCE1, 1, cr20, Input, Output);
    Pass := True;
    if Length(Output) <> Length(EXPECTED_CIPHERTEXT1) then
      Pass := False
    else
      for I := 0 to Length(EXPECTED_CIPHERTEXT1) - 1 do
        if Output[I] <> EXPECTED_CIPHERTEXT1[I] then
        begin
          Pass := False;
          WriteLn(Format('Test Vector 1: Mismatch at byte %d: got %02x, expected %02x', [I, Output[I], EXPECTED_CIPHERTEXT1[I]]));
          Break;
        end;
    if not Pass then
      raise Exception.Create('ChaCha20 Test Vector 1 failed!');

    SetLength(Input, Length(PLAINTEXT2));
    Move(PLAINTEXT2, Input[0], Length(PLAINTEXT2));
    ChaCha.Encrypt(KEY2, NONCE2, 1, cr20, Input, Output);
    Pass := True;
    if Length(Output) <> Length(EXPECTED_CIPHERTEXT2) then
      Pass := False
    else
      for I := 0 to Length(EXPECTED_CIPHERTEXT2) - 1 do
        if Output[I] <> EXPECTED_CIPHERTEXT2[I] then
        begin
          Pass := False;
          WriteLn(Format('Test Vector 2: Mismatch at byte %d: got %02x, expected %02x', [I, Output[I], EXPECTED_CIPHERTEXT2[I]]));
          Break;
        end;
    if not Pass then
      raise Exception.Create('ChaCha20 Test Vector 2 failed!');

    SetLength(Input, Length(PLAINTEXT1));
    Move(PLAINTEXT1, Input[0], Length(PLAINTEXT1));
    ChaCha.Encrypt(KEY1, NONCE1, 1, cr8, Input, Output);
    ChaCha.Encrypt(KEY1, NONCE1, 1, cr12, Input, Output);

    WriteLn('ChaCha self-test passed successfully.');
  finally
    ChaCha.Free;
  end;
end;

end.
Kas
  Mit Zitat antworten Zitat