- Move all .pas source files from root to src/ directory - Add cometcrypt.pas (X25519 key exchange + ChaCha20 encryption) - Add test_crypt.pas and test_outbound.pas - Add contrib/ (systemd service, install script) - Add COMET.QA quality assurance checklist - Update Makefile for src/ layout - Update FSP-COMET.001 protocol spec - Remove comet.exe binary from repo
130 lines
2.8 KiB
ObjectPascal
130 lines
2.8 KiB
ObjectPascal
{
|
|
Comet - Direct TCP File Transfer for FidoNet
|
|
cometcrc.pas - CRC-32 calculation
|
|
|
|
Standard reflected CRC-32 (ISO 3309 / ITU-T V.42):
|
|
Polynomial: 0xEDB88320 (reflected form of 0x04C11DB7)
|
|
Initial: 0xFFFFFFFF
|
|
Post-process: XOR with 0xFFFFFFFF (bitwise inversion)
|
|
Byte order: little-endian on wire
|
|
|
|
Same algorithm as ZModem, Janus, Hydra, PKZip, Ethernet, and Nova.
|
|
Output is byte-identical to Xenia's crc32block() and Fimail's CRC32Block().
|
|
|
|
Test vectors:
|
|
CRC32("") = 0x00000000 (empty input returns 0, not post-processed)
|
|
CRC32("123456789") = 0xCBF43926
|
|
|
|
Copyright (C) 2026 Ken Johnson
|
|
License: GPL-2.0
|
|
}
|
|
unit cometcrc;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
{ Update CRC-32 with a single byte }
|
|
function CRC32Update(CRC: LongWord; B: Byte): LongWord;
|
|
|
|
{ Compute CRC-32 of a buffer. Init and post-process are handled internally.
|
|
Returns the final CRC-32 value ready for use on the wire. }
|
|
function CRC32Block(const Buf; Len: LongInt): LongWord;
|
|
|
|
{ Compute CRC-32 of a string }
|
|
function CRC32String(const S: string): LongWord;
|
|
|
|
{ Incremental CRC-32: start with this value }
|
|
function CRC32Init: LongWord;
|
|
|
|
{ Incremental CRC-32: update with a buffer of bytes }
|
|
function CRC32UpdateBlock(CRC: LongWord; const Buf; Len: LongInt): LongWord;
|
|
|
|
{ Incremental CRC-32: finalize (XOR with $FFFFFFFF) }
|
|
function CRC32Finish(CRC: LongWord): LongWord;
|
|
|
|
|
|
implementation
|
|
|
|
var
|
|
CRC32Tab: array[0..255] of LongWord;
|
|
TabReady: Boolean = False;
|
|
|
|
procedure InitTable;
|
|
var
|
|
I, J: Integer;
|
|
CRC: LongWord;
|
|
begin
|
|
if TabReady then Exit;
|
|
|
|
for I := 0 to 255 do
|
|
begin
|
|
CRC := LongWord(I);
|
|
for J := 0 to 7 do
|
|
begin
|
|
if (CRC and 1) <> 0 then
|
|
CRC := (CRC shr 1) xor $EDB88320
|
|
else
|
|
CRC := CRC shr 1;
|
|
end;
|
|
CRC32Tab[I] := CRC;
|
|
end;
|
|
|
|
TabReady := True;
|
|
end;
|
|
|
|
|
|
function CRC32Update(CRC: LongWord; B: Byte): LongWord;
|
|
begin
|
|
if not TabReady then InitTable;
|
|
Result := CRC32Tab[(CRC xor B) and $FF] xor (CRC shr 8);
|
|
end;
|
|
|
|
function CRC32Init: LongWord;
|
|
begin
|
|
Result := $FFFFFFFF;
|
|
end;
|
|
|
|
function CRC32UpdateBlock(CRC: LongWord; const Buf; Len: LongInt): LongWord;
|
|
var
|
|
P: PByte;
|
|
I: LongInt;
|
|
begin
|
|
if not TabReady then InitTable;
|
|
Result := CRC;
|
|
P := @Buf;
|
|
for I := 0 to Len - 1 do
|
|
begin
|
|
Result := CRC32Tab[(Result xor P^) and $FF] xor (Result shr 8);
|
|
Inc(P);
|
|
end;
|
|
end;
|
|
|
|
function CRC32Finish(CRC: LongWord): LongWord;
|
|
begin
|
|
Result := CRC xor $FFFFFFFF;
|
|
end;
|
|
|
|
function CRC32Block(const Buf; Len: LongInt): LongWord;
|
|
begin
|
|
if not TabReady then InitTable;
|
|
if Len <= 0 then
|
|
begin
|
|
Result := 0;
|
|
Exit;
|
|
end;
|
|
Result := CRC32UpdateBlock($FFFFFFFF, Buf, Len);
|
|
Result := Result xor $FFFFFFFF;
|
|
end;
|
|
|
|
function CRC32String(const S: string): LongWord;
|
|
begin
|
|
if Length(S) = 0 then
|
|
Result := 0
|
|
else
|
|
Result := CRC32Block(S[1], Length(S));
|
|
end;
|
|
|
|
|
|
end.
|