{* 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.