Posted by virantha on Thu 07 April 2016

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

Note

This is an update to my earlier post on the Nissan Leaf remote telematics. Nissan had taken their API offline in February 2016 to address the security vulnerabilities, and this post details how to connect to the new API that went into place in early April.

1   Nissan Connect EV

Most of this updated login information is courtesy of users on the MyNissanLeaf portal and specifically this post The new endpoints are now rooted at 'https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/' instead of 'https://gdcportalgw.its-mo.com/orchestration_1111/gdc/', and all accesses are now POST requests.

1.1   Initial login to Nissan servers

The initial login is now a two step process.

1.1.1   Request an encryption key

First, you need to call InitialApp.php with the parameter initial_app_string set to geORNtsZe5I4lRGjG9GZiA (taken from analyzing the iOS app traffic).

curl --data "RegionCode=NNA&lg=en-US&initial_app_strings=geORNtsZe5I4lRGjG9GZiA" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/InitialApp.php"

This returns an encryption key in the parameter baseprm

{"status":200,"message":"success","baseprm":"uyI5Dj9g8VCOFDnBRUbr3g"}

1.1.2   Initial logon request

Once you have the encryption key, you need to use it to encrypt the user's NissanConnect password using Blowfish ECB with PKCS5Padding. The encrypted key is then Base64 encoded, and then also urlencoded before being used in the login API request. Here's some sample Python code to show what needs to be done, assuming you have the pycrypto library installed.

from Crypto.Cipher import Blowfish
import base64

def pad_string(str):
    new_str = str
    pad_chars = 8-(len(str) % 8)
    if pad_chars != 0:
        for x in range(pad_chars):
            new_str += chr(pad_chars)
    return new_str

plaintext = "YOURPASSWORD"

crypt_obj = Blowfish.new('uyI5Dj9g8VCOFDnBRUbr3g', Blowfish.MODE_ECB)
ciphertext = crypt_obj.encrypt(pad_string(plaintext))

print "Plaintext: " + plaintext
print "Blowfish Cyphertext: " + ciphertext
print "Blowfish base64: " + base64.b64encode(ciphertext)

The base64 encoded string can then be used in the following API call:

curl --data "RegionCode=NNA&lg=en-US&initial_app_strings=geORNtsZe5I4lRGjG9GZiA&UserId=YOUREMAIL" --data-urlencode "Password=YOURBASE64PASSWORD" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/UserLoginRequest.php"

If successful, you will get back the following JSON, where custom_sessionid is what you'll be using for subsequent requests.

{
  "status": 200,
  "sessionId": "XXXXXXXX",
  "VehicleInfoList": {
        "VehicleInfo": [
          {
                "charger20066": "false",
                "nickname": "VLeaf",
                "telematicsEnabled": "true",
                "vin": "XXXX"
          }
        ],
        "vehicleInfo": [
          {
                "charger20066": "false",
                "nickname": "VLeaf",
                "telematicsEnabled": "true",
                "vin": "XXXX",
                "custom_sessionid": "XXXX"
          }
        ]
  },
  "vehicle": {
        "profile": {
          "vin": "XXXX",
          "gdcUserId": "",
          "gdcPassword": "",
          "encAuthToken": "XXXX",
          "dcmId": "XXXX",
          "nickname": "XXXX",
          "status": "ACCEPTED",
          "statusDate": "Sep 24, 2015 12:00 AM"
        }
  },
  "EncAuthToken": "XXXX",
  "CustomerInfo": {
        "UserId": "XXXX",
        "Language": "en-US",
        "Timezone": "America/New_York",
        "RegionCode": "NNA",
        "OwnerId": "XXXX",
        "Nickname": "XXXX",
        "Country": "US",
        "VehicleImage": "/content/language/default/images/img/ph_car.jpg",
        "UserVehicleBoundDurationSec": "946771200",
        "VehicleInfo": {
          "VIN": "XXXX",
          "DCMID": "XXXX",
          "SIMID": "XXXX",
          "NAVIID": "XXXX",
          "EncryptedNAVIID": "XXXX",
          "MSN": "XXXX",
          "LastVehicleLoginTime": "",
          "UserVehicleBoundTime": "2015-09-24T18:10:06Z",
          "LastDCMUseTime": "Apr  7, 2016 12:27 PM"
        }
  },
  "UserInfoRevisionNo": "1"
}

1.2   Request current status

Request the current battery status (not necessarily the latest, but whatever is cached from the last update on Nissan's server), using the urlencoded custom_session_id

curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/BatteryStatusRecordsRequest.php"

Response:

"status": 200,
"BatteryStatusRecords": {
"OperationResult": "START",
"OperationDateAndTime": "Apr  6, 2016 05:57 PM",
"BatteryStatus": {
  "BatteryChargingStatus": "NORMAL_CHARGING",
  "BatteryCapacity": "12",
  "BatteryRemainingAmount": "9",
  "BatteryRemainingAmountWH": "",
  "BatteryRemainingAmountkWH": ""
},
"PluginState": "CONNECTED",
"CruisingRangeAcOn": "110760.0",
"CruisingRangeAcOff": "112320.0",
"TimeRequiredToFull200_6kW": {
  "HourRequiredToFull": "2",
  "MinutesRequiredToFull": "30"
},
"NotificationDateAndTime": "2016/04/06 21:58",
"TargetDate": "2016/04/06 21:57"
}

1.3   Update status

Ask for an update from the car:

curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/BatteryStatusCheckRequest.php"
{ "status":200,"message":"success","userId":"XXXXXX","vin":"XXXXXXXXX","resultKey":"XXXXXXXXXX"}%

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

curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York&resultKey=XXXXXX" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/BatteryStatusCheckResultRequest.php"

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":""}
}

1.4   Remote Climate Control

1.4.1   Get current AC status

curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/remoteACRecordsRequest.php"

Response:

{
      "status": 200,
      "RemoteACRecords": {
              "OperationResult": "FINISH",
              "OperationDateAndTime": "Apr  6, 2016 07:19 PM",
              "RemoteACOperation": "STOP",
              "ACStartStopDateAndTime": "Apr  6, 2016 07:19 PM",
              "CruisingRangeAcOn": "152880.0",
              "CruisingRangeAcOff": "157248.0",
              "ACStartStopURL": "",
              "PluginState": "NOT_CONNECTED",
              "ACDurationBatterySec": "900",
              "ACDurationPluggedSec": "7200"
      },
      "OperationDateAndTime": ""
}

1.4.2   Turn on AC

curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/ACRemoteRequest.php"

Response:

{
  "status": 200,
  "userId": "virantha@gmail.com",
  "vin": "XXXX",
  "resultKey": "XXXXXXXXXXXXXX"
}

Use the resultKey to get the status of the AC request:

curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York&resultKey=XXXXXX" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/ACRemoteRsult.php"

If the status is not ready yet:

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

Once the AC is turned on, the following response will be sent:

{
      "status": 200,
      "responseFlag": "1",
      "operationResult": "START_BATTERY",
      "acContinueTime": "15",
      "cruisingRangeAcOn": "104720.0",
      "cruisingRangeAcOff": "109208.0",
      "timeStamp": "2016-04-07 16:47:41",
      "hvacStatus": "ON"
}

1.4.3   Turn off AC

curl --data "RegionCode=NNA&lg=en-US&DCMID=XXXX&VIN=XXXX&tz=America/New_York" --data-urlencode "custom_sessionid=XXXXXXXXXX" -X POST "https://gdcportalgw.its-mo.com/gworchest_0323A/gdc/ACRemoteOffRequest.php"

The response checking is identical to the AC on request.

2   to be continued....

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