pythondjangoqr-codehmacbankid

Swedish BankID Python Animated QR code generation with hmac


I'm developing a Django project that will use BankID for authorization and digitally sign. I am using pybankid, and I have nothing but nice things to say about that project. My problem lies with trying to use the code provided by bankIDs documentation.

QRCode Docs

import hashlib
import hmac
import time
 
qr_start_token = rp_response["qrStartToken"]
# "67df3917-fa0d-44e5-b327-edcc928297f8"
 
qr_start_secret = rp_response["qrStartSecret"]
# "d28db9a7-4cde-429e-a983-359be676944c"
 
order_time = time.time()
# (The time in seconds when the response from the BankID service was delivered)
 
qr_time = str(int(time.time() - order_time))
# ("0" or another string with a higher number depending on order_time and current time)
 
qr_auth_code = hmac.new(qr_start_secret, qr_time, hashlib.sha256).hexdigest()
# "dc69358e712458a66a7525beef148ae8526b1c71610eff2c16cdffb4cdac9bf8" (qr_time="0")
# "949d559bf23403952a94d103e67743126381eda00f0b3cbddbf7c96b1adcbce2" (qr_time="1")
# "a9e5ec59cb4eee4ef4117150abc58fad7a85439a6a96ccbecc3668b41795b3f3" (qr_time="2")
# (64 chars hex)
 
qr_data = str.join(".", "bankid", qr_start_token, qr_time, qr_auth_code)
# "bankid.67df3917-fa0d-44e5-b327-edcc928297f8.0.dc69358e712458a66a7525beef148ae8526b1c71610eff2c16cdffb4cdac9bf8" (qr_time="0")
# "bankid.67df3917-fa0d-44e5-b327-edcc928297f8.1.949d559bf23403952a94d103e67743126381eda00f0b3cbddbf7c96b1adcbce2" (qr_time="1")
# "bankid.67df3917-fa0d-44e5-b327-edcc928297f8.2.a9e5ec59cb4eee4ef4117150abc58fad7a85439a6a96ccbecc3668b41795b3f3" (qr_time="2")

I get TypeError: key: expected bytes or bytearray, but got 'str', when I try to convert qr_start_secret to bytes I get Unicode-objects must be encoded before hashing. I'm at a loss. Does anyone have any ideas?

EDIT: Here is my current code, and it works. Currently struggling with how to render a constantly changing QR code value client side, as context will only transfer static values.

if request.META['HTTP_USER_AGENT']:
 ua_string = request.META['HTTP_USER_AGENT']
 user_agent = parse(ua_string)
 if user_agent.is_pc:
                        
  status=client.collect(order_ref=auth["orderRef"])["status"]
                        order_time = time.time()
                        while status == "pending":

  qr_start_token = auth["qrStartToken"]

  qr_start_secret = auth["qrStartSecret"]

  qr_time = str(int(time.time() - order_time))

  qr_auth_code = hmac.new(qr_start_secret.encode(), qr_time.encode(), hashlib.sha256).hexdigest()

  qr_data = ".".join(["bankid", qr_start_token, qr_time, qr_auth_code])

  print(f'qr_data: {qr_data}')

                            
  status=client.collect(order_ref=auth["orderRef"])["status"]

  print(status)

  qr = segno.make(qr_data)
  qr.save('media/img/temp/' + personal_number + '.svg')

  if status == "complete":
   print("Logged on")
   dj_login(request, user)
   return render(request, 'home/auth-login-Success.html')

  time.sleep(1)

Solution

  • There are multiple issues with their sample code

    1. It looks like qr_start_token and qr_start_secret are strings.
    2. str.join will raise error for receiving 4 arguments

    Try:

    import hashlib
    import hmac
    import time
     
    qr_start_token = "67df3917-fa0d-44e5-b327-edcc928297f8"
     
    qr_start_secret = "d28db9a7-4cde-429e-a983-359be676944c"
     
    order_time = time.time()
    # (The time in seconds when the response from the BankID service was delivered)
     
    qr_time = str(int(time.time() - order_time))
    # ("0" or another string with a higher number depending on order_time and current time)
    print(f'qr_time: {qr_time}')
    qr_auth_code = hmac.new(qr_start_secret.encode(), qr_time.encode(), hashlib.sha256).hexdigest()
    # "dc69358e712458a66a7525beef148ae8526b1c71610eff2c16cdffb4cdac9bf8" (qr_time="0")
    # "949d559bf23403952a94d103e67743126381eda00f0b3cbddbf7c96b1adcbce2" (qr_time="1")
    # "a9e5ec59cb4eee4ef4117150abc58fad7a85439a6a96ccbecc3668b41795b3f3" (qr_time="2")
    # (64 chars hex)
    
    print(f'qr_auth_code: {qr_auth_code}')
    print(qr_auth_code == "dc69358e712458a66a7525beef148ae8526b1c71610eff2c16cdffb4cdac9bf8")
     
    qr_data = str.join(".", ["bankid", qr_start_token, qr_time, qr_auth_code])
    # or better
    # qr_data = ".".join(["bankid", qr_start_token, qr_time, qr_auth_code])
    
    # "bankid.67df3917-fa0d-44e5-b327-edcc928297f8.0.dc69358e712458a66a7525beef148ae8526b1c71610eff2c16cdffb4cdac9bf8" (qr_time="0")
    # "bankid.67df3917-fa0d-44e5-b327-edcc928297f8.1.949d559bf23403952a94d103e67743126381eda00f0b3cbddbf7c96b1adcbce2" (qr_time="1")
    # "bankid.67df3917-fa0d-44e5-b327-edcc928297f8.2.a9e5ec59cb4eee4ef4117150abc58fad7a85439a6a96ccbecc3668b41795b3f3" (qr_time="2")
    
    print(f'qr_data: {qr_data}')
    

    output:

    qr_time: 0
    qr_auth_code: dc69358e712458a66a7525beef148ae8526b1c71610eff2c16cdffb4cdac9bf8
    True
    qr_data: bankid.67df3917-fa0d-44e5-b327-edcc928297f8.0.dc69358e712458a66a7525beef148ae8526b1c71610eff2c16cdffb4cdac9bf8