Skip to content

Commit

Permalink
Bump 2FA secret bit length from 80 bits to 160 bits as recommended by…
Browse files Browse the repository at this point in the history
… RFC4226
  • Loading branch information
evilaliv3 committed May 20, 2021
1 parent a5303ac commit 4e9735e
Showing 1 changed file with 15 additions and 2 deletions.
17 changes: 15 additions & 2 deletions securedrop/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import base64
import os
import scrypt
import secrets
import string
import pyotp
import qrcode
# Using svg because it doesn't require additional dependencies
Expand Down Expand Up @@ -35,6 +37,17 @@
ARGON2_PARAMS = dict(memory_cost=2**16, rounds=4, parallelism=2)


def generate_otp_secret() -> str:
"""
Generate an OTP secret of 160 bits
For usability reasons the return charset is variation
of base32 that does not include any symbol in [O,0,I,1]
"""
symbols = list('ABCDEFGHJKLMNPQRSTUVWXYZ23456789')
return ''.join(secrets.choice(symbols) for i in range(32))


def get_one_or_else(query: Query,
logger: 'Logger',
failure_method: 'Callable[[int], None]') -> db.Model:
Expand Down Expand Up @@ -403,7 +416,7 @@ class Journalist(db.Model):
is_admin = Column(Boolean) # type: Column[Optional[bool]]
session_nonce = Column(Integer, nullable=False, default=0)

otp_secret = Column(String(16), default=pyotp.random_base32)
otp_secret = Column(String(32), default=generate_otp_secret)
is_totp = Column(Boolean, default=True) # type: Column[Optional[bool]]
hotp_counter = Column(Integer, default=0) # type: Column[Optional[int]]
last_token = Column(String(6))
Expand Down Expand Up @@ -566,7 +579,7 @@ def valid_password(self, passphrase: 'Optional[str]') -> bool:
return is_valid

def regenerate_totp_shared_secret(self) -> None:
self.otp_secret = pyotp.random_base32()
self.otp_secret = generate_otp_secret()

def set_hotp_secret(self, otp_secret: str) -> None:
self.otp_secret = base64.b32encode(
Expand Down

0 comments on commit 4e9735e

Please sign in to comment.