464 lines
15 KiB
ObjectPascal
464 lines
15 KiB
ObjectPascal
{* rijndael-api-ref.c v2.0 August '99 *}
|
|
(* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* --------------------------------- *
|
|
* DELPHI *
|
|
* Rijndael API *
|
|
* --------------------------------- *
|
|
* December 2000 *
|
|
* *
|
|
* Authors: Paulo Barreto *
|
|
* Vincent Rijmen *
|
|
* *
|
|
* Delphi translation by Sergey Kirichenko (ksv@cheerful.com) *
|
|
* Home Page: http://rcolonel.tripod.com *
|
|
* Adapted to FastReport: Alexander Tzyganenko *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *)
|
|
|
|
unit rc_ApiRef;
|
|
|
|
{$I frx.inc}
|
|
|
|
interface
|
|
|
|
uses rc_AlgRef;
|
|
|
|
const
|
|
MAXBC = (256 div 32);
|
|
MAXKC = (256 div 32);
|
|
MAXROUNDS = 14;
|
|
|
|
DIR_ENCRYPT = 0; { Are we encrpyting? }
|
|
DIR_DECRYPT = 1; { Are we decrpyting? }
|
|
MODE_ECB = 1; { Are we ciphering in ECB mode? }
|
|
MODE_CBC = 2; { Are we ciphering in CBC mode? }
|
|
MODE_CFB1 = 3; { Are we ciphering in 1-bit CFB mode? }
|
|
rTRUE = 1; { integer(true) }
|
|
rFALSE = 0; { integer(false) }
|
|
BITSPERBLOCK = 128; { Default number of bits in a cipher block }
|
|
|
|
{ Error Codes - CHANGE POSSIBLE: inclusion of additional error codes }
|
|
BAD_KEY_DIR = -1; { Key direction is invalid, e.g., unknown value }
|
|
BAD_KEY_MAT = -2; { Key material not of correct length }
|
|
BAD_KEY_INSTANCE = -3; { Key passed is not valid }
|
|
BAD_CIPHER_MODE = -4; { Params struct passed to cipherInit invalid }
|
|
BAD_CIPHER_STATE = -5; { Cipher in wrong state (e.g., not initialized) }
|
|
BAD_CIPHER_INSTANCE = -7;
|
|
|
|
{ CHANGE POSSIBLE: inclusion of algorithm specific defines }
|
|
MAX_KEY_SIZE = 64; { # of ASCII char's needed to represent a key }
|
|
MAX_IV_SIZE = (BITSPERBLOCK div 8); { # bytes needed to represent an IV }
|
|
|
|
type
|
|
{ Typedef'ed data storage elements. Add any algorithm specific
|
|
parameters at the bottom of the structs as appropriate. }
|
|
|
|
word8 = byte; // unsigned 8-bit
|
|
word16 = word; // unsigned 16-bit
|
|
word32 = longword; // unsigned 32-bit
|
|
TByteArray = array [0..MaxInt div sizeof(Byte)-1] of Byte;
|
|
PByte = ^TByteArray;
|
|
|
|
{ The structure for key information }
|
|
PkeyInstance = ^keyInstance;
|
|
keyInstance = packed record
|
|
direction: Byte; { Key used for encrypting or decrypting? }
|
|
keyLen: integer; { Length of the key }
|
|
keyMaterial: array [0..MAX_KEY_SIZE+1-1] of Ansichar; { Raw key data in ASCII, e.g., user input or KAT values }
|
|
{ The following parameters are algorithm dependent, replace or add as necessary }
|
|
blockLen: integer; { block length }
|
|
keySched: TArrayRK; { key schedule }
|
|
end; {* keyInstance *}
|
|
TkeyInstance = keyInstance;
|
|
|
|
{ The structure for cipher information }
|
|
PcipherInstance = ^cipherInstance;
|
|
cipherInstance = packed record
|
|
mode: Byte; // MODE_ECB, MODE_CBC, or MODE_CFB1
|
|
IV: array [0..MAX_IV_SIZE-1] of Byte; // A possible Initialization Vector for ciphering
|
|
{ Add any algorithm specific parameters needed here }
|
|
blockLen: integer; // Sample: Handles non-128 bit block sizes (if available)
|
|
end; {* cipherInstance *}
|
|
TcipherInstance = cipherInstance;
|
|
|
|
{ Function prototypes }
|
|
function makeKey(key: PkeyInstance; direction: Byte; keyLen: integer; keyMaterial: pAnsichar): integer;
|
|
function cipherInit(cipher: PcipherInstance; mode: Byte; IV: pchar): integer;
|
|
{sergey has corrected it}
|
|
function blocksEnCrypt(cipher: PcipherInstance; key: PkeyInstance; input: PByte;
|
|
inputLen: integer; outBuffer: PByte): integer;
|
|
{sergey has corrected it}
|
|
function blocksDeCrypt(cipher: PcipherInstance; key: PkeyInstance; input: PByte;
|
|
inputLen: integer; outBuffer: PByte): integer;
|
|
{ cipherUpdateRounds:
|
|
|
|
Encrypts/Decrypts exactly one full block a specified number of rounds.
|
|
Only used in the Intermediate Value Known Answer Test.
|
|
|
|
Returns:
|
|
TRUE - on success
|
|
BAD_CIPHER_STATE - cipher in bad state (e.g., not initialized) }
|
|
function cipherUpdateRounds(cipher: PcipherInstance; key: PkeyInstance; input: PByte;
|
|
inputLen: integer; outBuffer: PByte; iRounds: integer): integer;
|
|
|
|
implementation
|
|
|
|
uses
|
|
SysUtils;
|
|
|
|
{ StrLCopy copies at most MaxLen characters from Source to Dest and returns Dest. }
|
|
{
|
|
function StrLCopy(Dest: PAnsiChar; const Source: PAnsiChar; MaxLen: Cardinal): PAnsiChar; assembler;
|
|
asm
|
|
PUSH EDI
|
|
PUSH ESI
|
|
PUSH EBX
|
|
MOV ESI,EAX
|
|
MOV EDI,EDX
|
|
MOV EBX,ECX
|
|
XOR AL,AL
|
|
TEST ECX,ECX
|
|
JZ @@1
|
|
REPNE SCASB
|
|
JNE @@1
|
|
INC ECX
|
|
@@1: SUB EBX,ECX
|
|
MOV EDI,ESI
|
|
MOV ESI,EDX
|
|
MOV EDX,EDI
|
|
MOV ECX,EBX
|
|
SHR ECX,2
|
|
REP MOVSD
|
|
MOV ECX,EBX
|
|
AND ECX,3
|
|
REP MOVSB
|
|
STOSB
|
|
MOV EAX,EDX
|
|
POP EBX
|
|
POP ESI
|
|
POP EDI
|
|
end;
|
|
}
|
|
|
|
{$WARN USE_BEFORE_DEF OFF}
|
|
function makeKey(key: PkeyInstance; direction: Byte; keyLen: integer; keyMaterial: pAnsichar): integer;
|
|
var
|
|
k: TArrayK;
|
|
i, j, t: integer;
|
|
begin
|
|
if not assigned(key) then
|
|
begin
|
|
result:= BAD_KEY_INSTANCE;
|
|
exit;
|
|
end;
|
|
|
|
if ((direction = DIR_ENCRYPT) or (direction = DIR_DECRYPT)) then
|
|
key.direction:= direction
|
|
else
|
|
begin
|
|
result:= BAD_KEY_DIR;
|
|
exit;
|
|
end;
|
|
|
|
if ((keyLen = 128) or (keyLen = 192) or (keyLen = 256)) then
|
|
key.keyLen:= keyLen
|
|
else
|
|
begin
|
|
result:= BAD_KEY_MAT;
|
|
exit;
|
|
end;
|
|
|
|
if (keyMaterial^ <> #0) then
|
|
StrLCopy(PAnsiChar(@key.keyMaterial[0]), keyMaterial, keyLen div 4); // strncpy
|
|
|
|
//j := 0;
|
|
{ initialize key schedule: }
|
|
for i:= 0 to (key.keyLen div 8)-1 do
|
|
begin
|
|
t:= integer(key.keyMaterial[2*i]);
|
|
if ((t >= ord('0')) and (t <= ord('9'))) then
|
|
j:= (t - ord('0')) shl 4
|
|
else
|
|
if ((t >= ord('a')) and (t <= ord('f'))) then
|
|
j:= (t - ord('a') + 10) shl 4
|
|
else
|
|
if ((t >= ord('A')) and (t <= ord('F'))) then
|
|
j:= (t - ord('A') + 10) shl 4
|
|
else
|
|
begin
|
|
result:= BAD_KEY_MAT;
|
|
exit;
|
|
end;
|
|
|
|
t:= integer(key.keyMaterial[2*i+1]);
|
|
if ((t >= ord('0')) and (t <= ord('9'))) then
|
|
j:= j xor (t - ord('0'))
|
|
else
|
|
if ((t >= ord('a')) and (t <= ord('f'))) then
|
|
j:= j xor (t - ord('a') + 10)
|
|
else
|
|
if ((t >= ord('A')) and (t <= ord('F'))) then
|
|
j:= j xor (t - ord('A') + 10)
|
|
else
|
|
begin
|
|
result:= BAD_KEY_MAT;
|
|
exit;
|
|
end;
|
|
|
|
k[i mod 4][i div 4]:= word8(j);
|
|
end;
|
|
rijndaelKeySched(k, key.keyLen, key.blockLen, key.keySched);
|
|
result:= rTRUE;
|
|
end;
|
|
|
|
function cipherInit(cipher: PcipherInstance; mode: Byte; IV: pchar): integer;
|
|
var
|
|
i, j, t: integer;
|
|
begin
|
|
if ((mode = MODE_ECB) or (mode = MODE_CBC) or (mode = MODE_CFB1)) then
|
|
cipher.mode:= mode
|
|
else
|
|
begin
|
|
result:= BAD_CIPHER_MODE;
|
|
exit;
|
|
end;
|
|
|
|
//j := 0;
|
|
|
|
if assigned(IV) then
|
|
for i:= 0 to (cipher.blockLen div 8)-1 do
|
|
begin
|
|
t:= integer(IV[2*i]);
|
|
if ((t >= ord('0')) and (t <= ord('9'))) then
|
|
j:= (t - ord('0')) shl 4
|
|
else
|
|
if ((t >= ord('a')) and (t <= ord('f'))) then
|
|
j:= (t - ord('a') + 10) shl 4
|
|
else
|
|
if ((t >= ord('A')) and (t <= ord('F'))) then
|
|
j:= (t - ord('A') + 10) shl 4
|
|
else
|
|
begin
|
|
result:= BAD_CIPHER_INSTANCE;
|
|
exit;
|
|
end;
|
|
|
|
t:= integer(IV[2*i+1]);
|
|
if ((t >= ord('0')) and (t <= ord('9'))) then
|
|
j:= j xor (t - ord('0'))
|
|
else
|
|
if ((t >= ord('a')) and (t <= ord('f'))) then
|
|
j:= j xor (t - ord('a') + 10)
|
|
else
|
|
if ((t >= ord('A')) and (t <= ord('F'))) then
|
|
j:= j xor (t - ord('A') + 10)
|
|
else
|
|
begin
|
|
result:= BAD_CIPHER_INSTANCE;
|
|
exit;
|
|
end;
|
|
cipher.IV[i]:= Byte(j);
|
|
end;
|
|
result:= rTRUE;
|
|
end;
|
|
{$WARN USE_BEFORE_DEF ON}
|
|
|
|
function blocksEnCrypt(cipher: PcipherInstance; key: PkeyInstance;
|
|
input: PByte; inputLen: integer; outBuffer: PByte): integer;
|
|
var
|
|
i, j, t, numBlocks: integer;
|
|
block: TArrayK;
|
|
begin
|
|
{ check parameter consistency: }
|
|
if (not assigned(key)) or
|
|
(key.direction <> DIR_ENCRYPT) or
|
|
((key.keyLen <> 128) and (key.keyLen <> 192) and (key.keyLen <> 256)) then
|
|
begin
|
|
result:= BAD_KEY_MAT;
|
|
exit;
|
|
end;
|
|
|
|
if (not assigned(cipher)) or
|
|
((cipher.mode <> MODE_ECB) and (cipher.mode <> MODE_CBC) and (cipher.mode <> MODE_CFB1)) or
|
|
((cipher.blockLen <> 128) and (cipher.blockLen <> 192) and (cipher.blockLen <> 256)) then
|
|
begin
|
|
result:= BAD_CIPHER_STATE;
|
|
exit;
|
|
end;
|
|
|
|
numBlocks:= inputLen div cipher.blockLen;
|
|
case (cipher.mode) of
|
|
MODE_ECB:
|
|
for i:= 0 to numBlocks-1 do
|
|
begin
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
for t:= 0 to 4-1 do
|
|
{ parse input stream into rectangular array }
|
|
block[t][j]:= input[4*j+t] and $FF;
|
|
rijndaelEncrypt(block, key.keyLen, cipher.blockLen, key.keySched);
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
{ parse rectangular array into output ciphertext bytes }
|
|
for t:= 0 to 4-1 do
|
|
outBuffer[4*j+t]:= Byte(block[t][j]);
|
|
end;
|
|
MODE_CBC:
|
|
begin
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
for t:= 0 to 4-1 do
|
|
{ parse initial value into rectangular array }
|
|
block[t][j]:= cipher.IV[t+4*j] and $FF;
|
|
for i:= 0 to numBlocks-1 do
|
|
begin
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
for t:= 0 to 4-1 do
|
|
{ parse input stream into rectangular array and exor with
|
|
IV or the previous ciphertext }
|
|
// block[t][j]:= block[t][j] xor (input[4*j+t] and $FF); {!original!}
|
|
block[t][j]:= block[t][j] xor (input[(i*(cipher.blockLen div 8))+4*j+t] and $FF); {!sergey made it!}
|
|
rijndaelEncrypt(block, key.keyLen, cipher.blockLen, key.keySched);
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
{ parse rectangular array into output ciphertext bytes }
|
|
for t:= 0 to 4-1 do
|
|
// outBuffer[4*j+t]:= Byte(block[t][j]); {!original!}
|
|
outBuffer[(i*(cipher.blockLen div 8))+4*j+t]:= Byte(block[t][j]); {!sergey made it!}
|
|
end;
|
|
end;
|
|
else
|
|
begin
|
|
result:= BAD_CIPHER_STATE;
|
|
exit
|
|
end;
|
|
end;
|
|
result:= numBlocks*cipher.blockLen;
|
|
end;
|
|
|
|
function blocksDeCrypt(cipher: PcipherInstance; key: PkeyInstance; input: PByte;
|
|
inputLen: integer; outBuffer: PByte): integer;
|
|
var
|
|
i, j, t, numBlocks: integer;
|
|
block: TArrayK;
|
|
begin
|
|
if (not assigned(cipher)) or
|
|
(not assigned(key)) or
|
|
(key.direction = DIR_ENCRYPT) or
|
|
(cipher.blockLen <> key.blockLen) then
|
|
begin
|
|
result:= BAD_CIPHER_STATE;
|
|
exit;
|
|
end;
|
|
|
|
{ check parameter consistency: }
|
|
if (not assigned(key)) or
|
|
(key.direction <> DIR_DECRYPT) or
|
|
((key.keyLen <> 128) and (key.keyLen <> 192) and (key.keyLen <> 256)) then
|
|
begin
|
|
result:= BAD_KEY_MAT;
|
|
exit;
|
|
end;
|
|
|
|
if (not assigned(cipher)) or
|
|
((cipher.mode <> MODE_ECB) and (cipher.mode <> MODE_CBC) and (cipher.mode <> MODE_CFB1)) or
|
|
((cipher.blockLen <> 128) and (cipher.blockLen <> 192) and (cipher.blockLen <> 256)) then
|
|
begin
|
|
result:= BAD_CIPHER_STATE;
|
|
exit;
|
|
end;
|
|
|
|
numBlocks:= inputLen div cipher.blockLen;
|
|
case (cipher.mode) of
|
|
MODE_ECB:
|
|
for i:= 0 to numBlocks-1 do
|
|
begin
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
for t:= 0 to 4-1 do
|
|
{ parse input stream into rectangular array }
|
|
block[t][j]:= input[4*j+t] and $FF;
|
|
rijndaelDecrypt (block, key.keyLen, cipher.blockLen, key.keySched);
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
{ parse rectangular array into output ciphertext bytes }
|
|
for t:= 0 to 4-1 do
|
|
outBuffer[4*j+t]:= Byte(block[t][j]);
|
|
end;
|
|
MODE_CBC:
|
|
{! sergey has rearranged processing blocks and
|
|
corrected exclusive-ORing operation !}
|
|
|
|
begin
|
|
{ blocks after first }
|
|
for i:= numBlocks-1 downto 1 do
|
|
begin
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
for t:= 0 to 4-1 do
|
|
{ parse input stream into rectangular array }
|
|
block[t][j]:= input[(i*(cipher.blockLen div 8))+ 4*j+ t] and $FF;
|
|
rijndaelDecrypt(block, key.keyLen, cipher.blockLen, key.keySched);
|
|
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
{ exor previous ciphertext block and parse rectangular array
|
|
into output ciphertext bytes }
|
|
for t:= 0 to 4-1 do
|
|
outBuffer[(i*(cipher.blockLen div 8))+ 4*j+t]:= Byte(block[t][j] xor
|
|
input[(i-1)*(cipher.blockLen div 8)+ 4*j+ t]);
|
|
end;
|
|
|
|
{ first block }
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
for t:= 0 to 4-1 do
|
|
{ parse input stream into rectangular array }
|
|
block[t][j]:= input[4*j+t] and $FF;
|
|
rijndaelDecrypt(block, key.keyLen, cipher.blockLen, key.keySched);
|
|
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
{ exor the IV and parse rectangular array into output ciphertext bytes }
|
|
for t:= 0 to 4-1 do
|
|
outBuffer[4*j+t]:= Byte(block[t][j] xor cipher.IV[t+4*j]);
|
|
end;
|
|
else
|
|
begin
|
|
result:= BAD_CIPHER_STATE;
|
|
exit;
|
|
end;
|
|
end;
|
|
result:= numBlocks*cipher.blockLen;
|
|
end;
|
|
|
|
function cipherUpdateRounds(cipher: PcipherInstance; key: PkeyInstance; input: PByte;
|
|
inputLen: integer; outBuffer: PByte; iRounds: integer): integer;
|
|
var
|
|
j, t: integer;
|
|
block: TArrayK;
|
|
begin
|
|
if (not assigned(cipher)) or
|
|
(not assigned(key)) or
|
|
(cipher.blockLen <> key.blockLen) then
|
|
begin
|
|
result:= BAD_CIPHER_STATE;
|
|
exit;
|
|
end;
|
|
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
for t:= 0 to 4-1 do
|
|
{ parse input stream into rectangular array }
|
|
block[t][j]:= input[4*j+t] and $FF;
|
|
|
|
case (key.direction) of
|
|
DIR_ENCRYPT:
|
|
rijndaelEncryptRound(block, key.keyLen, cipher.blockLen, key.keySched, irounds);
|
|
DIR_DECRYPT:
|
|
rijndaelDecryptRound(block, key.keyLen, cipher.blockLen, key.keySched, irounds);
|
|
else
|
|
begin
|
|
result:= BAD_KEY_DIR;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
for j:= 0 to (cipher.blockLen div 32)-1 do
|
|
{ parse rectangular array into output ciphertext bytes }
|
|
for t:= 0 to 4-1 do
|
|
outBuffer[4*j+t]:= Byte(block[t][j]);
|
|
result:= rTRUE;
|
|
end;
|
|
|
|
|
|
end.
|