You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
RnQ/for.RnQ/AES/AES_HMAC.pas

525 lines
18 KiB
Plaintext

unit AES_HMAC;
// This is an implementation of HMAC, the FIPS standard keyed hash function
interface
uses
Windows,
AES_Type,
OverbyteIcsSha1,
RDGlobal
// sha1;
;
const
HASH_INPUT_SIZE = 64;
// HASH_OUTPUT_SIZE = SHA1HashSize;
HASH_OUTPUT_SIZE = 20;
HMAC_OK = 0;
HMAC_BAD_MODE = -1;
HMAC_IN_DATA = Integer($ffffffff);
{ ShaBlockSize = 64; // see sha1.h
ShaDigestSize = 20;
ShaLength = 23;
HMacContextSize = ShaBlockSize+4*ShaLength+sizeof(integer);
}
//type
// THMacContext = packed array[0..HMacContextSize-1] of byte;
type
hmac_ctx = record
key : array[0..HASH_INPUT_SIZE-1] of AnsiChar;
ctx : SHA1Context;
// ctx : T_Sha1_Ctx;
klen : Integer;
end;
// TData_Type = array of byte;
procedure hmac_sha1_begin(var cx : hmac_ctx);
function hmac_sha1_key(const key : RawByteString; key_len : Integer; var cx : hmac_ctx) : Integer;
procedure hmac_sha1_data(const data : Pointer; data_len : Integer; var cx : hmac_ctx);
procedure hmac_sha1_end(mac : PByte; mac_len : Integer; cx : hmac_ctx);
procedure hmac_sha(const key : RawByteString; key_len : Integer;
const data : Pointer; data_len : Int64;
var mac : RawByteString; const mac_len : Integer);
function derive_key(const pwd : RawByteString; // the PASSWORD
const salt : RawByteString; //* the SALT and its */
const iter : Integer; //* the number of iterations */
const key_len : Integer)//* and its required length */
: RawByteString;
(*
Field lengths (in bytes) versus File Encryption Mode (0 < mode < 4)
Mode Key Salt MAC Overhead
1 16 8 10 18
2 24 12 10 22
3 32 16 10 26
The following macros assume that the mode value is correct.
*)
const
KEY_LENGTH : array[1..3] of byte = (16, 24, 32);
SALT_LENGTH : array[1..3] of byte = (8, 12, 16);
MAC_LENGTH = 10;
EXPKEY_LENGTH : array[1..3] of byte = (44, 54, 64);
PWD_VER_LENGTH = 2;
MAX_KEY_LENGTH = 32;
MAX_PWD_LENGTH = 128;
MAX_SALT_LENGTH = 16;
KEYING_ITERATIONS = 1000;
AES_BLOCK_SIZE = 16; //* the AES block size in bytes */
BLOCK_SIZE = AES_BLOCK_SIZE;
//* a maximum of 60 32-bit words are needed for the key schedule */
KS_LENGTH = 64;
GOOD_RETURN = 0;
PASSWORD_TOO_LONG = -100;
BAD_MODE = -101;
type
Pint = ^Cardinal;
type
// aes_encrypt_ctx = record
// ks : array[0..KS_LENGTH-1] of cardinal;
// end;
fcrypt_ctx = record
nonce : array[0..BLOCK_SIZE-1] of Byte; //* the CTR nonce */
encr_bfr : array[0..BLOCK_SIZE-1] of Byte; //* encrypt buffer */
// nonce : TAESBuffer; //* the CTR nonce */
// encr_bfr : TAESBuffer; //* encrypt buffer */
// nonce : TAESBlock; //* the CTR nonce */
// encr_bfr : TAESBlock; //* encrypt buffer */
// encr_ctx : T_AES_Ctx; //* encryption context */
aes_ctx : TAESContext;
auth_ctx : hmac_ctx; //* authentication context */
// auth_ctx : THMacContext;
encr_pos : cardinal; //* block position (enc) */
pwd_len : cardinal; //* password length */
mode : cardinal; //* File encryption mode */
end;
//* initialise file encryption or decryption */
function fcrypt_init(
mode : byte; //* the mode to be used (input) */
const pwd : RawByteString; //* the user specified password (input) */
// unsigned int pwd_len, //* the length of the password (input) */
const salt : RawByteString; //* the salt (input) */
var pwd_ver : RawByteString; //* 2 byte password verifier (output) */
var cx : fcrypt_ctx) : Integer; //* the file encryption context (output) */
//* perform 'in place' encryption or decryption and authentication */
procedure fcrypt_encrypt(data : Pointer; data_len : Integer; var cx : fcrypt_ctx);
procedure fcrypt_decrypt(data : Pointer; data_len : Integer; var cx : fcrypt_ctx);
//* close encryption/decryption and return the MAC value */
//* the return value is the length of the MAC */
function fcrypt_end(var cx : fcrypt_ctx) //* the context (input) */
: RawByteString; //* the MAC value (output) */
implementation
uses
SysUtils,
// DIFileEncrypt,
AES_Encr
;
(*
{$L sha1.obj}
{$L hmac.obj}
{ ---------------------------------------------------------------------------- }
// replacement for C library functions
procedure _memset (var Dest; Value,Count : integer); cdecl;
begin
FillChar (Dest,Count,chr(Value));
end;
procedure _memcpy (var Dest; const Source; Count : integer); cdecl;
begin
Move (Source,Dest,Count);
end;
{ ---------------------------------------------------------------------------- }
procedure hmac_sha1_begin (var HMacContext : THMacContext); external;
procedure hmac_sha1_key (const Key : PAnsiChar; KeyLen : cardinal; var HMacContext : THMacContext); external;
procedure hmac_sha1_data (const Data : PAnsiChar; DataLen : cardinal; var HMacContext : THMacContext); external;
procedure hmac_sha1_end (const Mac : PAnsiChar; MacLen : cardinal; var HMacContext : THMacContext); external;
*)
function derive_key(const pwd : RawByteString; // the PASSWORD
const salt : RawByteString; //* the SALT and its */
const iter : Integer; //* the number of iterations */
const key_len : Integer)//* and its required length */
: RawByteString;
var
i, j, k, n_blk : Integer;
// k_ipad, k_opad: array[0..64] of Byte;
// uu, ux : array[0..SHA1HashSize-1] of char;
// uu, ux : array[0..HASH_OUTPUT_SIZE-1] of Byte;
uu, ux : array[1..HASH_OUTPUT_SIZE] of Byte;
// uu, ux : String[HASH_OUTPUT_SIZE];
// uu, ux : RawByteString;
// s : AnsiString;
// hmac_ctx c1[1], c2[1], c3[1];
c1, c2, c3: hmac_ctx;
// c1, c2, c3: THMacContext;
begin
// SetLength(uu, HASH_OUTPUT_SIZE);
// SetLength(ux, HASH_OUTPUT_SIZE);
SetLength(Result, key_len);
//* set HMAC context (c1) for password */
hmac_sha1_begin(c1);
hmac_sha1_key(PAnsiChar(pwd), Length(pwd), c1);
//* set HMAC context (c2) for password and salt */
// memcpy(c2, c1, sizeof(hmac_ctx));
CopyMemory(@c2, @c1, sizeof(c1));
hmac_sha1_data(Pointer(salt), Length(salt), c2);
//* find the number of SHA blocks in the key */
n_blk := 1 + (key_len - 1) div HASH_OUTPUT_SIZE;
// for(i = 0; i < n_blk; ++i) /* for each block in key */
for i := 0 to n_blk-1 do //* for each block in key */
begin
//* ux[] holds the running xor value */
// memset(ux, 0, HASH_OUTPUT_SIZE);
FillMemory(@ux[1], HASH_OUTPUT_SIZE, 00);
//* set HMAC context (c3) for password and salt */
// memcpy(c3, c2, sizeof(hmac_ctx));
CopyMemory(@c3, @c2, sizeof(c2));
//* enter additional data for 1st block into uu */
uu[1] := Byte((i + 1) shr 24);
uu[2] := Byte((i + 1) shr 16);
uu[3] := Byte((i + 1) shr 8);
uu[4] := Byte(i + 1);
//* this is the key mixing iteration */
k := 4;
for j := 0 to iter-1 do
begin
//* add previous round data to HMAC */
hmac_sha1_data(@uu[1], k, c3);
//* obtain HMAC for uu[] */
// hmac_sha1_end(PAnsiChar(uu), HASH_OUTPUT_SIZE, c3);
hmac_sha1_end(@uu[1], HASH_OUTPUT_SIZE, c3);
//* xor into the running xor block */
for k := 1 to HASH_OUTPUT_SIZE do
ux[k] := Byte(byte(ux[k]) xor byte(uu[k]));
//* set HMAC context (c3) for password */
// memcpy(c3, c1, sizeof(hmac_ctx));
CopyMemory(@c3, @c1, sizeof(c1));
k := HASH_OUTPUT_SIZE;
end;
//* compile key blocks into the key output */
j := 0; k := i * HASH_OUTPUT_SIZE;
while(j < HASH_OUTPUT_SIZE) and (k < key_len) do
begin
Result[k+1] := AnsiChar(ux[j+1]);
inc(k);
inc(j);
end;
end;
end;
//* initialise the HMAC context to zero */
procedure hmac_sha1_begin(var cx : hmac_ctx);
begin
FillMemory(@cx, SizeOf(hmac_ctx), 00);
end;
//* input the HMAC key (can be called multiple times) */
function hmac_sha1_key(const key : RawByteString; key_len : Integer; var cx : hmac_ctx) : Integer;
begin
if (cx.klen = HMAC_IN_DATA) then //* error if further key input */
begin
result := HMAC_BAD_MODE; //* is attempted in data mode */
Exit;
end;
if(cx.klen + key_len > HASH_INPUT_SIZE) then //* if the key has to be hashed */
begin
if(cx.klen <= HASH_INPUT_SIZE) then //* if the hash has not yet been */
begin //* started, initialise it and */
// sha1_begin(cx.ctx); //* hash stored key characters */
// sha1_hash(@cx.key[0], cx.klen, cx.ctx);
SHA1Reset (cx.ctx);
SHA1Input(cx.ctx, cx.key, cx.klen);
end;
// sha1_hash(@key[1], key_len, cx.ctx); //* hash long key data into hash */
SHA1Input(cx.ctx, PAnsiChar(key), key_len);
end
else //* otherwise store key data */
// memcpy(cx->key + cx->klen, key, key_len);
CopyMemory(@cx.key[cx.klen], Pointer(key), key_len);
// cx.klen += key_len; //* update the key length count */
inc(cx.klen, key_len);
Result := HMAC_OK;
end;
//* input the HMAC data (can be called multiple times) - */
//* note that this call terminates the key input phase */
procedure hmac_sha1_data(const data : Pointer; data_len : Integer; var cx : hmac_ctx);
var
k : Integer;
res : SHA1Digest;
// res : T_Sha1_Digest;
begin
if(cx.klen <> HMAC_IN_DATA) then //* if not yet in data phase */
begin
if(cx.klen > HASH_INPUT_SIZE) then //* if key is being hashed */
begin //* complete the hash and */
// sha1_end(res, cx.ctx); //* store the result as the */
SHA1Result(cx.ctx, res);
CopyMemory(@cx.key[0], @res[0], HASH_OUTPUT_SIZE);
cx.klen := HASH_OUTPUT_SIZE; //* key and set new length */
end;
//* pad the key if necessary */
// memset(cx->key + cx->klen, 0, HASH_INPUT_SIZE - cx->klen);
FillMemory(@cx.key[cx.klen], HASH_INPUT_SIZE - cx.klen, 00);
//* xor ipad into key value */
// for i := 0 to HASH_INPUT_SIZE do
// byte(cx.key[i]) := byte(cx.key[i]) xor $36;
k := 0;
while k < HASH_INPUT_SIZE do
begin
Pint(@cx.key[k])^ := Pint(@cx.key[k])^ xor $36363636;
Inc(k, 4);
end;
//* and start hash operation */
// sha1_begin(cx.ctx);
// sha1_hash(@cx.key[0], HASH_INPUT_SIZE, cx.ctx);
SHA1Reset(cx.ctx);
SHA1Input(cx.ctx, cx.key, HASH_INPUT_SIZE);
//* mark as now in data mode */
cx.klen := HMAC_IN_DATA;
end;
//* hash the data (if any) */
if (data_len > 0) then
// sha1_hash(data, data_len, cx.ctx);
SHA1Input(cx.ctx, data, data_len);
// SHA1Input(cx.ctx, PAnsiChar(data), data_len);
end;
//* input the HMAC data (can be called multiple times) - */
//* note that this call terminates the key input phase */
procedure hmac_sha1_end(mac : PByte; mac_len : Integer; cx : hmac_ctx);
var
dig : SHA1Digest;
// dig : T_Sha1_Digest;
i, k : Integer;
begin
//* if no data has been entered perform a null data phase */
if (cx.klen <> HMAC_IN_DATA) then
hmac_sha1_data(nil, 0, cx);
// hmac_sha_data(i, 0, cx);
// sha1_end(dig, cx.ctx); //* complete the inner hash */
SHA1Result(cx.ctx, dig);
//* set outer key value using opad and removing ipad */
{
for i := 0 to (HASH_INPUT_SIZE) do
// ((unsigned long*)cx->key)[i] ^= 0x36363636 ^ 0x5c5c5c5c;
byte(cx.key[i]) := byte(cx.key[i]) xor $36 xor $5c;
}
k := 0;
while k < HASH_INPUT_SIZE do
begin
Pint(@cx.key[k])^ := Pint(@cx.key[k])^ xor $36363636 xor $5c5c5c5c;
Inc(k, 4);
end;
//* perform the outer hash operation */
// sha1_begin(cx.ctx);
// sha1_hash(@cx.key[0], HASH_INPUT_SIZE, cx.ctx);
// sha1_hash(@dig[0], HASH_OUTPUT_SIZE, cx.ctx);
// sha1_end(dig, cx.ctx);
SHA1Reset(cx.ctx);
SHA1Input(cx.ctx, cx.key, HASH_INPUT_SIZE);
SHA1Input(cx.ctx, dig, HASH_OUTPUT_SIZE);
SHA1Result(cx.ctx, dig);
// SetLength(mac, HASH_OUTPUT_SIZE);
//* output the hash value */
// SetLength(mac, mac_len);
for i := 0 to mac_len-1 do
// mac[i+1] := AnsiChar(dig[i]);
// mac[i] := Byte(dig[i]);
Byte(PByte(Cardinal(mac)+i)^) := Byte(dig[i]);
end;
//* 'do it all in one go' subroutine */
procedure hmac_sha(const key : RawByteString; key_len : Integer;
const data : Pointer; data_len : Int64;
var mac : RawByteString; const mac_len : Integer);
var
cx : hmac_ctx;
begin
hmac_sha1_begin(cx);
hmac_sha1_key(key, key_len, cx);
hmac_sha1_data(data, data_len, cx);
SetLength(mac, mac_len);
hmac_sha1_end(@mac[1], mac_len, cx);
end;
//* initialise file encryption or decryption */
function fcrypt_init(
mode : byte; //* the mode to be used (input) */
const pwd : RawByteString; //* the user specified password (input) */
// unsigned int pwd_len, //* the length of the password (input) */
const salt : RawByteString; //* the salt (input) */
var pwd_ver : RawByteString; //* 2 byte password verifier (output) */
var cx : fcrypt_ctx) : Integer; //* the file encryption context (output) */
var
// kbuf : array[0..2 * MAX_KEY_LENGTH + PWD_VER_LENGTH-1] of byte;
buf, buf2 : RawByteString;
// ab: TAESBlock;
// act : TAESContext;
begin
if(Length(pwd) > MAX_PWD_LENGTH) then
begin
Result := PASSWORD_TOO_LONG;
exit;
end;
if(mode < 1) or (mode > 3) then
begin
Result := BAD_MODE;
Exit;
end;
cx.mode := mode;
cx.pwd_len := Length(pwd);
//* initialise the encryption nonce and buffer pos */
cx.encr_pos := BLOCK_SIZE;
//* if we need a random component in the encryption */
//* nonce, this is where it would have to be set */
// memset(cx.nonce, 0, BLOCK_SIZE * sizeof(unsigned char));
FillMemory(@cx.nonce[0], BLOCK_SIZE, 00);
//* derive the encryption and authetication keys and the password verifier */
buf := derive_key(pwd, salt, KEYING_ITERATIONS, 2 * KEY_LENGTH[mode] + PWD_VER_LENGTH);
//* set the encryption key */
{ CopyMemory(@cx.encr_ctx.ks[0], @cx.aes_ctx.RK[0][0], EXPKEY_LENGTH[mode]);
// aes_encrypt_key(kbuf, KEY_LENGTH(mode), cx->encr_ctx);
}
// aes_encrypt_key(@buf[1], KEY_LENGTH[mode], cx.encr_ctx);
AES_Init_Encr(Pointer(buf)^, KEY_LENGTH[mode] * 8, cx.aes_ctx);
//* initialise for authentication */
hmac_sha1_begin(cx.auth_ctx);
//* set the authentication key */
buf2 := Copy(buf, KEY_LENGTH[mode]+1, KEY_LENGTH[mode]);
// hmac_sha1_key(PAnsichar(buf) + KEY_LENGTH[mode], KEY_LENGTH[mode], cx.auth_ctx);
hmac_sha1_key(buf2, KEY_LENGTH[mode], cx.auth_ctx);
// memcpy(pwd_ver, kbuf + 2 * KEY_LENGTH(mode), PWD_VER_LENGTH);
pwd_ver := copy(buf, 2 * KEY_LENGTH[mode] + 1, PWD_VER_LENGTH);
//* clear the buffer holding the derived key values */
// memset(kbuf, 0, 2 * KEY_LENGTH(mode) + PWD_VER_LENGTH);
buf := '';
Result := GOOD_RETURN;
end;
procedure encr_data(var data : Pointer; d_len : Integer; var cx : fcrypt_ctx);
var
i, j : Integer;
pos : Cardinal;
begin
i := 0;
pos := cx.encr_pos;
while(i < d_len) do
begin
if(pos = BLOCK_SIZE) then
begin
j := 0;
//* increment encryption nonce */
// while(j < 8 && !++cx->nonce[j])
// ++j;
while j < 8 do
begin
inc(cx.nonce[j]);
// if cx.nonce[j] = 0 then
if cx.nonce[j] <> 0 then
Break;
inc(j);
end;
//* encrypt the nonce to form next xor buffer */
// aes_encrypt(cx->nonce, cx->encr_bfr, cx->encr_ctx);
{ EncryptAES(cx.nonce, ExpKey2^, cx.encr_bfr);
}
// AES_CTR_Encrypt(@cx.nonce[0], @cx.encr_bfr[0], BLOCK_SIZE, cx.aes_ctx);
AES_Encr.AES_Encrypt(cx.aes_ctx, TAESBlock(cx.nonce), TAESBlock(cx.encr_bfr));
// diaes.aes_encrypt(@cx.nonce[0], @cx.encr_bfr[0], cx.encr_ctx);
pos := 0;
end;
Byte(Pointer(Cardinal(data)+i)^) := Byte(Pointer(Cardinal(data)+i)^) xor cx.encr_bfr[pos];
inc(i);
inc(pos);
end;
cx.encr_pos := pos;
end;
//* perform 'in place' encryption or decryption and authentication */
procedure fcrypt_encrypt(data : Pointer; data_len : Integer; var cx : fcrypt_ctx);
begin
encr_data(data, data_len, cx);
hmac_sha1_data(data, data_len, cx.auth_ctx);
end;
procedure fcrypt_decrypt(data : Pointer; data_len : Integer; var cx : fcrypt_ctx);
begin
hmac_sha1_data(data, data_len, cx.auth_ctx);
encr_data(data, data_len, cx);
end;
//* close encryption/decryption and return the MAC value */
//* the return value is the length of the MAC */
function fcrypt_end(var cx : fcrypt_ctx) //* the context (input) */
: RawByteString; //* the MAC value (output) */
begin
SetLength(Result, MAC_LENGTH);
hmac_sha1_end(@Result[1], MAC_LENGTH, cx.auth_ctx);
// hmac_sha1_end(Result, MAC_LENGTH, cx.auth_ctx);
// memset(cx, 0, sizeof(fcrypt_ctx)); //* clear the encryption context */
FillMemory(@cx, sizeof(fcrypt_ctx), 0);
// Result MAC_LENGTH(res); //* return MAC length in bytes */
end;
end.