274 lines
12 KiB
ObjectPascal
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.
|
|
|