├── .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 |
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 |
--------------------------------------------------------------------------------