doublecmd/components/kascrypt/Hashes/Private/kdf.pas
2023-12-04 20:25:15 +03:00

439 lines
16 KiB
ObjectPascal

unit kdf;
{(Password Based) Key Derivation Functions}
interface
(*************************************************************************
DESCRIPTION : (Password Based) Key Derivation Functions
REQUIREMENTS : TP5-7, D1-D7/D9-D10/D12/D17-D18, FPC, VP, WDOSX
EXTERNAL DATA : ---
MEMORY USAGE : ---
DISPLAY MODE : ---
REFERENCES : http://tools.ietf.org/html/rfc2898
http://tools.ietf.org/html/rfc3211 [includes test vectors]
http://tools.ietf.org/html/rfc5869 [includes test vectors]
http://www.di-mgt.com.au/cryptoKDFs.html [includes test vectors]
Version Date Author Modification
------- -------- ------- ------------------------------------------
0.10 17.01.06 W.Ehrhardt Initial version based on keyderiv 1.29
0.11 17.01.06 we error codes
0.12 23.01.06 we new names: unit pb_kdf, functions kdf2/s
0.13 22.06.08 we Make IncMSB work with FPC -dDebug
0.14 12.07.08 we Rename old kdf2 to pbkdf2, unit kdf
0.15 12.07.08 we pbkdf1
0.16 12.07.08 we kdf1, kdf2, kdf3, mgf1
0.17 12.11.08 we uses BTypes, Ptr2Inc and/or Str255
0.18 25.04.09 we updated RFC URL(s)
0.19 19.08.10 we kdf_err_nil_input
0.20 20.08.10 we hkdf/hkdfs
0.21 15.08.14 we pbkdf2 functions with longint sLen, dkLen
0.22 16.08.15 we Removed $ifdef DLL / stdcall
**************************************************************************)
(*-------------------------------------------------------------------------
(C) Copyright 2006-2015 Wolfgang Ehrhardt
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in
a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
----------------------------------------------------------------------------*)
{$i STD.INC}
uses
BTypes,Hash,HMAC;
const
kdf_err_nil_pointer = $0001; {phash descriptor is nil}
kdf_err_digestlen = $0002; {digest length from descriptor is zero}
kdf_err_invalid_dKLen = $0003; {dKLen greater than hash digest length}
kdf_err_nil_input = $0004; {input nil pointer and non-zero length}
function kdf1(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
{-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
function kdf2(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
{-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
function kdf3(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
{-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
function mgf1(phash: PHashDesc; pSeed: pointer; sLen: word; var Mask; mLen: word): integer;
{-Derive Mask from seed, hash function from phash, Mask Generation Function 1 for PKCS #1}
function pbkdf1(phash: PHashDesc; pPW: pointer; pLen: word; salt: pointer; C: longint; var DK; dkLen: word): integer;
{-Derive key DK from password pPW using 8 byte salt and iteration count C, uses hash function from phash}
function pbkdf1s(phash: PHashDesc; sPW: Str255; salt: pointer; C: longint; var DK; dkLen: word): integer;
{-Derive key DK from password string sPW using 8 byte salt and iteration count C, uses hash function from phash}
function pbkdf2(phash: PHashDesc; pPW: pointer; pLen: word; salt: pointer; sLen,C: longint; var DK; dkLen: longint): integer;
{-Derive key DK from password pPW using salt and iteration count C, uses hash function from phash}
function pbkdf2s(phash: PHashDesc; sPW: Str255; salt: pointer; sLen,C: longint; var DK; dkLen: longint): integer;
{-Derive key DK from password string sPW using salt and iteration count C, uses hash function from phash}
function hkdf(phash: PHashDesc; {Descriptor of the Hash to use}
pIKM: pointer; L_IKM: word; {input key material: addr/length}
salt: pointer; L_salt: word; {optional salt; can be nil: see below }
info: pointer; L_info: word; {optional context/application specific information}
var DK; dkLen: word): integer; {output key material: addr/length}
{-Derive key DK from input key material and salt/info, uses hash function from phash}
{ If salt=nil then phash^.HDigestLen binary zeros will be used as salt.}
function hkdfs(phash: PHashDesc; sIKM: Str255; {Hash; input key material as string}
salt: pointer; L_salt: word; {optional salt; can be nil: see below }
info: pointer; L_info: word; {optional context/application specific information}
var DK; dkLen: word): integer; {output key material: addr/length}
{-Derive key DK from input key material and salt/info, uses hash function from phash}
{ If salt=nil then phash^.HDigestLen binary zeros will be used as salt.}
implementation
{helper type}
type
TMSBA = array[0..3] of byte;
{---------------------------------------------------------------------------}
procedure IncMSB(var CTR: TMSBA);
{-Increment CTR[3]..CTR[0], i.e. 32 Bit MSB counter}
var
j: integer;
begin
for j:=3 downto 0 do begin
if CTR[j]=$FF then CTR[j] := 0
else begin
inc(CTR[j]);
exit;
end;
end;
end;
{---------------------------------------------------------------------------}
function pbkdf1(phash: PHashDesc; pPW: pointer; pLen: word; salt: pointer; C: longint; var DK; dkLen: word): integer;
{-Derive key DK from password pPW using 8 byte salt and iteration count C, uses hash function from phash}
var
ctx: THashContext;
Digest: THashDigest;
hlen: word;
begin
if (phash=nil) or (phash^.HSig<>C_HashSig) then begin
pbkdf1 := kdf_err_nil_pointer;
exit;
end;
hLen := phash^.HDigestLen;
if hLen=0 then begin
pbkdf1 := kdf_err_digestlen;
exit;
end;
if hLen<dKLen then begin
pbkdf1 := kdf_err_invalid_dKLen;
exit;
end;
if ((pPW=nil) and (pLen>0)) or (salt=nil) then begin
pbkdf1 := kdf_err_nil_input;
exit;
end;
pbkdf1 := 0;
{calculate T_1 = hash(PW || salt)}
with phash^ do begin
HInit(ctx);
HUpdateXL(ctx, pPW, pLen);
HUpdateXL(ctx, salt, 8);
HFinal(ctx, Digest);
end;
while C>1 do begin
HashFullXL(phash, Digest, @Digest, hlen);
dec(C);
end;
move(Digest, DK, dKLen);
end;
{---------------------------------------------------------------------------}
function pbkdf1s(phash: PHashDesc; sPW: Str255; salt: pointer; C: longint; var DK; dkLen: word): integer;
{-Derive key DK from password string sPW using 8 byte salt and iteration count C, uses hash function from phash}
begin
pbkdf1s := pbkdf1(phash, @sPW[1], length(sPW), salt, C, DK, dkLen);
end;
{---------------------------------------------------------------------------}
function pbkdf2(phash: PHashDesc; pPW: pointer; pLen: word; salt: pointer; sLen,C: longint; var DK; dkLen: longint): integer;
{-Derive key DK from password pPW using salt and iteration count C, uses hash function from phash}
var
k, hLen: word;
i, j, lk: longint;
pDK: pByte; {pointer to DK }
ii: TMSBA; {i in big endian}
u, ucum: THashDigest;
ctx: THMAC_Context;
begin
if phash=nil then begin
pbkdf2 := kdf_err_nil_pointer;
exit;
end;
if phash^.HDigestLen=0 then begin
pbkdf2 := kdf_err_digestlen;
exit;
end;
if ((pPW=nil) and (pLen>0)) or ((salt=nil) and (sLen>0)) then begin
pbkdf2 := kdf_err_nil_input;
exit;
end;
pbkdf2 := 0;
hLen := phash^.HDigestLen;
lk := 0;
pDK := pByte(@DK);
fillchar(ii, sizeof(ii), 0);
for i:=1 to 1 + pred(dkLen) div hLen do begin
IncMSB(ii);
fillchar(ucum, sizeof(ucum),0);
for j:=1 to C do begin
hmac_init(ctx, phash, pPW, pLen);
if j=1 then begin
{U_1 = PRF(pPW, salt || ii)}
hmac_updateXL(ctx, salt, sLen);
hmac_updateXL(ctx, @ii, 4);
end
else begin
{U_i = PRF(pPW, U_(i-1)}
hmac_updateXL(ctx, @u, hLen);
end;
hmac_final(ctx, u);
{update cumulative XOR U_i}
for k:=0 to hLen-1 do ucum[k] := ucum[k] xor u[k];
end;
{T_i = F(P,S,C,l) = ucum}
for k:=0 to hLen-1 do begin
{concat T_i}
if lk<dkLen then begin
pDK^ := ucum[k];
inc(lk);
inc(Ptr2Inc(pDK));
end;
end;
end;
end;
{---------------------------------------------------------------------------}
function pbkdf2s(phash: PHashDesc; sPW: Str255; salt: pointer; sLen, C: longint; var DK; dkLen: longint): integer;
{-Derive key DK from password string sPW using salt and iteration count C, uses hash function from phash}
begin
pbkdf2s := pbkdf2(phash, @sPW[1], length(sPW), salt, sLen, C, DK, dkLen);
end;
{---------------------------------------------------------------------------}
function kdfx(phash: PHashDesc; x: byte; Z, pOtherInfo: pointer; zLen, oLen: word; var DK; dkLen: word): integer;
{-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
{ Internal function for all kdf1, kdf2, kdf3, mgf1}
var
ctr: TMSBA; {i in big endian}
ctx: THashContext;
pDK: pByte; {pointer to DK }
Digest: THashDigest;
i, k, lk, hLen: word;
begin
if (phash=nil) or (phash^.HSig<>C_HashSig) then begin
kdfx := kdf_err_nil_pointer;
exit;
end;
hLen := phash^.HDigestLen;
if hLen=0 then begin
kdfx := kdf_err_digestlen;
exit;
end;
if ((Z=nil) and (zLen>0)) or ((pOtherInfo=nil) and (oLen>0)) then begin
kdfx := kdf_err_nil_input;
exit;
end;
kdfx := 0;
fillchar(ctr, sizeof(ctr), 0);
if x=2 then IncMSB(ctr);
lk := 0;
pDK := pByte(@DK);
for i:=1 to 1 + pred(dkLen) div hLen do begin
if x=3 then begin
{Hash(ctr || Z || [OtherInfo])} {x=3}
phash^.HInit(ctx);
phash^.HUpdateXL(ctx, @ctr, sizeof(ctr));
phash^.HUpdateXL(ctx, Z, zLen);
end
else begin
{Hash(Z || ctr || [OtherInfo])} {x=1,2}
phash^.HInit(ctx);
phash^.HUpdateXL(ctx, Z, zLen);
phash^.HUpdateXL(ctx, @ctr, sizeof(ctr));
end;
if oLen<>0 then phash^.HUpdateXL(ctx, pOtherInfo, oLen);
phash^.HFinal(ctx, Digest);
for k:=0 to hLen-1 do begin
{store T_i}
if lk<dkLen then begin
pDK^ := Digest[k];
inc(lk);
inc(Ptr2Inc(pDK));
end
else exit;
end;
IncMSB(ctr);
end;
end;
{---------------------------------------------------------------------------}
function kdf1(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
{-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
begin
kdf1 := kdfx(phash, 1, Z, pOtherInfo, zLen, oiLen, DK, dkLen);
end;
{---------------------------------------------------------------------------}
function kdf2(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
{-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
begin
kdf2 := kdfx(phash, 2, Z, pOtherInfo, zLen, oiLen, DK, dkLen);
end;
{---------------------------------------------------------------------------}
function kdf3(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
{-Derive key DK from shared secret Z using optional OtherInfo, hash function from phash}
begin
kdf3 := kdfx(phash, 3, Z, pOtherInfo, zLen, oiLen, DK, dkLen);
end;
{---------------------------------------------------------------------------}
function mgf1(phash: PHashDesc; pSeed: pointer; sLen: word; var Mask; mLen: word): integer;
{-Derive Mask from seed, hash function from phash, Mask Generation Function 1 for PKCS #1}
begin
mgf1 := kdfx(phash, 1, pSeed, nil, sLen, 0, Mask, mLen);
end;
{---------------------------------------------------------------------------}
function hkdf(phash: PHashDesc; {Descriptor of the Hash to use}
pIKM: pointer; L_IKM: word; {input key material: addr/length}
salt: pointer; L_salt: word; {optional salt; can be nil: see below }
info: pointer; L_info: word; {optional context/application specific information}
var DK; dkLen: word): integer; {output key material: addr/length}
{-Derive key DK from input key material and salt/info, uses hash function from phash}
{ If salt=nil then phash^.HDigestLen binary zeros will be used as salt.}
var
PRK,TI: THashDigest;
ctx: THMAC_Context;
i,k,hLen,lt,lk: word;
ctr: byte;
pDK: pByte;
begin
{Ref: (IETF) rfc5869 - H. Krawczyk, P. Eronen, May 2010}
{HMAC-based Extract-and-Expand Key Derivation Function (HKDF)}
{Check input parameters}
if (phash=nil) or (phash^.HSig<>C_HashSig) then begin
hkdf := kdf_err_nil_pointer;
exit;
end;
if ((pIKM=nil) and (L_IKM>0)) or ((info=nil) and (L_info>0)) then begin
hkdf := kdf_err_nil_input;
exit;
end;
hLen := phash^.HDigestLen;
if hLen=0 then begin
hkdf := kdf_err_digestlen;
exit;
end;
{Stage 1: Extract}
hkdf := 0;
{if salt=nil then hLen binary zeros are used}
if salt=nil then begin
fillchar(TI, sizeof(TI), 0);
hmac_init(ctx, phash, @TI, hLen);
end
else hmac_init(ctx, phash, salt, L_salt);
hmac_update(ctx, pIKM, L_IKM);
hmac_final(ctx, PRK);
{Stage 2: Expand}
lt := 0;
lk := 0;
ctr := 1;
pDK := pByte(@DK);
for i:=1 to 1 + pred(dkLen) div hLen do begin
{calculate T_i from T_(i-1), info, and ctr}
hmac_init(ctx, phash, @PRK, hLen);
hmac_update(ctx, @TI, lt);
hmac_update(ctx, info, L_info);
hmac_update(ctx, @ctr, 1);
hmac_final(ctx, TI);
for k:=0 to hLen-1 do begin
{store T_i}
if lk<dkLen then begin
pDK^ := TI[k];
inc(lk);
inc(Ptr2Inc(pDK));
end
else exit;
end;
lt := hLen;
inc(ctr);
end;
end;
{---------------------------------------------------------------------------}
function hkdfs(phash: PHashDesc; sIKM: Str255; {Hash; input key material as string}
salt: pointer; L_salt: word; {optional salt; can be nil: see below }
info: pointer; L_info: word; {optional context/application specific information}
var DK; dkLen: word): integer; {output key material: addr/length}
{-Derive key DK from input key material and salt/info, uses hash function from phash}
{ If salt=nil then phash^.HDigestLen binary zeros will be used as salt.}
begin
hkdfs := hkdf(phash,@sIKM[1],length(sIKM),salt,L_salt,info,L_info,DK,dkLen);
end;
end.