fcl-hash: added PSS helper functions I2OSP, MGF1SHA256

This commit is contained in:
mattias 2022-05-01 20:17:53 +02:00
parent a81e527e1d
commit 7d98462c1c
3 changed files with 185 additions and 8 deletions

View File

@ -42,6 +42,7 @@ Procedure BytesToHexStr(out aHexStr : AnsiString; aBytes : PByte; aSize : Intege
Procedure BytesToHexStr(out aHexStr : AnsiString; aBytes : TBytes); overload;
Function BytesToHexStr(aBytes : TBytes) : AnsiString; overload;
Procedure BytesToHexStrAppend(aBytes : TBytes;var aHexStr : AnsiString);
Function StringToHex(const s: string): string; overload;
Procedure BytesEncodeBase64(Source: Tbytes; out Dest: AnsiString; const IsURL, MultiLines, Padding: Boolean);
Function BytesEncodeBase64(Source: Tbytes; const IsURL, MultiLines, Padding: Boolean) : AnsiString;
@ -59,6 +60,9 @@ var
implementation
Const
HexDigits: Array[0..15] of AnsiChar = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
procedure BytesFromVar(out aBytes: TBytes; aLocation: Pointer; aSize: Integer);
begin
@ -151,10 +155,6 @@ begin
end;
procedure BytesToHexStr(out aHexStr : AnsiString; aBytes : PByte; aSize : Integer);
Const
Digits: Array[0..15] of AnsiChar = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
var
I: Integer;
PB : Pbyte;
@ -169,9 +169,9 @@ begin
PC:=PChar(aHexStr);
for I:=0 to aSize-1 do
begin
PC^:=Digits[(PB^ shr 4) and $0f];
PC^:=HexDigits[PB^ shr 4];
Inc(PC);
PC^:=Digits[PB^ and $0f];
PC^:=HexDigits[PB^ and $f];
Inc(PC);
Inc(PB);
end;
@ -195,6 +195,12 @@ begin
aHexStr:=aHexStr+BytesToHexStr(aBytes);
end;
function StringToHex(const s: string): string;
begin
if s='' then exit;
BytesToHexStr(Result,@s[1],length(s));
end;
function GetBase64EncodedSize(const SourceSize: Int32; const MultiLines: Boolean): Int32;
var
Lines: Int32;

View File

@ -9,7 +9,7 @@ interface
{off $DEFINE CRYPTO_DEBUG}
uses
sysutils, Classes, fpTLSBigInt, fphashutils, fpasn, basenenc;
sysutils, Classes, sha1, fpsha256, fpTLSBigInt, fphashutils, fpasn, basenenc;
const
RSAPublicKeyOID = '1.2.840.113549.1.1.1';
@ -106,6 +106,17 @@ function TestRS256Verify: Boolean;
function EncodeDigestInfoSHA(SHAType, len: byte): TBytes;
// integer <-> octetstring
function I2OSP(c: DWord; Len: integer): string;
function OSP2I(const Octet: string): DWord;
// MGF1 (Mask Generating Function 1) of PKCS1 (Public Key Cryptography Standard #1)
type
THashFunction = function(const s: string): string; // string to hash digest
function MGF1(const InputStr: string; Len: integer; HashFunc: THashFunction): string;
function MGF1SHA1(const InputStr: string; Len: integer): string;
function MGF1SHA256(const InputStr: string; Len: integer): string;
implementation
const
@ -678,6 +689,80 @@ begin
];
end;
function I2OSP(c: DWord; Len: integer): string;
var
i: DWord;
begin
if Len>4 then
raise Exception.Create('20220501190110');
SetLength(Result{%H-},Len);
for i:=Len downto 1 do
begin
Result[i]:=chr(c and $ff);
c:=c shr 8;
end;
if c>0 then
raise Exception.Create('20220501190124');
end;
function OSP2I(const Octet: string): DWord;
var
i: Integer;
begin
Result:=0;
if length(Octet)>4 then
raise Exception.Create('20220501190308');
for i:=1 to length(Octet) do
Result:=Result shl 8 + ord(Octet[i]);
end;
function MGF1(const InputStr: string; Len: integer; HashFunc: THashFunction
): string;
var
Counter: DWord;
begin
Counter:=0;
Result:='';
while length(Result)<Len do
begin
Result:=Result+HashFunc(InputStr+I2OSP(Counter,4));
inc(Counter);
end;
SetLength(Result,Len);
end;
function SHA1StrToDigest(const InputStr: string): string;
var
Digest: TSHA1Digest;
begin
Digest:=SHA1String(InputStr);
SetLength(Result{%H-},length(Digest));
System.Move(Digest[0],Result[1],length(Digest));
if Digest[0]=0 then ;
end;
function MGF1SHA1(const InputStr: string; Len: integer): string;
begin
Result:=MGF1(InputStr,Len,@SHA1StrToDigest);
end;
function SHA256StrToDigest(const InputStr: string): string;
var
SHA256: TSHA256;
begin
SHA256.Init;
SHA256.Update(@InputStr[1],length(InputStr));
SHA256.Final;
SetLength(Result{%H-},length(SHA256.Digest));
System.Move(SHA256.Digest[0],Result[1],length(SHA256.Digest));
end;
function MGF1SHA256(const InputStr: string; Len: integer): string;
begin
Result:=MGF1(InputStr,Len,@SHA256StrToDigest);
end;
{ TX509RSAPrivateKey }
procedure TX509RSAPrivateKey.InitWithHexStrings(const n, e, d, p, q, dp, dq, qi: string

View File

@ -28,6 +28,7 @@ type
end;
{ TTestJWT }
TTestJWT= class(TTestCase)
private
FJWT: TJWT;
@ -42,18 +43,28 @@ type
published
procedure TestSignNone;
procedure TestVerifyNone;
// SHA
procedure TestSignSHA256;
procedure TestVerifySHA256;
procedure TestSignSHA512;
procedure TestVerifySHA512;
procedure TestSignSHA384;
procedure TestVerifySHA384;
// ES
procedure TestVerifyES256;
procedure TestVerifyES256Pem;
// RSA
procedure TestVerifyRS256Pem;
procedure TestVerifyRS384Pem;
procedure TestVerifyRS512Pem;
procedure TestVerifyRS256_rfc7515;
procedure TestI2OSP;
procedure TestOSP2I;
procedure TestMGF1SHA1;
procedure TestMGF1SHA256;
end;
implementation
@ -236,7 +247,6 @@ begin
AssertEquals('Have correct sub','1234567890',FVerifyResult.Claims.sub);
AssertEquals('Have correct name','John Doe',(TMyJWT(FVerifyResult).Claims as TMyClaims).Name);
AssertEquals('Have correct admin',true,(TMyJWT(FVerifyResult).Claims as TMyClaims).Admin);
end;
procedure TTestJWT.TestVerifyES256Pem;
@ -396,6 +406,82 @@ begin
end;
end;
procedure TTestJWT.TestI2OSP;
procedure t(c: DWord; Len: integer; const Expected: string);
var
Actual: String;
begin
Actual:=I2OSP(c,Len);
if Actual<>Expected then
Fail('I2OSP('+IntToStr(c)+','+IntToStr(Len)+') expected "'+StringToHex(Expected)+'", but got "'+StringToHex(Actual)+'"');
end;
begin
t(0,0,'');
t(0,1,#0);
t(1,1,#1);
t(1,2,#0#1);
t(258,2,#1#2);
t($10203,3,#1#2#3);
t($1020304,4,#1#2#3#4);
t($ffffffff,4,#255#255#255#255);
end;
procedure TTestJWT.TestOSP2I;
procedure t(const Octet: string; const Expected: DWord);
var
Actual: DWord;
begin
Actual:=OSP2I(Octet);
if Actual<>Expected then
Fail('OSP2I('+StringToHex(Octet)+') expected "'+HexStr(Expected,8)+'", but got "'+HexStr(Actual,8)+'"');
end;
begin
t('',0);
t(#0,0);
t(#0#0,0);
t(#0#0#0,0);
t(#0#0#0#0,0);
t(#1#0#0#0,$1000000);
t(#255#255#255#255,$ffffffff);
end;
procedure TTestJWT.TestMGF1SHA1;
procedure t(const InputStr: string; Len: integer; const ExpectedHex: String);
var
ActualHex: string;
begin
ActualHex:=StringToHex(MGF1SHA1(InputStr,Len));
if ActualHex<>ExpectedHex then
Fail('MGF1SHA1('+StringToHex(InputStr)+','+IntToStr(Len)+') expected "'+ExpectedHex+'", but got "'+ActualHex+'"');
end;
begin
t('foo',3,'1AC907');
t('foo',5,'1AC9075CD4');
t('bar',5,'BC0C655E01');
t('bar',50,'BC0C655E016BC2931D85A2E675181ADCEF7F581F76DF2739DA74FAAC41627BE2F7F415C89E983FD0CE80CED9878641CB4876');
end;
procedure TTestJWT.TestMGF1SHA256;
procedure t(const InputStr: string; Len: integer; const ExpectedHex: String);
var
ActualHex: string;
begin
ActualHex:=StringToHex(MGF1SHA256(InputStr,Len));
if ActualHex<>ExpectedHex then
Fail('MGF1SHA256('+StringToHex(InputStr)+','+IntToStr(Len)+') expected "'+ExpectedHex+'", but got "'+ActualHex+'"');
end;
begin
t('bar',50,'382576A7841021CC28FC4C0948753FB8312090CEA942EA4C4E735D10DC724B155F9F6069F289D61DACA0CB814502EF04EAE1');
end;
procedure TTestJWT.SetUp;
begin
Inherited;