├── .gitattributes ├── .gitignore ├── Base32U.pas ├── Example ├── ExampleApp.dpr ├── ExampleApp.dproj ├── ExampleApp.res ├── Form.dfm └── Form.pas ├── GoogleOTP.pas ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment these types if you want even more clean repository. But be careful. 2 | # It can make harm to an existing project source. Read explanations below. 3 | # 4 | # Resource files are binaries containing manifest, project icon and version info. 5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. 6 | #*.res 7 | # 8 | # Type library file (binary). In old Delphi versions it should be stored. 9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored. 10 | #*.tlb 11 | # 12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7. 13 | # Uncomment this if you are not using diagrams or use newer Delphi version. 14 | #*.ddp 15 | # 16 | # Visual LiveBindings file. Added in Delphi XE2. 17 | # Uncomment this if you are not using LiveBindings Designer. 18 | #*.vlb 19 | # 20 | # Deployment Manager configuration file for your project. Added in Delphi XE2. 21 | # Uncomment this if it is not mobile development and you do not use remote debug feature. 22 | #*.deployproj 23 | # 24 | # C++ object files produced when C/C++ Output file generation is configured. 25 | # Uncomment this if you are not using external objects (zlib library for example). 26 | #*.obj 27 | # 28 | 29 | # Delphi compiler-generated binaries (safe to delete) 30 | *.exe 31 | *.dll 32 | *.bpl 33 | *.bpi 34 | *.dcp 35 | *.so 36 | *.apk 37 | *.drc 38 | *.map 39 | *.dres 40 | *.rsm 41 | *.tds 42 | *.dcu 43 | *.lib 44 | *.a 45 | *.o 46 | *.ocx 47 | 48 | # Delphi autogenerated files (duplicated info) 49 | *.cfg 50 | *.hpp 51 | *Resource.rc 52 | 53 | # Delphi local files (user-specific info) 54 | *.local 55 | *.identcache 56 | *.projdata 57 | *.tvsconfig 58 | *.dsk 59 | 60 | # Delphi history and backups 61 | __history/ 62 | __recovery/ 63 | *.~* 64 | 65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi) 66 | *.stat 67 | -------------------------------------------------------------------------------- /Base32U.pas: -------------------------------------------------------------------------------- 1 | {############################################################################### 2 | https://github.com/wendelb/DelphiOTP 3 | ###############################################################################} 4 | unit Base32U; 5 | 6 | { 7 | 8 | ================================================================================ 9 | If you found this Unit as your were looking for a delphi Base32 Implementation, 10 | that is also unicode ready, please see the Readme! 11 | ================================================================================ 12 | 13 | } 14 | 15 | {$IFDEF FPC} 16 | {$mode objfpc}{$H+} 17 | {$ENDIF} 18 | 19 | interface 20 | 21 | uses 22 | {$IFNDEF FPC}System.{$ENDIF}SysUtils; // For UpperCase (Base32Decode) 23 | 24 | type 25 | Base32 = class 26 | public 27 | /// 28 | /// Base32-String (Attention: No validity checks) 29 | /// 30 | /// 31 | /// Decodes a Base32-String 32 | /// 33 | /// 34 | /// Unicode String containing the ANSI-Data from that Base32-Input 35 | /// 36 | class function Decode(const inString: String): String; 37 | end; 38 | 39 | // As the FromBase32String Function doesn't has the result I'm looking for, here 40 | // is my version of that function. This is converted from a PHP function, which 41 | // can be found here: https://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/ 42 | const 43 | ValidChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; 44 | 45 | implementation 46 | 47 | {$REGION 'Base32Functions'} 48 | 49 | 50 | function Base32Decode(const source: String): String; 51 | var 52 | UpperSource: String; 53 | p, i, l, n, j: Integer; 54 | begin 55 | UpperSource := UpperCase(source); 56 | 57 | l := Length(source); 58 | n := 0; j := 0; 59 | Result := ''; 60 | 61 | for i := 1 to l do 62 | begin 63 | n := n shl 5; // Move buffer left by 5 to make room 64 | 65 | p := Pos(UpperSource[i], ValidChars); 66 | if p >= 0 then 67 | n := n + (p - 1); // Add value into buffer 68 | 69 | j := j + 5; // Keep track of number of bits in buffer 70 | 71 | if (j >= 8) then 72 | begin 73 | j := j - 8; 74 | Result := Result + chr((n AND ($FF shl j)) shr j); 75 | end; 76 | end; 77 | end; 78 | 79 | {$ENDREGION} 80 | 81 | { Base32 } 82 | 83 | class function Base32.Decode(const inString: String): String; 84 | begin 85 | Result := Base32Decode(inString); 86 | end; 87 | 88 | end. 89 | -------------------------------------------------------------------------------- /Example/ExampleApp.dpr: -------------------------------------------------------------------------------- 1 | program ExampleApp; 2 | 3 | uses 4 | Vcl.Forms, 5 | Form in 'Form.pas' {FormOTP}, 6 | Base32U in '..\Base32U.pas', 7 | GoogleOTP in '..\GoogleOTP.pas'; 8 | 9 | {$R *.res} 10 | 11 | begin 12 | Application.Initialize; 13 | Application.MainFormOnTaskbar := True; 14 | Application.Title := 'One Time Password Example'; 15 | Application.CreateForm(TFormOTP, FormOTP); 16 | Application.Run; 17 | end. 18 | -------------------------------------------------------------------------------- /Example/ExampleApp.dproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {260234B3-7B09-4E8B-A408-DCA45A6D6775} 4 | 13.4 5 | VCL 6 | ExampleApp.dpr 7 | True 8 | Debug 9 | Win32 10 | 1 11 | Application 12 | 13 | 14 | true 15 | 16 | 17 | true 18 | Base 19 | true 20 | 21 | 22 | true 23 | Base 24 | true 25 | 26 | 27 | true 28 | Base 29 | true 30 | 31 | 32 | true 33 | Cfg_1 34 | true 35 | true 36 | 37 | 38 | true 39 | Base 40 | true 41 | 42 | 43 | bindcompfmx;fmx;rtl;dbrtl;IndySystem;DbxClientDriver;bindcomp;inetdb;DBXInterBaseDriver;DataSnapCommon;DataSnapClient;DataSnapServer;DataSnapProviderClient;xmlrtl;DbxCommonDriver;IndyProtocols;DBXMySQLDriver;dbxcds;soaprtl;bindengine;DBXOracleDriver;dsnap;DBXInformixDriver;IndyCore;fmxase;DBXFirebirdDriver;inet;fmxobj;inetdbxpress;DBXSybaseASADriver;fmxdae;dbexpress;DataSnapIndy10ServerTransport;IPIndyImpl;$(DCC_UsePackage) 44 | $(BDS)\bin\delphi_PROJECTICON.ico 45 | System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) 46 | .\$(Platform)\$(Config) 47 | .\$(Platform)\$(Config) 48 | false 49 | false 50 | false 51 | false 52 | false 53 | 54 | 55 | DBXOdbcDriver;DBXSybaseASEDriver;vclimg;vclactnband;vcldb;bindcompvcl;vcldsnap;vclie;vcltouch;DBXDb2Driver;websnap;VclSmp;vcl;DBXMSSQLDriver;dsnapcon;vclx;webdsnap;$(DCC_UsePackage) 56 | 57 | 58 | vcldbx;frx16;TeeDB;inetdbbde;Tee;DBXOdbcDriver;svnui;DBXSybaseASEDriver;vclimg;frxDB16;intrawebdb_120_160;fmi;fs16;vclactnband;FMXTee;TeeUI;vcldb;bindcompvcl;vcldsnap;vclie;vcltouch;Intraweb_120_160;DBXDb2Driver;websnap;vclribbon;frxe16;VclSmp;fsDB16;vcl;DataSnapConnectors;CloudService;DBXMSSQLDriver;CodeSiteExpressPkg;FmxTeeUI;dsnapcon;vclx;webdsnap;svn;bdertl;adortl;$(DCC_UsePackage) 59 | true 60 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) 61 | 1033 62 | $(BDS)\bin\default_app.manifest 63 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= 64 | 65 | 66 | DEBUG;$(DCC_Define) 67 | false 68 | true 69 | true 70 | true 71 | 72 | 73 | true 74 | true 75 | 1033 76 | false 77 | 78 | 79 | false 80 | RELEASE;$(DCC_Define) 81 | 0 82 | false 83 | 84 | 85 | 86 | MainSource 87 | 88 | 89 |
FormOTP
90 | dfm 91 |
92 | 93 | 94 | 95 | Cfg_2 96 | Base 97 | 98 | 99 | Base 100 | 101 | 102 | Cfg_1 103 | Base 104 | 105 |
106 | 107 | Delphi.Personality.12 108 | 109 | 110 | 111 | 112 | False 113 | False 114 | 1 115 | 0 116 | 0 117 | 0 118 | False 119 | False 120 | False 121 | False 122 | False 123 | 1031 124 | 1252 125 | 126 | 127 | 128 | 129 | 1.0.0.0 130 | 131 | 132 | 133 | 134 | 135 | 1.0.0.0 136 | 137 | 138 | 139 | ExampleApp.dpr 140 | 141 | 142 | Microsoft Office 2000 Beispiele für gekapselte Komponenten für Automatisierungsserver 143 | Microsoft Office XP Beispiele für gekapselte Komponenten für Automation Server 144 | 145 | 146 | 147 | 148 | False 149 | True 150 | 151 | 152 | 12 153 | 154 | 155 | 156 |
157 | -------------------------------------------------------------------------------- /Example/ExampleApp.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wendelb/DelphiOTP/4335bda4213c22a4fea9a97546255bf3d1a44bb6/Example/ExampleApp.res -------------------------------------------------------------------------------- /Example/Form.dfm: -------------------------------------------------------------------------------- 1 | object FormOTP: TFormOTP 2 | Left = 0 3 | Top = 0 4 | BorderIcons = [biSystemMenu, biMinimize] 5 | BorderStyle = bsSingle 6 | Caption = 'One Time Password Example' 7 | ClientHeight = 236 8 | ClientWidth = 141 9 | Color = clBtnFace 10 | Font.Charset = DEFAULT_CHARSET 11 | Font.Color = clWindowText 12 | Font.Height = -11 13 | Font.Name = 'Tahoma' 14 | Font.Style = [] 15 | OldCreateOrder = False 16 | ScreenSnap = True 17 | PixelsPerInch = 96 18 | TextHeight = 13 19 | object Label1: TLabel 20 | Left = 8 21 | Top = 8 22 | Width = 105 23 | Height = 13 24 | Caption = 'Private Key (base32):' 25 | end 26 | object Label2: TLabel 27 | Left = 8 28 | Top = 103 29 | Width = 43 30 | Height = 13 31 | Caption = 'Counter:' 32 | Enabled = False 33 | end 34 | object Label3: TLabel 35 | Left = 8 36 | Top = 183 37 | Width = 94 38 | Height = 13 39 | Caption = 'One Time Password' 40 | end 41 | object EdtKey: TEdit 42 | Left = 8 43 | Top = 27 44 | Width = 121 45 | Height = 21 46 | TabOrder = 0 47 | end 48 | object CBTOPT: TCheckBox 49 | Left = 8 50 | Top = 54 51 | Width = 121 52 | Height = 35 53 | Caption = 'Use Time Based One Time Password' 54 | Checked = True 55 | State = cbChecked 56 | TabOrder = 1 57 | WordWrap = True 58 | OnClick = CBTOPTClick 59 | end 60 | object EdtHOTP: TEdit 61 | Left = 8 62 | Top = 122 63 | Width = 121 64 | Height = 21 65 | Enabled = False 66 | NumbersOnly = True 67 | TabOrder = 2 68 | end 69 | object BtnCalculate: TButton 70 | Left = 8 71 | Top = 149 72 | Width = 121 73 | Height = 25 74 | Caption = 'Calculate' 75 | Default = True 76 | TabOrder = 3 77 | OnClick = BtnCalculateClick 78 | end 79 | object EdtResult: TEdit 80 | Left = 8 81 | Top = 202 82 | Width = 121 83 | Height = 21 84 | ReadOnly = True 85 | TabOrder = 4 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /Example/Form.pas: -------------------------------------------------------------------------------- 1 | unit Form; 2 | 3 | interface 4 | 5 | uses 6 | Winapi.Windows, System.SysUtils, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, 7 | Vcl.StdCtrls, System.Classes; 8 | 9 | type 10 | TFormOTP = class(TForm) 11 | EdtKey: TEdit; 12 | Label1: TLabel; 13 | CBTOPT: TCheckBox; 14 | Label2: TLabel; 15 | EdtHOTP: TEdit; 16 | BtnCalculate: TButton; 17 | Label3: TLabel; 18 | EdtResult: TEdit; 19 | procedure BtnCalculateClick(Sender: TObject); 20 | procedure CBTOPTClick(Sender: TObject); 21 | private 22 | { Private-Deklarationen } 23 | public 24 | { Public-Deklarationen } 25 | end; 26 | 27 | var 28 | FormOTP: TFormOTP; 29 | 30 | implementation 31 | 32 | {$R *.dfm} 33 | 34 | uses 35 | GoogleOTP; 36 | 37 | procedure TFormOTP.BtnCalculateClick(Sender: TObject); 38 | var 39 | Secret: String; 40 | n: Integer; 41 | begin 42 | Secret := EdtKey.Text; 43 | 44 | if (CBTOPT.Checked) then 45 | begin 46 | // This is the default in case the parameter is missing! 47 | n := -1; 48 | end 49 | else 50 | begin 51 | if (not TryStrToInt(EdtHOTP.Text, n)) then 52 | begin 53 | MessageBox(Handle, 'Please enter a valid number into the counter', 54 | 'One Time Password Example', MB_ICONEXCLAMATION); 55 | Exit; 56 | end; 57 | end; 58 | 59 | EdtResult.Text := Format('%.6d', [CalculateOTP(Secret, n)]); 60 | end; 61 | 62 | procedure TFormOTP.CBTOPTClick(Sender: TObject); 63 | begin 64 | Label2.Enabled := not CBTOPT.Checked; 65 | EdtHOTP.Enabled := not CBTOPT.Checked; 66 | end; 67 | 68 | end. 69 | -------------------------------------------------------------------------------- /GoogleOTP.pas: -------------------------------------------------------------------------------- 1 | {############################################################################### 2 | https://github.com/wendelb/DelphiOTP 3 | ###############################################################################} 4 | unit GoogleOTP; 5 | 6 | {$IFDEF FPC} 7 | {$mode objfpc}{$H+} 8 | {$ENDIF} 9 | 10 | interface 11 | 12 | uses 13 | {$IFNDEF FPC}System.{$ENDIF}SysUtils, {$IFNDEF FPC}System.{$ENDIF}Math, Base32U, {$IFNDEF FPC}System.{$ENDIF}DateUtils 14 | {$IFNDEF FPC} 15 | , IdGlobal, IdHMACSHA1 16 | {$ELSE} 17 | , HMAC 18 | {$IFEND}; 19 | 20 | (* 21 | 22 | Test Case for the CalculateOTP function 23 | --------------------------------------- 24 | 25 | Init key: AAAAAAAAAAAAAAAAAAAA 26 | Timestamp: 1 27 | BinCounter: 0000000000000001 (HEX-Representation) 28 | Hash: eeb00b0bcc864679ff2d8dd30bec495cb5f2ee9e (HEX-Representation) 29 | Offset: 14 30 | Part 1: 73 31 | Part 2: 92 32 | Part 3: 181 33 | Part 4: 242 34 | One time password: 812658 35 | 36 | Easy Display: Format('%.6d', [CalculateOTP(SECRET)]); 37 | *) 38 | 39 | function CalculateOTP(const Secret: String; const Counter: Integer = -1): Integer; 40 | function ValidateTOPT(const Secret: String; const Token: Integer; const WindowSize: Integer = 4): Boolean; 41 | function GenerateOTPSecret(len: Integer = -1): String; 42 | 43 | implementation 44 | 45 | Type 46 | {$IFNDEF FPC} 47 | OTPBytes = TIdBytes; 48 | {$ELSE} 49 | OTPBytes = TBytes; 50 | {$IFEND} 51 | 52 | const 53 | otpLength = 6; 54 | keyRegeneration = 30; 55 | SecretLengthDef = 20; 56 | 57 | {$IFDEF FPC} 58 | function BytesToStringRaw(const InValue: TBytes): RawByteString; 59 | begin 60 | SetString(Result, PAnsiChar(Pointer(InValue)), length(InValue)); 61 | end; 62 | 63 | function RawByteStringToBytes(const InValue: RawByteString): TBytes; 64 | begin 65 | Result := []; 66 | SetLength(Result, Length(InValue)); 67 | Move(InValue[1], Result[0], Length(InValue)); 68 | end; 69 | 70 | function ToBytes(const InValue: Int64): TBytes; 71 | begin 72 | Result := []; 73 | SetLength(Result, SizeOf(Int64)); 74 | Move(InValue, Result[0], SizeOf(Int64)); 75 | end; 76 | {$ENDIF} 77 | 78 | /// 79 | /// Sign the Buffer with the given Key 80 | /// 81 | function HMACSHA1(const _Key: OTPBytes; const Buffer: OTPBytes): OTPBytes; 82 | begin 83 | {$IFNDEF FPC} 84 | with TIdHMACSHA1.Create do 85 | begin 86 | Key := _Key; 87 | Result := HashValue(Buffer); 88 | Free; 89 | end; 90 | {$ELSE} 91 | Result := HMAC.HMACSHA1Digest(BytesToStringRaw(_Key), BytesToStringRaw(Buffer)); 92 | {$IFEND} 93 | end; 94 | 95 | /// 96 | /// Reverses TIdBytes (from low->high to high->low) 97 | /// 98 | function ReverseIdBytes(const inBytes: OTPBytes): OTPBytes; 99 | var 100 | i: Integer; 101 | begin 102 | {$IFDEF FPC}Result := [];{$IFEND} 103 | SetLength(Result, Length(inBytes)); 104 | for i := Low(inBytes) to High(inBytes) do 105 | Result[High(inBytes) - i] := inBytes[i]; 106 | end; 107 | 108 | /// 109 | /// My own ToBytes function. Something in the original one isn't working as expected. 110 | /// 111 | function StrToIdBytes(const inString: String): OTPBytes; 112 | var 113 | ch: Char; 114 | i: Integer; 115 | begin 116 | {$IFDEF FPC}Result := [];{$ENDIF} 117 | SetLength(Result, Length(inString)); 118 | 119 | i := 0; 120 | for ch in inString do 121 | begin 122 | Result[i] := Ord(ch); 123 | inc(i); 124 | end; 125 | end; 126 | 127 | function CalculateOTP(const Secret: String; const Counter: Integer = -1): Integer; 128 | var 129 | BinSecret: String; 130 | Hash: String; 131 | Offset: Integer; 132 | Part1, Part2, Part3, Part4: Integer; 133 | Key: Integer; 134 | Time: Integer; 135 | begin 136 | 137 | if Counter <> -1 then 138 | Time := Counter 139 | else 140 | Time := DateTimeToUnix(Now, False) div keyRegeneration; 141 | 142 | BinSecret := Base32.Decode(Secret); 143 | Hash := BytesToStringRaw(HMACSHA1(StrToIdBytes(BinSecret), ReverseIdBytes(ToBytes(Int64(Time))))); 144 | 145 | Offset := (ord(Hash[20]) AND $0F) + 1; 146 | Part1 := (ord(Hash[Offset+0]) AND $7F); 147 | Part2 := (ord(Hash[Offset+1]) AND $FF); 148 | Part3 := (ord(Hash[Offset+2]) AND $FF); 149 | Part4 := (ord(Hash[Offset+3]) AND $FF); 150 | 151 | Key := (Part1 shl 24) OR (Part2 shl 16) OR (Part3 shl 8) OR (Part4); 152 | Result := Key mod Trunc(IntPower(10, otpLength)); 153 | end; 154 | 155 | function ValidateTOPT(const Secret: String; const Token: Integer; const WindowSize: Integer = 4): Boolean; 156 | var 157 | TimeStamp: Integer; 158 | TestValue: Integer; 159 | begin 160 | Result := false; 161 | 162 | TimeStamp := DateTimeToUnix(Now, False) div keyRegeneration; 163 | for TestValue := Timestamp - WindowSize to TimeStamp + WindowSize do 164 | begin 165 | if (CalculateOTP(Secret, TestValue) = Token) then 166 | Result := true; 167 | end; 168 | end; 169 | 170 | function GenerateOTPSecret(len: Integer = -1): String; 171 | var 172 | i : integer; 173 | ValCharLen : integer; 174 | begin 175 | Result := ''; 176 | ValCharLen := Length(Base32U.ValidChars); 177 | 178 | if (len < 1) then 179 | len := SecretLengthDef; 180 | 181 | for i := 1 to len do 182 | begin 183 | Result := Result + copy(Base32U.ValidChars, Random(ValCharLen) + 1, 1); 184 | end; 185 | end; 186 | 187 | end. 188 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bernhard Wendel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OTP for Delphi 2 | ================ 3 | 4 | Google Authenticator is based on [RFC 4226](http://www.ietf.org/rfc/rfc4226.txt) - a Time based One Time Password (TOTP) which is initialised using a 16 digit base 32 ([RFC 4648](http://tools.ietf.org/html/rfc4648)) encoded seed value. Initial seeds used for the TOTP can be entered into the Google Authenticator via a camera using QR codes or via the keyboard. 5 | 6 | This Repository contains the working functions and all dependencies to either create a token generator or to validate tokens. 7 | 8 | Base32-Unit 9 | ----------- 10 | 11 | The code in previous versions of the `Base32U.pas` was not even close to being unicode ready. If you found this repository while looking for a working Base32 Implementation in Delphi, please have a look at [SZCodeBaseX](http://torry.net/authorsmore.php?id=5726). To have that one working in Delphi XE2 and higher, you will have to replace all `String`-Arguments with `AnsiString` ones. 12 | 13 | 14 | How to generate Time-based OTP using the code provided? 15 | ------------------------------------------------------- 16 | _MYBASE32SECRET_ is the pre-shared secret. 17 | 18 | ```Pascal 19 | uses 20 | GoogleOTP; 21 | 22 | [...] 23 | 24 | var 25 | Token: Integer; 26 | begin 27 | // Using time-based Token 28 | Token := CalculateOTP('MYBASE32SECRET'); // Returns the Token as Integer; 29 | 30 | // To display the token, use the Format-Function 31 | ShowMessage(Format('%.6d', [Token])); 32 | end; 33 | ``` 34 | 35 | How to generate Counter-based OTP using the code provided? 36 | ------------------------------------------------------- 37 | _MYBASE32SECRET_ is the pre-shared secret. 38 | _4_ is an example value from the counter 39 | 40 | ```Pascal 41 | uses 42 | GoogleOTP; 43 | 44 | [...] 45 | 46 | var 47 | Token: Integer; 48 | begin 49 | // Using counter-based Token 50 | Token := CalculateOTP('MYBASE32SECRET', 4); // Returns the Token as Integer; 51 | 52 | // To display the token, use the Format-Function 53 | ShowMessage(Format('%.6d', [Token])); 54 | end; 55 | ``` 56 | 57 | How to validate Time-based OTP using the code provided? 58 | ------------------------------------------------------- 59 | 60 | ```Pascal 61 | uses 62 | GoogleOTP; 63 | 64 | [...] 65 | 66 | var 67 | Token: Integer; 68 | begin 69 | // Ask the user for a token 70 | Token := 123456; 71 | 72 | // Check the Token 73 | if (ValidateTOPT('MYBASE32SECRET', Token)) then 74 | ShowMessage('Access granted') 75 | else 76 | ShowMessage('Access denied'); 77 | end; 78 | ``` 79 | 80 | License 81 | ------- 82 | 83 | This repository is licensed under the MIT license. 84 | --------------------------------------------------------------------------------