Skip to content

Commit

Permalink
Merge pull request radprogrammer#7 from radprogrammer/RFC_MinimumKeyL…
Browse files Browse the repository at this point in the history
…ength_optional

Close radprogrammer#5 Make RFC minimum key length optional
  • Loading branch information
darianmiller authored Jul 16, 2022
2 parents 852317a + a99331b commit 17022e7
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 9 deletions.
18 changes: 18 additions & 0 deletions Source/Tests/DUnit/radRTL.HOTP.Tests.pas
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ interface
type

THOTPTest = class(TTestCase)
public const
INVALID_KEY = 'KNEE6USU'; //'SHORT' base32 encoded
published
procedure TestRFCVectors;
procedure TestMinimumKeyLengthIgnoredByDefault;
procedure TestMinimumKeyLengthException;
end;


Expand Down Expand Up @@ -78,6 +82,20 @@ procedure THOTPTest.TestRFCVectors;
end;


procedure THOTPTest.TestMinimumKeyLengthIgnoredByDefault;
begin
THOTP.EnforceMinimumKeyLength := False;
THOTP.GeneratePassword(INVALID_KEY, 0); //should not generate an exception event though we are using a short key
end;

procedure THOTPTest.TestMinimumKeyLengthException;
begin
ExpectedException := EOTPException;
THOTP.EnforceMinimumKeyLength := True;
THOTP.GeneratePassword(INVALID_KEY, 0); //should toss EOTPException as key is less than 128-bits which is minimum required per RFC-4226
end;


initialization

RegisterTest(THOTPTest.Suite);
Expand Down
12 changes: 3 additions & 9 deletions Source/radRTL.HOTP.pas
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ interface

type

{$IFDEF STRICT_RFC_4226}
EOTPException = class(Exception);
{$ENDIF}


// "Password generated must be at least 6, but can be 7 or 8" digits in length (simply changes the MOD operation) (9-digit addition suggested in errata: https://www.rfc-editor.org/errata/eid2400)
Expand All @@ -23,20 +21,18 @@ THOTP = class
private const
ModTable: array [0 .. 2] of integer = (1000000, 10000000, 100000000); // 6,7,8 zeros matching OTP Length
FormatTable: array [0 .. 2] of string = ('%.6d', '%.7d', '%.8d'); // 6,7,8 string length (padded left with zeros)
{$IFDEF STRICT_RFC_4226}
RFCMinimumKeyLengthBytes = 16; // "The length of shared secret MUST be at least 128 bits. This document RECOMMENDs a shared secret length of 160 bits."
{$ENDIF}
public class var
EnforceMinimumKeyLength:Boolean;
public
/// <summary> HOTP: HMAC-Based One-Time Password Algorithm</summary>
class function GeneratePassword(const pBase32EncodedSecretKey:string; const pCounterValue:Int64; const pOutputLength:TOTPLength = TOTPLength.SixDigits):string; overload;
class function GeneratePassword(const pPlainTextSecretKey:TBytes; const pCounterValue:Int64; const pOutputLength:TOTPLength = TOTPLength.SixDigits):string; overload;
end;


{$IFDEF STRICT_RFC_4226}
resourcestring
sOTPKeyLengthTooShort = 'Key length must be at least 128bits';
{$ENDIF}


implementation
Expand Down Expand Up @@ -68,13 +64,11 @@ class function THOTP.GeneratePassword(const pPlainTextSecretKey:TBytes; const pC
vBinCode:integer;
vPinNumber:integer;
begin
{$IFDEF STRICT_RFC_4226}
if Length(pPlainTextSecretKey) < RFCMinimumKeyLengthBytes then
if EnforceMinimumKeyLength and (Length(pPlainTextSecretKey) < RFCMinimumKeyLengthBytes) then
begin
// RFC minimum length required (Note: did not see this limitation in other implementations)
raise EOTPException.CreateRes(@sOTPKeyLengthTooShort);
end;
{$ENDIF}
vData := ReverseByteArray(ConvertToByteArray(pCounterValue)); // RFC reference implmentation reversed order of CounterValue (movingFactor) bytes
vHMAC := THashSHA1.GetHMACAsBytes(vData, pPlainTextSecretKey); // SHA1 = 20 byte digest

Expand Down

0 comments on commit 17022e7

Please sign in to comment.