mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
439 lines
16 KiB
ObjectPascal
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.
|