Posted by virantha on Tue 12 January 2016

Reverse engineering the remote control protocol for the Nissan LEAF (the new Nissan EV protocol)

1   Nissan Connect EV

Note

Update April 2016: Nissan has updated their API calls to actually use the user login information to authenticate instead of just the VIN. You can see a summary of the newer API in my new post here.

For those of you with a Nissan LEAF, the first cool things you get for free with the car is remote telematics via a web interface or a iOS or Android application. The car has a built-in cellular modem that can be used to get all kinds of neat stats from the car (like miles driven, energy consumption, efficiency, etc), as well as send map directions and enable/disable the charging and the climate control. In the latter part of 2015, Nissan revamped their Carwings servers which provided these services, and they appear to be combining it with their general telematics interfaces across all their car models. The new application now talks to a different REST endpoint and I could find scant documentation on this, so I decided to try my hand at reverse-engineering what the new Nissan EV application is doing.

2   Older API

The following pages and threads have an outdated API:

Pycarwings actually works, but uses the original API. The forum thread then details an updated REST endpoint (v3) but I could never get the authenticate to take (possibly, I was using the wrong type of headers).

So the next step was to actually sniff the requests coming out of the Nissan EV Connect app using a man-in-the-middle-proxy.

3   First steps to analyzing the Nissan EV network traffic

3.1   Installing the proxy to view the Nissan EV iOS app headers

I followed the instructions on using mitmproxy here.

Of course, just doing:

pip install mitmproxy

unfortunately did not work on my mac running El Capitan, so I had to diagnose the following errors:

  • lxml compilation failed, so I realized I had to reinstall the xcode command line tools after upgrading to El Capitan by doing xcode-select --install
  • Then, it couldn't find the openssl headers to install the crytography package. So I had to read up on how El Capitan had deprecated the standard openssl headers. tldr; run env LDFLAGS="-L$(brew --prefix openssl)/lib" CFLAGS="-I$(brew --prefix openssl)/include" pip install mitmproxy

Then, it was a matter of running mitmproxy and setting my iOS device to use a manual proxy on port 8080 pointing to my mac. Then, you browse to the mitm.it using safari and install the appropriate certificate for your device. Then I launched the Nissan EV application and started looking at the traffic. (note to self, in order to copy text from the mitmproxy screen, you need to hold fn+option down as you select)

3.2   REST requests

The request/responses appear to be pretty straight-forward. On my initial reading, curiously, it seems like you just need the constant DCMID and VIN fields from the login for subsequent requests. There doesn't appear to be any session based authentication token used.

3.2.1   Initial login to Nissan servers

Initial logon request, where USERID is your Nissan Portal login (email address) and USERPASSWORD is the accompanying password:

https://gdcportalgw.its-mo.com/orchestration_1111/gdc/UserLoginRequest.php?RegionCode=NNA&lg=en-US&DCMID=&VIN=&tz=&UserId=USERID&Password=USERPASSWORD

Results in:

{
        "CustomerInfo": {
                "Country": "US",
                "Language": "en-US",
                "Nickname": "XXXXXXXXX",
                "OwnerId": "XXXXXXXXXX",
                "RegionCode": "NNA",
                "Timezone": "America/New_York",
                "UserId": "XXXXXX",
                "UserVehicleBoundDurationSec": "946771200",
                "VehicleImage": "/content/language/default/ima
                "VehicleInfo": {
                        "DCMID": "XXXXXXXXXXXX",
                        "EncryptedNAVIID": "XXXXXXXXXXXXXXXXXXXXXX
                        "LastDCMUseTime": "Jan 12, 2016 03:37 PM",
                        "LastVehicleLoginTime": "",
                        "MSN": "XXXXXXXXXXXXXXX",
                        "NAVIID": "XXXXXXXXXXXX",
                        "SIMID": "XXXXXXXXXXXXXXXXXXX",
                        "UserVehicleBoundTime": "2015-09-24T18:10:
                        "VIN": "XXXXXXXXXXXXXXXXX"
                }
        },
        "EncAuthToken": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        "UserInfoRevisionNo": "1",
        "VehicleInfoList": {
                "VehicleInfo": [
                        {
                                "charger20066": "false",
                                "nickname": "VLeaf",
                                "telematicsEnabled": "true",
                                "vin": "XXXXXXXXXXXXXXXXX"
                        }
                ],
                "vehicleInfo": [
                        {
                                "charger20066": "false",
                                "nickname": "VLeaf",
                                "telematicsEnabled": "true",
                                "vin": "XXXXXXXXXXXXXXXXX"
                        }
                ]
        },
        "message": "success",
        "sessionId": "43978f5b-e45c-498c-a9e5-be85e59df3dc",
        "status": 200,
        "vehicle": {
                "profile": {
                        "dcmId": "XXXXXXXXXXXX",
                        "encAuthToken": "XXXXXXXXXXXXXXXXXXXXXXXXX",
                        "gdcPassword": "XXXXXXXX",
                        "gdcUserId": "XXXXXXXX",
                        "nickname": "XXXXXXXXX",
                        "status": "ACCEPTED",
                        "statusDate": "Sep 24, 2015 12:00 AM",
                        "vin": "XXXXXXXXXXXXXXXXX"
                }
        }
}

3.2.2   Request current status

Request an update on the battery status:

https://gdcportalgw.its-mo.com/orchestration_1111/gdc/BatteryStatusRecordsRequest.php?RegionCode=NNA&lg=en-US&DCMID=XXXXXXXXXXXX&VIN=XXXXXXXXXXXXXXXXX&tz=America/New_York&TimeFrom=2015-09-24T18:10:06

Response:

{
        "BatteryStatusRecords": {
                "BatteryStatus": {
                        "BatteryCapacity": "12",
                        "BatteryChargingStatus": "NOT_CHARGING",
                        "BatteryRemainingAmount": "11",
                        "BatteryRemainingAmountWH": "",
                        "BatteryRemainingAmountkWH": ""
                },
                "CruisingRangeAcOff": "133840.0",
                "CruisingRangeAcOn": "122368.0",
                "NotificationDateAndTime": "2016/01/12 15:37",
                "OperationDateAndTime": "Jan 12, 2016 10:37 AM",
                "OperationResult": "START",
                "PluginState": "NOT_CONNECTED",
                "TargetDate": "2016/01/12 15:37",
                "TimeRequiredToFull": {
                        "HourRequiredToFull": "3",
                        "MinutesRequiredToFull": "30"
                },
                "TimeRequiredToFull200": {
                        "HourRequiredToFull": "2",
                        "MinutesRequiredToFull": "0"
                },
                "TimeRequiredToFull200_6kW": {
                        "HourRequiredToFull": "2",
                        "MinutesRequiredToFull": "0"
                }
        },
        "message": "success",
        "status": 200
}

3.2.3   Update status

Ask for an update from the car:

https://gdcportalgw.its-mo.com/orchestration_1111/gdc/BatteryStatusCheckRequest.php?RegionCode=NNA&lg=en-US&DCMID=XXXXXXXXXXXX&VIN=XXXXXXXXXXXXXXXXX&tz=America/New_York
{ "status":200,"message":"success","userId":"XXXXXX","vin":"XXXXXXXXX","resultKey":"XXXXXXXXXX"}%

The resultKey can be used to poll for when the updated value is available:

https://gdcportalgw.its-mo.com/orchestration_1111/gdc/BatteryStatusCheckResultRequest.php?RegionCode=NNA&lg=en-US&DCMID=XXXXXXXXXXXX&VIN=XXXXXXXXXXXXXXXXX&tz=America/New_York&resultKey=XXXXXXX

If it's not ready yet:

{"status":200,"message":"success","responseFlag":"0"}

Once status is updated, you get:

{
    "status":200,
    "message":"success",
    "responseFlag":"1",
    "operationResult":"START",
    "timeStamp":"2016-02-20 20:29:33",
    "cruisingRangeAcOn":"129712.0",
    "cruisingRangeAcOff":"133584.0",
    "currentChargeLevel":"0",
    "chargeMode":"NOT_CHARGING",
    "pluginState":"NOT_CONNECTED",
    "charging":"NO",
    "chargeStatus":"CT",
    "batteryDegradation":"11",
    "batteryCapacity":"12",
    "timeRequiredToFull":{"hours":"","minutes":""},
    "timeRequiredToFull200":{"hours":"","minutes":""},
    "timeRequiredToFull200_6kW":{"hours":"","minutes":""}
}

3.2.4   Get various formats

Here some misc requests:

4   to be continued....

© Virantha Ekanayake. Built using Pelican. Modified svbhack theme, based on theme by Carey Metcalfe