浏览代码

fcl-hash: added PSS helper functions I2OSP, MGF1SHA256

mattias 3 年之前
父节点
当前提交
7d98462c1c
共有 3 个文件被更改,包括 185 次插入8 次删除
  1. 12 6
      packages/fcl-hash/src/fphashutils.pp
  2. 86 1
      packages/fcl-hash/src/fprsa.pas
  3. 87 1
      packages/fcl-web/tests/tcjwt.pp

+ 12 - 6
packages/fcl-hash/src/fphashutils.pp

@@ -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;

+ 86 - 1
packages/fcl-hash/src/fprsa.pas

@@ -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

+ 87 - 1
packages/fcl-web/tests/tcjwt.pp

@@ -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;