xtool/contrib/fundamentals/Cipher/flcCipherRC2.pas

274 lines
12 KiB
ObjectPascal

{******************************************************************************}
{ }
{ Library: Fundamentals 5.00 }
{ File name: flcCipherRC2.pas }
{ File version: 5.01 }
{ Description: RC2 cipher routines }
{ }
{ Copyright: Copyright (c) 2007-2020, David J Butler }
{ All rights reserved. }
{ This file is licensed under the BSD License. }
{ See http://www.opensource.org/licenses/bsd-license.php }
{ Redistribution and use in source and binary forms, with }
{ or without modification, are permitted provided that }
{ the following conditions are met: }
{ Redistributions of source code must retain the above }
{ copyright notice, this list of conditions and the }
{ following disclaimer. }
{ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND }
{ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED }
{ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED }
{ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A }
{ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL }
{ THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, }
{ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR }
{ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF }
{ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) }
{ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER }
{ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING }
{ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE }
{ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE }
{ POSSIBILITY OF SUCH DAMAGE. }
{ }
{ Github: https://github.com/fundamentalslib }
{ E-mail: fundamentals.library at gmail.com }
{ }
{ Revision history: }
{ }
{ 2007/01/05 4.01 Initial version. }
{ }
{******************************************************************************}
{$INCLUDE flcCipher.inc}
unit flcCipherRC2;
interface
{ }
{ RC2 }
{ RC2 was developed by Ron Rivest in 1987. }
{ }
type
TRC2CipherKey = packed record
case Integer of
0 : (Bytes: array[0..127] of Byte);
1 : (Words: array[0..63] of Word);
end;
PRC2CipherKey = ^TRC2CipherKey;
TRC2Block = packed record
case Integer of
0 : (Bytes: array[0..7] of Byte);
1 : (Words: array[0..3] of Word);
2 : (A, B, C, D: Word);
end;
PRC2Block = ^TRC2Block;
const
RC2BlockSize = 8;
procedure RC2Init(const Key; const KeySize: Integer; const KeyBits: Integer;
var CipherKey: TRC2CipherKey);
procedure RC2EncryptBlock(const CipherKey: TRC2CipherKey; var Block: TRC2Block);
procedure RC2DecryptBlock(const CipherKey: TRC2CipherKey; var Block: TRC2Block);
implementation
uses
{ Cipher }
flcCipherUtils;
{ }
{ RC2 }
{ See RFC 2268 for details on the algorithm. }
{ }
// RC2 PITABLE - "Random" values based on value of Pi
const
RC2Table: array[Byte] of Byte = (
$D9, $78, $F9, $C4, $19, $DD, $B5, $ED, $28, $E9, $FD, $79, $4A, $A0, $D8, $9D,
$C6, $7E, $37, $83, $2B, $76, $53, $8E, $62, $4C, $64, $88, $44, $8B, $FB, $A2,
$17, $9A, $59, $F5, $87, $B3, $4F, $13, $61, $45, $6D, $8D, $09, $81, $7D, $32,
$BD, $8F, $40, $EB, $86, $B7, $7B, $0B, $F0, $95, $21, $22, $5C, $6B, $4E, $82,
$54, $D6, $65, $93, $CE, $60, $B2, $1C, $73, $56, $C0, $14, $A7, $8C, $F1, $DC,
$12, $75, $CA, $1F, $3B, $BE, $E4, $D1, $42, $3D, $D4, $30, $A3, $3C, $B6, $26,
$6F, $BF, $0E, $DA, $46, $69, $07, $57, $27, $F2, $1D, $9B, $BC, $94, $43, $03,
$F8, $11, $C7, $F6, $90, $EF, $3E, $E7, $06, $C3, $D5, $2F, $C8, $66, $1E, $D7,
$08, $E8, $EA, $DE, $80, $52, $EE, $F7, $84, $AA, $72, $AC, $35, $4D, $6A, $2A,
$96, $1A, $D2, $71, $5A, $15, $49, $74, $4B, $9F, $D0, $5E, $04, $18, $A4, $EC,
$C2, $E0, $41, $6E, $0F, $51, $CB, $CC, $24, $91, $AF, $50, $A1, $F4, $70, $39,
$99, $7C, $3A, $85, $23, $B8, $B4, $7A, $FC, $02, $36, $5B, $25, $55, $97, $31,
$2D, $5D, $FA, $98, $E3, $8A, $92, $AE, $05, $DF, $29, $10, $67, $6C, $BA, $C9,
$D3, $00, $E6, $CF, $E1, $9E, $A8, $2C, $63, $16, $01, $3F, $58, $E2, $89, $A9,
$0D, $38, $34, $1B, $AB, $33, $FF, $B0, $BB, $48, $0C, $5F, $B9, $B1, $CD, $2E,
$C5, $F3, $DB, $47, $E5, $A5, $9C, $77, $0A, $A6, $20, $68, $FE, $7F, $C1, $AD);
// RC2 ROR helper function (16 bit rotate right)
{$IFDEF ASM386_DELPHI}
function RC2ROR(const Value: Word; const Bits: Byte): Word;
asm
MOV CL, DL
ROR AX, CL
end;
{$ELSE}
function RC2ROR(const Value: Word; const Bits: Byte): Word;
var I : Integer;
begin
Result := Value;
for I := 1 to Bits do
if Result and 1 = 0 then
Result := Result shr 1
else
Result := (Result shr 1) or $8000;
end;
{$ENDIF}
// RC2 ROL helper function (16 bit rotate left)
{$IFDEF ASM386_DELPHI}
function RC2ROL(const Value: Word; const Bits: Byte): Word;
asm
MOV CL, DL
ROL AX, CL
end;
{$ELSE}
function RC2ROL(const Value: Word; const Bits: Byte): Word;
var I : Integer;
begin
Result := Value;
for I := 1 to Bits do
if Result and $8000 = 0 then
Result := Result shl 1
else
Result := Word(Result shl 1) or 1;
end;
{$ENDIF}
// RC2 initialization
procedure RC2Init(
const Key; const KeySize: Integer;
const KeyBits: Integer;
var CipherKey: TRC2CipherKey);
var I : Integer;
T8 : Byte;
TM : Byte;
begin
// Validate parameters
if KeySize > 128 then
raise ECipher.Create(CipherError_InvalidKeySize, 'Maximum RC2 key length is 128');
if KeySize < 1 then
raise ECipher.Create(CipherError_InvalidKeySize, 'Minimum RC2 key length is 1');
if (KeyBits <= 0) or (KeyBits > 1024) then
raise ECipher.Create(CipherError_InvalidKeyBits, 'Invalid number of key bits');
// RC2 key expansion
Move(Key, CipherKey, KeySize);
with CipherKey do
begin
for I := KeySize to 127 do
Bytes[I] := RC2Table[Byte(Bytes[I - 1] + Bytes[I - KeySize])];
T8 := (KeyBits + 7) div 8;
TM := 255 mod (1 shl (8 + KeyBits - 8 * T8));
Bytes[128 - T8] := RC2Table[Bytes[128 - T8] and TM];
for I := 127 - T8 downto 0 do
Bytes[I] := RC2Table[Bytes[I + 1] xor Bytes[I + T8]];
end;
end;
// RC2 Encrypt
// RC2 Mix: R[i] = R[i] + K[j] + (R[i-1] & R[i-2]) + ((~R[i-1]) & R[i-3])
// R[i] = R[i] rol s[i]
// RC2 Mash: R[i] = R[i] + K[R[i-1] & 63]
procedure RC2EncryptBlock(const CipherKey: TRC2CipherKey; var Block: TRC2Block);
var J : PWord;
I : Integer;
begin
J := @CipherKey.Words[0];
with Block do
begin
for I := 1 to 5 do
begin
A := RC2ROL(Word(A + J^ + (D and C) + (not D and B)), 1); Inc(J);
B := RC2ROL(Word(B + J^ + (A and D) + (not A and C)), 2); Inc(J);
C := RC2ROL(Word(C + J^ + (B and A) + (not B and D)), 3); Inc(J);
D := RC2ROL(Word(D + J^ + (C and B) + (not C and A)), 5); Inc(J);
end;
A := Word(A + CipherKey.Words[D and $3F]);
B := Word(B + CipherKey.Words[A and $3F]);
C := Word(C + CipherKey.Words[B and $3F]);
D := Word(D + CipherKey.Words[C and $3F]);
for I := 1 to 6 do
begin
A := RC2ROL(Word(A + J^ + (D and C) + (not D and B)), 1); Inc(J);
B := RC2ROL(Word(B + J^ + (A and D) + (not A and C)), 2); Inc(J);
C := RC2ROL(Word(C + J^ + (B and A) + (not B and D)), 3); Inc(J);
D := RC2ROL(Word(D + J^ + (C and B) + (not C and A)), 5); Inc(J);
end;
A := Word(A + CipherKey.Words[D and $3F]);
B := Word(B + CipherKey.Words[A and $3F]);
C := Word(C + CipherKey.Words[B and $3F]);
D := Word(D + CipherKey.Words[C and $3F]);
for I := 1 to 5 do
begin
A := RC2ROL(Word(A + J^ + (D and C) + (not D and B)), 1); Inc(J);
B := RC2ROL(Word(B + J^ + (A and D) + (not A and C)), 2); Inc(J);
C := RC2ROL(Word(C + J^ + (B and A) + (not B and D)), 3); Inc(J);
D := RC2ROL(Word(D + J^ + (C and B) + (not C and A)), 5); Inc(J);
end;
end;
end;
// RC2 Decrypt
// RC2 r-Mix: R[i] = R[i] ror s[i]
// R[i] = R[i] - K[j] - (R[i-1] & R[i-2]) - ((~R[i-1]) & R[i-3])
// RC2 r-Mash: R[i] = R[i] - K[R[i-1] & 63]
procedure RC2DecryptBlock(const CipherKey: TRC2CipherKey; var Block: TRC2Block);
var J : PWord;
I : Integer;
begin
J := @CipherKey.Words[63];
with Block do
begin
for I := 1 to 5 do
begin
D := Word(RC2ROR(D, 5) - J^ - (C and B) - (not C and A)); Dec(J);
C := Word(RC2ROR(C, 3) - J^ - (B and A) - (not B and D)); Dec(J);
B := Word(RC2ROR(B, 2) - J^ - (A and D) - (not A and C)); Dec(J);
A := Word(RC2ROR(A, 1) - J^ - (D and C) - (not D and B)); Dec(J);
end;
D := Word(D - CipherKey.Words[C and $3F]);
C := Word(C - CipherKey.Words[B and $3F]);
B := Word(B - CipherKey.Words[A and $3F]);
A := Word(A - CipherKey.Words[D and $3F]);
for I := 1 to 6 do
begin
D := Word(RC2ROR(D, 5) - J^ - (C and B) - (not C and A)); Dec(J);
C := Word(RC2ROR(C, 3) - J^ - (B and A) - (not B and D)); Dec(J);
B := Word(RC2ROR(B, 2) - J^ - (A and D) - (not A and C)); Dec(J);
A := Word(RC2ROR(A, 1) - J^ - (D and C) - (not D and B)); Dec(J);
end;
D := Word(D - CipherKey.Words[C and $3F]);
C := Word(C - CipherKey.Words[B and $3F]);
B := Word(B - CipherKey.Words[A and $3F]);
A := Word(A - CipherKey.Words[D and $3F]);
for I := 1 to 5 do
begin
D := Word(RC2ROR(D, 5) - J^ - (C and B) - (not C and A)); Dec(J);
C := Word(RC2ROR(C, 3) - J^ - (B and A) - (not B and D)); Dec(J);
B := Word(RC2ROR(B, 2) - J^ - (A and D) - (not A and C)); Dec(J);
A := Word(RC2ROR(A, 1) - J^ - (D and C) - (not D and B)); Dec(J);
end;
end;
end;
end.