pylessard / python-udsoncan

Python implementation of UDS (ISO-14229) standard.
MIT License
575 stars 199 forks source link

Issue with SecurityAccess #145

Closed pompushko closed 1 year ago

pompushko commented 1 year ago

Hello. I have strange behavior with SecurityAccess. Where is seed in response?

My code is simple.

config = dict(udsoncan.configs.default_client_config)
config['exception_on_negative_response'] = False
config['exception_on_timeout']= False
config['p2_timeout']= 2.0
config['p2_star_timeout']= 2.0
config['use_server_timing']= False

tpsock = isotp.socket(timeout=0.1)
ecu_address = isotp.Address(isotp.AddressingMode.Normal_29bits, txid=0x18DA87F1, rxid=0x18DAF187) 

conn = IsoTPSocketConnection('can0', rxid=0x18DAF110, txid=0x18DA10F1, tpsock=tpsock, address=ecu_address)

with Client(conn,  request_timeout=5, config=config) as client:
    result = client.change_session(3)

    try:
        req = Request(SecurityAccess, subfunction=5)
        conn.send(req.get_payload())

        payload = conn.wait_frame(timeout=1)
        response = Response.from_payload(payload)
        print(response)

        if response.service == SecurityAccess and response.code == Response.Code.PositiveResponse: 
          print("sending key now...")
          seedint = int.from_bytes(response.data, "big")
          key = get_key_by_seed(seedint)

          print("SEED " + hex(seedint))
          print("KEY "  + hex(key))

          keybytes = key.to_bytes(4, byteorder="big")

          req = Request(SecurityAccess, subfunction=6, data=keybytes)
          conn.send(req.get_payload())

          payload = conn.wait_frame(timeout=1)
          response = Response.from_payload(payload)

udsoncan1 udsoncan2

pylessard commented 1 year ago

1st byte is an echo of the security access, 4 last bytes is the seed. SecurityAccess.interpret_response will extract it and put it in response.service_data, check the doc https://udsoncan.readthedocs.io/en/latest/udsoncan/services.html#udsoncan.services.SecurityAccess.interpret_response

Also, do you know that the client can unlock a session by itself? You just need to provide your algorithm. Check the doc again

https://udsoncan.readthedocs.io/en/latest/udsoncan/services.html#udsoncan.services.SecurityAccess.interpret_response

pompushko commented 1 year ago

1st byte is an echo of the security access, 4 last bytes is the seed. SecurityAccess.interpret_response will extract it and put it in response.response_data, check the doc https://udsoncan.readthedocs.io/en/latest/udsoncan/services.html#udsoncan.services.SecurityAccess.interpret_response

Thank you for a fast response :) But I still cannot understand, how to make integer from that bytes.. Could you, please, help me? seedint = int.from_bytes(response.data, "big")

pylessard commented 1 year ago

I'm pretty sure you haven't looked much at the link I gave you :)

So what you are trying to do is : int.from_bytes(response.data[1:4]) this takes bytes 1,2,3,4, skipping bytes #0. But this method is really not the best approach. The library can do that for you.

A cleaner approach would be:

SecurityAccess.interpret_response(response, SecrutiyAccess.Mode.RequestSeed)
int.from_bytes(response.service_data.seed)

But even that is unnecessary work. You could replace most of your code by something like this:


def myalgo(level, seed, params):
    # level = 5 in this case. Params=None because security_algo_params is unset
    seedint = int.from_bytes(seed, 'big')
    key = get_key_by_seed(seedint)
    keybytes = key.to_bytes(4, byteorder="big")
    return keybytes

client.set_config('security_algo', myalgo)
client.unlock_security_access(5)   # Everything happens here. Exception in case of failure.
pompushko commented 1 year ago

Yeah, I put response.data[1:] it start works fine. Thank you.