lib/rotp/totp.rb in rotp-3.3.1 vs lib/rotp/totp.rb in rotp-4.0.0
- old
+ new
@@ -12,68 +12,50 @@
super
end
# Accepts either a Unix timestamp integer or a Time object.
# Time objects will be adjusted to UTC automatically
- # @param [Time/Integer] time the time to generate an OTP for
- # @option [Boolean] padding (true) Issue the number as a 0 padded string
- def at(time, padding=true)
- unless time.class == Time
- time = Time.at(time.to_i)
- end
-
- generate_otp(timecode(time), padding)
+ # @param time [Time/Integer] the time to generate an OTP for, integer unix timestamp or Time object
+ def at(time)
+ generate_otp(timecode(time))
end
# Generate the current time OTP
# @return [Integer] the OTP as an integer
- def now(padding=true)
- generate_otp(timecode(Time.now), padding)
+ def now()
+ generate_otp(timecode(Time.now))
end
# Verifies the OTP passed in against the current time OTP
- # @param [String/Integer] otp the OTP to check against
- def verify(otp, time = Time.now)
- super(otp, self.at(time))
- end
-
- # Verifies the OTP passed in against the current time OTP
- # and adjacent intervals up to +drift+.
- # @param [String] otp the OTP to check against
- # @param [Integer] drift the number of seconds that the client
- # and server are allowed to drift apart
- def verify_with_drift(otp, drift, time = Time.now)
- time = time.to_i
- times = (time-drift..time+drift).step(interval).to_a
- times << time + drift if times.last < time + drift
- times.any? { |ti| verify(otp, ti) }
- end
-
- # Verifies the OTP passed in against the current time OTP
# and adjacent intervals up to +drift+. Excludes OTPs
- # from prior_time and earlier. Returns time value of
+ # from `after` and earlier. Returns time value of
# matching OTP code for use in subsequent call.
- # @param [String] otp the OTP to check against
- # @param [Integer] drift the number of seconds that the client
- # and server are allowed to drift apart
- # @param [Integer] time value of previous match
- def verify_with_drift_and_prior(otp, drift, prior_time = nil, time = Time.now)
- # calculate normalized bin start times based on drift
- first_bin = (time - drift).to_i / interval * interval
- last_bin = (time + drift).to_i / interval * interval
+ # @param otp [String] the one time password to verify
+ # @param drift_behind [Integer] how many seconds to look back
+ # @param drift_ahead [Integer] how many seconds to look ahead
+ # @param after [Integer] prevent token reuse, last login timestamp
+ # @param at [Time] time at which to generate and verify a particular
+ # otp. default Time.now
+ # @return [Integer, nil] the last successful timestamp
+ # interval
+ def verify(otp, drift_ahead: 0, drift_behind: 0, after: nil, at: Time.now)
+ timecodes = get_timecodes(at, drift_behind, drift_ahead)
- # if prior_time was supplied, adjust first bin if necessary to exclude it
- if prior_time
- prior_bin = prior_time.to_i / interval * interval
- first_bin = prior_bin + interval if prior_bin >= first_bin
- # fail if we've already used the last available OTP code
- return if first_bin > last_bin
+ if after
+ timecodes = timecodes.select { |t| t > timecode(after) }
end
- times = (first_bin..last_bin).step(interval).to_a
- times.find { |ti| verify(otp, ti) }
+
+ result = nil
+ timecodes.each { |t|
+ if (super(otp, self.generate_otp(t)))
+ result = t * interval
+ end
+ }
+ return result
end
+
# Returns the provisioning URI for the OTP
# This can then be encoded in a QR Code and used
# to provision the Google Authenticator app
# @param [String] name of the account
# @return [String] provisioning URI
@@ -93,11 +75,27 @@
encode_params("otpauth://totp/#{issuer_string}#{URI.encode(name)}", params)
end
private
+ # Get back an array of timecodes for a period
+ def get_timecodes(at, drift_behind, drift_ahead)
+ now = timeint(at)
+ timecode_start = timecode(now - drift_behind)
+ timecode_end = timecode(now + drift_ahead)
+ return (timecode_start..timecode_end).step(1).to_a
+ end
+
+ # Ensure UTC int
+ def timeint(time)
+ unless time.class == Time
+ return time.to_i
+ end
+ return time.utc.to_i
+ end
+
def timecode(time)
- time.utc.to_i / interval
+ return timeint(time) / interval
end
end
end