Upgrading PyOTP 1 to 2
Breaking1.4.2 → 2.10.0Breaking2Security2Platform2New9Changed6Fixed16Other10
BreakingUpgrade verdict
2 breaking changes and 2 platform requirement changes and 2 security fixes.
In plain terms
- Secrets shorter than 26 characters are no longer accepted, and newly generated secrets are longer by default.
- Three methods that generate one-time passwords now give you text instead of a number, so code that does math or comparisons on the result needs updating.
- This version no longer works on Python 3.6, but it now works on Python 3.11.
- The code works on Python 2.6 again, but the project still does not officially support that version.
Must act Top 5 of 6 that need code or config changes
Requires Python >=3.8 from 2.10.0 (none stated before).
What you must change
Breaking2
Raises the default and minimum base32 secret length to 26 characters.
The pyotp.HOTP.at(), pyotp.TOTP.at(), and pyotp.TOTP.now() methods now return strings instead of integers.
pyotp.HOTP.at()pyotp.TOTP.at()pyotp.TOTP.now()Security2
Modifies OTP generation to run in constant time.
Fixes a timing attack by no longer revealing string length to an attacker.
Platform2
Drops Python 3.6 support and adds Python 3.11 support.
Restores Python 2.6 compatibility, though Python 2.6 remains unsupported.
API changes
Generated by DepDelta from published Python source. Not mixed with prose notes.
- removed:
export utils - removed:
variable VERSION - changed:
function random_base32(length=…, random=…, chars=…) → function random_base32(length: int=…, chars: Sequence[str]=…) -> str - added:
export contrib - added:
export random - added:
function parse_uri(uri: str) -> OTP - added:
function random_hex(length: int=…, chars: Sequence[str]=…) -> str
- added:
variable random
- added:
export Steam
- added:
class Steam(TOTP) - added:
constructor Steam(s: str, name: Optional[str]=…, issuer: Optional[str]=…, interval: int=…, digits: int=…) -> None - added:
method Steam.generate_otp(input: int) -> str - added:
variable STEAM_CHARS - added:
variable STEAM_DEFAULT_DIGITS
- changed:
method HOTP.at(count) → method HOTP.at(count: int) -> str - changed:
method HOTP.provisioning_uri(name, initial_count=…, issuer_name=…) → method HOTP.provisioning_uri(name: Optional[str]=…, initial_count: Optional[int]=…, issuer_name: Optional[str]=…, **kwargs) -> str - changed:
method HOTP.verify(otp, counter) → method HOTP.verify(otp: str, counter: int) -> bool - added:
constructor HOTP(s: str, digits: int=…, digest: Any=…, name: Optional[str]=…, issuer: Optional[str]=…, initial_count: int=…) -> None
- changed:
constructor OTP(s, digits=…, digest=…) → constructor OTP(s: str, digits: int=…, digest: Any=…, name: Optional[str]=…, issuer: Optional[str]=…) -> None - changed:
method OTP.byte_secret() → method OTP.byte_secret() -> bytes - changed:
method OTP.generate_otp(input) → method OTP.generate_otp(input: int) -> str - changed:
static method OTP.int_to_bytestring(i, padding=…) → static method OTP.int_to_bytestring(i: int, padding: int=…) -> bytes
- changed:
constructor TOTP(*args, **kwargs) → constructor TOTP(s: str, digits: int=…, digest: Any=…, name: Optional[str]=…, issuer: Optional[str]=…, interval: int=…) -> None - changed:
method TOTP.at(for_time) → method TOTP.at(for_time: Union[int, datetime.datetime], counter_offset: int=…) -> str - changed:
method TOTP.now() → method TOTP.now() -> str - changed:
method TOTP.provisioning_uri(name, issuer_name=…) → method TOTP.provisioning_uri(name: Optional[str]=…, issuer_name: Optional[str]=…, **kwargs) -> str - changed:
method TOTP.timecode(for_time) → method TOTP.timecode(for_time: datetime.datetime) -> int - changed:
method TOTP.verify(otp, for_time=…) → method TOTP.verify(otp: str, for_time: Optional[datetime.datetime]=…, valid_window: int=…) -> bool
- changed:
function build_uri(secret, name, initial_count=…, issuer_name=…) → function build_uri(secret: str, name: str, initial_count: Optional[int]=…, issuer: Optional[str]=…, algorithm: Optional[str]=…, digits: Optional[int]=…, period: Optional[int]=…, **kwargs) -> str - changed:
function strings_equal(s1, s2) → function strings_equal(s1: str, s2: str) -> bool