From 64a135ad06a93a06b5729d16225b56b8c446025d Mon Sep 17 00:00:00 2001 From: Mark Percival Date: Wed, 11 Jun 2025 20:52:46 -0700 Subject: [PATCH 1/7] Fix: cleanup CLI --- lib/rotp/cli.rb | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/lib/rotp/cli.rb b/lib/rotp/cli.rb index 834bb07..175227f 100644 --- a/lib/rotp/cli.rb +++ b/lib/rotp/cli.rb @@ -16,25 +16,17 @@ def run # :nocov: def errors - if %i[time hmac].include?(options.mode) - if options.secret.to_s == '' - red 'You must also specify a --secret. Try --help for help.' - elsif options.secret.to_s.chars.any? { |c| ROTP::Base32::CHARS.index(c.upcase).nil? } - red 'Secret must be in RFC4648 Base32 format - http://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet' - end + return unless needs_secret? + + if blank_secret? + red 'You must also specify a --secret. Try --help for help.' + elsif invalid_secret? + red 'Secret must be in RFC4648 Base32 format - http://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet' end end def output - return options.warnings if options.warnings - return errors if errors - return arguments.to_s if options.mode == :help - - if options.mode == :time - ROTP::TOTP.new(options.secret, options.to_h).now - elsif options.mode == :hmac - ROTP::HOTP.new(options.secret, options.to_h).at options.counter - end + options.warnings || errors || help_message || otp_value end def arguments @@ -48,5 +40,32 @@ def options def red(string) "\033[31m#{string}\033[0m" end + + private + + def help_message + arguments.to_s if options.mode == :help + end + + def otp_value + case options.mode + when :time + ROTP::TOTP.new(options.secret, options.to_h).now + when :hmac + ROTP::HOTP.new(options.secret, options.to_h).at(options.counter) + end + end + + def needs_secret? + %i[time hmac].include?(options.mode) + end + + def blank_secret? + options.secret.to_s.empty? + end + + def invalid_secret? + options.secret.to_s.chars.any? { |c| ROTP::Base32::CHARS.index(c.upcase).nil? } + end end end From 6fc3b0eb8fcd1f7e13474426ec8921a9785b2804 Mon Sep 17 00:00:00 2001 From: Mark Percival Date: Wed, 11 Jun 2025 20:58:38 -0700 Subject: [PATCH 2/7] Fix: Feedback from Jazzberry --- lib/rotp/cli.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/rotp/cli.rb b/lib/rotp/cli.rb index 175227f..2f76437 100644 --- a/lib/rotp/cli.rb +++ b/lib/rotp/cli.rb @@ -26,7 +26,8 @@ def errors end def output - options.warnings || errors || help_message || otp_value + return help_message if options.mode == :help + options.warnings || errors || otp_value end def arguments @@ -44,7 +45,7 @@ def red(string) private def help_message - arguments.to_s if options.mode == :help + arguments.to_s end def otp_value From 76673dd4641dde378bf60384127eaabcb8eb4f8f Mon Sep 17 00:00:00 2001 From: Mark Percival Date: Wed, 11 Jun 2025 21:05:31 -0700 Subject: [PATCH 3/7] Fix: Feedback from Jazzberry --- lib/rotp/cli.rb | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/rotp/cli.rb b/lib/rotp/cli.rb index 2f76437..eec7872 100644 --- a/lib/rotp/cli.rb +++ b/lib/rotp/cli.rb @@ -16,18 +16,17 @@ def run # :nocov: def errors - return unless needs_secret? + if requires_secret? && blank_secret? + return red 'You must also specify a --secret. Try --help for help.' + end - if blank_secret? - red 'You must also specify a --secret. Try --help for help.' - elsif invalid_secret? - red 'Secret must be in RFC4648 Base32 format - http://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet' + if secret_provided? && invalid_secret? + return red 'Secret must be in RFC4648 Base32 format - http://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet' end end def output - return help_message if options.mode == :help - options.warnings || errors || otp_value + options.warnings || errors || (help_message if options.mode == :help) || otp_value end def arguments @@ -57,10 +56,14 @@ def otp_value end end - def needs_secret? + def requires_secret? %i[time hmac].include?(options.mode) end + def secret_provided? + !options.secret.to_s.empty? + end + def blank_secret? options.secret.to_s.empty? end From f83a59675d380e4a2b20a49dcb46172f8da0c319 Mon Sep 17 00:00:00 2001 From: Mark Percival Date: Wed, 11 Jun 2025 21:15:23 -0700 Subject: [PATCH 4/7] Fix: Feedback from Jazzberry --- lib/rotp/cli.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rotp/cli.rb b/lib/rotp/cli.rb index eec7872..b77456f 100644 --- a/lib/rotp/cli.rb +++ b/lib/rotp/cli.rb @@ -26,7 +26,7 @@ def errors end def output - options.warnings || errors || (help_message if options.mode == :help) || otp_value + errors || options.warnings || (help_message if options.mode == :help) || otp_value end def arguments From ae49e9cc96ec9017780acf1a36551cb9ddffbcc1 Mon Sep 17 00:00:00 2001 From: Mark Percival Date: Wed, 11 Jun 2025 21:21:00 -0700 Subject: [PATCH 5/7] Fix: Feedback from Jazzberry --- lib/rotp/cli.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/rotp/cli.rb b/lib/rotp/cli.rb index b77456f..9bca21e 100644 --- a/lib/rotp/cli.rb +++ b/lib/rotp/cli.rb @@ -1,4 +1,5 @@ require 'rotp/arguments' +require 'rotp/base32' module ROTP class CLI @@ -69,7 +70,10 @@ def blank_secret? end def invalid_secret? - options.secret.to_s.chars.any? { |c| ROTP::Base32::CHARS.index(c.upcase).nil? } + ROTP::Base32.decode(options.secret) + false + rescue ROTP::Base32::Base32Error + true end end end From b6358c99c226b0510341f3ea2e684f0c3b289680 Mon Sep 17 00:00:00 2001 From: Mark Percival Date: Wed, 11 Jun 2025 21:32:46 -0700 Subject: [PATCH 6/7] Fix: Feedback from Jazzberry --- lib/rotp/cli.rb | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/rotp/cli.rb b/lib/rotp/cli.rb index 9bca21e..4ea5f72 100644 --- a/lib/rotp/cli.rb +++ b/lib/rotp/cli.rb @@ -21,8 +21,13 @@ def errors return red 'You must also specify a --secret. Try --help for help.' end - if secret_provided? && invalid_secret? - return red 'Secret must be in RFC4648 Base32 format - http://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet' + if secret_provided? + if invalid_secret? + return red 'Secret must be in RFC4648 Base32 format - http://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet' + end + if decoded_secret.length < 20 + return red 'Secret must be at least 160 bits (32 characters in Base32)' + end end end @@ -70,10 +75,14 @@ def blank_secret? end def invalid_secret? - ROTP::Base32.decode(options.secret) + decoded_secret false rescue ROTP::Base32::Base32Error true end + + def decoded_secret + @decoded_secret ||= ROTP::Base32.decode(options.secret) + end end end From ce709a14247733aacc05a32ef16d021c5af5554f Mon Sep 17 00:00:00 2001 From: Mark Percival Date: Wed, 11 Jun 2025 21:36:48 -0700 Subject: [PATCH 7/7] Fix: Feedback from Jazzberry --- lib/rotp/cli.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rotp/cli.rb b/lib/rotp/cli.rb index 4ea5f72..bda1115 100644 --- a/lib/rotp/cli.rb +++ b/lib/rotp/cli.rb @@ -25,7 +25,7 @@ def errors if invalid_secret? return red 'Secret must be in RFC4648 Base32 format - http://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet' end - if decoded_secret.length < 20 + if options.secret.tr('=', '').length < 32 return red 'Secret must be at least 160 bits (32 characters in Base32)' end end