Challenge 3: Identification

One Time Passwords (OTPs) are passwords used once, that are either sent on a separate channel (e.g. as sms messages) or generated by dedicated devices and smartphone apps. 

Security property: OTP generators should guarantee that, even if some OTPs are leaked, the attacker won’t be able to forge next OTPs. In other words, secrecy of next OTPs should be preserved even if previous OTPs has been leaked. This property is what makes OTPs stronger than passwords: a password, once intercepted, can be easily reused. 

Your goal

The Acme Corporation wants to adopt a new OPT generator implemented in a smartphone app:

  1. the app asks the user to insert the secret PIN;
  2. the app uses the PIN to generate the OTP. Some time-variant parameter is used to make the OTP always different, for example, a timestamp;
  3. the user sends the OTP to the server;
  4. the server knows the user’s PIN and the time-variant parameter: it re-generates the OTP and checks that it is the same as the one sent by the user.

The algorithm is based on SHA-256 hash, a state-of-the-art secure one-way function. Acme Corporation engineers are so confident about the security of their scheme that they have published three consecutive OTPs generated starting from a secret 8 digits PIN and and they are challenging security experts to break the scheme.

OTP1 = 754104, generated on 8 April 2020 at 21:45
OTP2 = 353471, generated on 8 April 2020 at 21:46
OTP3 = 084633, generated on 8 April 2020 at 21:47

Can you find the secret 8 digits PIN and prove that Acme’s scheme is weak? Can you also suggest a fix? 

import time
import hashlib

def otp(pin):
  '''The Acme Corporation OTP generator.

  This generator is based on SHA256, a secure one-way hash so to prevent
  that the pin value is computed starting from the hash.
  Moreover the hash is cut to 6 (decimalized) bytes, which makes inversion 
  even harder!
  '''
  size = 6
  otp = ''

  # timestamp changes every minute to allow for synchronization
  t = time.localtime(time.time())
  timestamp = '{}-{}-{}-{}:{}'.format(t.tm_year,t.tm_mon,t.tm_mday,t.tm_hour,t.tm_min)

  # One-way function, to prevent computing PIN from OTP!
  h = hashlib.sha256((pin+timestamp).encode()).digest()

  for i in range(size):
    # decimalize i-th byte and add it to the otp
    digit = h[i] % 10 
    otp += str(digit)
  return otp

Bonus

Send me and @Simone Jovon on slack the script used to the attack with an accurate description of how you solved the challenge by 22 November 2023 to get a 0.5 bonus on the final grade! As usual, the report should illustrate the step you followed to solve the challenge and be pleasant to read  !

In bocca al lupo!  😁