Browse Source

* Demo authenticator app

Michaël Van Canneyt 3 years ago
parent
commit
4d5d1c8e10

+ 56 - 0
packages/fcl-hash/examples/authenticator.lpi

@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+        <UseDefaultCompilerOptions Value="True"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="authenticator"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <Units>
+      <Unit>
+        <Filename Value="authenticator.pp"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="authenticator"/>
+    </Target>
+    <SearchPaths>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 216 - 0
packages/fcl-hash/examples/authenticator.pp

@@ -0,0 +1,216 @@
+{ Demo Google-authenticator compatible authenticator app
+
+  Copyright (C) 2022 Michael Van Canneyt [email protected]
+
+  This source is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+
+  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  A copy of the GNU General Public License is available on the World Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can
+  also obtain it by writing to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.
+}
+
+{$mode objfpc}
+{$h+}
+uses sysutils, classes, onetimepass, inifiles, custapp;
+
+Type
+  TMode = (mError,mHelp,mAdd,mDelete,mPrint,mGenerate,mList,mCheck);
+
+
+  { TAuthenticatorApplication }
+
+  TAuthenticatorApplication = Class(TCustomApplication)
+  Private
+    FIni : TMemIniFile;
+    procedure CheckKey(aName, aCode: String);
+    function getMode: TMode;
+    procedure ListKeys;
+    procedure PrintKey(aKey: String);
+    procedure Usage(const aError: String);
+  Public
+    Constructor Create(aOwner : TComponent); override;
+    Destructor Destroy; override;
+    Procedure DoRun; override;
+  end;
+
+Const
+  SKeys = 'Keys';
+  Need : array[TMode] of Integer = (0,0,2,1,1,0,0,2);
+
+constructor TAuthenticatorApplication.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+  FIni:=TMemIniFile.Create(GetAppConfigFile(False));
+end;
+
+destructor TAuthenticatorApplication.Destroy;
+begin
+  FreeAndNil(FIni);
+  inherited Destroy;
+end;
+
+Procedure TAuthenticatorApplication.Usage(const aError : String);
+
+begin
+  if (aError<>'') then
+    Writeln('Error: ',aError);
+  Writeln('Usage: ',ExtractFileName(ParamStr(0)),' [-a|-d|-h|-p|-g|-c|-l] [name [key|Value]');
+  Writeln('If no options are specified, print key code');
+  Writeln('-h --help      This help text');
+  Writeln('-a --add       Add key with given name and key value');
+  Writeln('-d --remove    Remove key with given name');
+  Writeln('-g --generate  Generate and print new key');
+  Writeln('-l --list      List known keys');
+  Writeln('-c --check     Check code against key for given name');
+  ExitCode:=Ord(AError<>'')
+end;
+
+Function TAuthenticatorApplication.getMode : TMode;
+
+var
+  aMode : TMode;
+
+begin
+  aMode:=mPrint;
+  if HasOption('h','help') then
+    aMode:=mHelp
+  else if HasOption('a','add') then
+    aMode:=mAdd
+  else if HasOption('g','generate') then
+    aMode:=mGenerate
+  else if HasOption('c','check') then
+    aMode:=mCheck
+  else if HasOption('r','remove') then
+    aMode:=mDelete
+  else if HasOption('l','list') then
+    aMode:=mList;
+  result:=aMode;
+end;
+
+Procedure TAuthenticatorApplication.CheckKey(aName,aCode : String);
+
+Var
+  S : String;
+  aCount :  Integer;
+
+begin
+  S:=FIni.ReadString(SKeys,aName,'');
+  if S='' then
+    begin
+    Writeln('No such key : ',aName);
+    ExitCode:=1;
+    end
+  else
+    begin
+    if TOTPValidate(S,StrToIntDef(aCode,-1),1,aCount) then
+      Writeln('Code OK')
+    else
+      begin
+      Writeln('Code wrong');
+      ExitCode:=1;
+      end;
+    end;
+end;
+
+Procedure TAuthenticatorApplication.PrintKey(aKey : String);
+
+Var
+  S : String;
+
+begin
+  S:=FIni.ReadString(SKeys,aKey,'');
+  if S='' then
+    begin
+    Writeln('No such key : ',S);
+    ExitCode:=1;
+    end
+  else
+    Writeln('Token: ',TOTPGenerateToken(S));
+end;
+
+Procedure TAuthenticatorApplication.ListKeys;
+
+Var
+  L : TStrings;
+  I : Integer;
+  N,K : String;
+
+begin
+  L:=TStringList.Create;
+  try
+    Fini.ReadSectionValues(SKeys,L);
+    Writeln('Known keys: ');
+    For I:=0 to L.Count-1 do
+      begin
+      L.GetNameValue(I,N,K);
+      Writeln(N,' : ',K);
+      end;
+  finally
+    L.Free;
+  end;
+end;
+
+Procedure TAuthenticatorApplication.DoRun;
+
+Const
+  Opts : String ='harpgcl';
+  LongOpts : Array of string = ('help','add','remove','print','generate','check','list');
+
+Var
+  aErr : String;
+  aMode : TMode;
+  NonArgs : TStringArray;
+
+begin
+  Terminate;
+  aMode:=mError;
+  aErr:=CheckOptions(Opts,LongOpts);
+  NonArgs:=GetNonOptions(Opts,LongOpts);
+  if (aErr='') then
+    begin
+    aMode:=GetMode;
+    if aMode in [mAdd,mDelete,mGenerate] then
+      if Length(NonArgs)<>Need[aMode] then
+        begin
+        aErr:=Format('Need %d arguments, got %d',[Need[aMode],Length(NonArgs)]);
+        aMode:=mError;
+        end;
+    end;
+  Case aMode of
+    mError,mHelp:
+      Usage(aErr);
+    mAdd:
+      begin
+      FIni.WriteString(SKeys,NonArgs[0],NonArgs[1]);
+      Fini.UpdateFile;
+      end;
+    mDelete:
+      begin
+      FIni.DeleteKey(SKeys,NonArgs[0]);
+      Fini.UpdateFile;
+      end;
+    mPrint:
+      begin
+      PrintKey(NonArgs[0]);
+      end;
+    mGenerate:
+      Writeln(TOTPSharedSecret());
+    mCheck:
+      begin
+      CheckKey(NonArgs[0],NonArgs[1]);
+      end;
+    mList:
+      ListKeys;
+  end;
+end;
+
+begin
+  CustomApplication:=TAuthenticatorApplication.Create(Nil);
+  CustomApplication.Initialize;
+  CustomApplication.Run;
+  CustomApplication.Free;
+
+end.