├── .clang-tidy ├── .editorconfig ├── .gitignore ├── CMakeLists.txt ├── FUNDING.yml ├── LICENSE.md ├── README.md ├── arduino.sh ├── docs ├── metadata.md ├── security.md ├── session.md └── shared.md ├── examples ├── arduino │ └── arduino.ino ├── esp32 │ ├── .gitignore │ ├── .vscode │ │ └── extensions.json │ ├── CMakeLists.txt │ ├── platformio.ini │ ├── sdkconfig.seeed_xiao_esp32c3 │ └── src │ │ ├── CMakeLists.txt │ │ └── main.cpp └── simple │ ├── CMakeLists.txt │ ├── ble.cpp │ ├── include │ └── ble.h │ └── src │ └── ble.cpp ├── include ├── authenticator.h ├── car_server.pb.h ├── carserver.h ├── common.pb.h ├── errors.pb.h ├── keys.pb.h ├── metadata.h ├── security.h ├── session.h ├── shared.h ├── signatures.pb.h ├── universal_message.pb.h ├── vcsec.pb.h └── vehicle.pb.h ├── library.json ├── patches └── mbedtls2.patch ├── proto.sh ├── protobuf ├── car_server.options ├── car_server.proto ├── common.proto ├── errors.proto ├── keys.proto ├── signatures.options ├── signatures.proto ├── universal_message.options ├── universal_message.proto ├── vcsec.options ├── vcsec.proto └── vehicle.proto └── src ├── authenticator.cpp ├── car_server.pb.c ├── carserver.cpp ├── common.pb.c ├── errors.pb.c ├── keys.pb.c ├── metadata.cpp ├── security.cpp ├── session.cpp ├── shared.cpp ├── signatures.pb.c ├── universal_message.pb.c ├── vcsec.pb.c └── vehicle.pb.c /.clang-tidy: -------------------------------------------------------------------------------- 1 | # Generated from CLion Inspection settings 2 | --- 3 | Checks: '-*, 4 | bugprone-argument-comment, 5 | bugprone-assert-side-effect, 6 | bugprone-bad-signal-to-kill-thread, 7 | bugprone-branch-clone, 8 | bugprone-copy-constructor-init, 9 | bugprone-dangling-handle, 10 | bugprone-dynamic-static-initializers, 11 | bugprone-fold-init-type, 12 | bugprone-forward-declaration-namespace, 13 | bugprone-forwarding-reference-overload, 14 | bugprone-inaccurate-erase, 15 | bugprone-incorrect-roundings, 16 | bugprone-integer-division, 17 | bugprone-lambda-function-name, 18 | bugprone-macro-parentheses, 19 | bugprone-macro-repeated-side-effects, 20 | bugprone-misplaced-operator-in-strlen-in-alloc, 21 | bugprone-misplaced-pointer-arithmetic-in-alloc, 22 | bugprone-misplaced-widening-cast, 23 | bugprone-move-forwarding-reference, 24 | bugprone-multiple-statement-macro, 25 | bugprone-no-escape, 26 | bugprone-parent-virtual-call, 27 | bugprone-posix-return, 28 | bugprone-reserved-identifier, 29 | bugprone-sizeof-container, 30 | bugprone-sizeof-expression, 31 | bugprone-spuriously-wake-up-functions, 32 | bugprone-string-constructor, 33 | bugprone-string-integer-assignment, 34 | bugprone-string-literal-with-embedded-nul, 35 | bugprone-suspicious-enum-usage, 36 | bugprone-suspicious-include, 37 | bugprone-suspicious-memset-usage, 38 | bugprone-suspicious-missing-comma, 39 | bugprone-suspicious-semicolon, 40 | bugprone-suspicious-string-compare, 41 | bugprone-suspicious-memory-comparison, 42 | bugprone-suspicious-realloc-usage, 43 | bugprone-swapped-arguments, 44 | bugprone-terminating-continue, 45 | bugprone-throw-keyword-missing, 46 | bugprone-too-small-loop-variable, 47 | bugprone-undefined-memory-manipulation, 48 | bugprone-undelegated-constructor, 49 | bugprone-unhandled-self-assignment, 50 | bugprone-unused-raii, 51 | bugprone-unused-return-value, 52 | bugprone-use-after-move, 53 | bugprone-virtual-near-miss, 54 | cert-dcl21-cpp, 55 | cert-dcl58-cpp, 56 | cert-err34-c, 57 | cert-err52-cpp, 58 | cert-err60-cpp, 59 | cert-flp30-c, 60 | cert-msc50-cpp, 61 | cert-msc51-cpp, 62 | cert-str34-c, 63 | cppcoreguidelines-interfaces-global-init, 64 | cppcoreguidelines-narrowing-conversions, 65 | cppcoreguidelines-pro-type-member-init, 66 | cppcoreguidelines-pro-type-static-cast-downcast, 67 | cppcoreguidelines-slicing, 68 | google-default-arguments, 69 | google-explicit-constructor, 70 | google-runtime-operator, 71 | hicpp-exception-baseclass, 72 | hicpp-multiway-paths-covered, 73 | misc-misplaced-const, 74 | misc-new-delete-overloads, 75 | misc-no-recursion, 76 | misc-non-copyable-objects, 77 | misc-throw-by-value-catch-by-reference, 78 | misc-unconventional-assign-operator, 79 | misc-uniqueptr-reset-release, 80 | modernize-avoid-bind, 81 | modernize-concat-nested-namespaces, 82 | modernize-deprecated-headers, 83 | modernize-deprecated-ios-base-aliases, 84 | modernize-loop-convert, 85 | modernize-make-shared, 86 | modernize-make-unique, 87 | modernize-pass-by-value, 88 | modernize-raw-string-literal, 89 | modernize-redundant-void-arg, 90 | modernize-replace-auto-ptr, 91 | modernize-replace-disallow-copy-and-assign-macro, 92 | modernize-replace-random-shuffle, 93 | modernize-return-braced-init-list, 94 | modernize-shrink-to-fit, 95 | modernize-unary-static-assert, 96 | modernize-use-auto, 97 | modernize-use-bool-literals, 98 | modernize-use-emplace, 99 | modernize-use-equals-default, 100 | modernize-use-equals-delete, 101 | modernize-use-nodiscard, 102 | modernize-use-noexcept, 103 | modernize-use-nullptr, 104 | modernize-use-override, 105 | modernize-use-transparent-functors, 106 | modernize-use-uncaught-exceptions, 107 | mpi-buffer-deref, 108 | mpi-type-mismatch, 109 | openmp-use-default-none, 110 | performance-faster-string-find, 111 | performance-for-range-copy, 112 | performance-implicit-conversion-in-loop, 113 | performance-inefficient-algorithm, 114 | performance-inefficient-string-concatenation, 115 | performance-inefficient-vector-operation, 116 | performance-move-const-arg, 117 | performance-move-constructor-init, 118 | performance-no-automatic-move, 119 | performance-noexcept-move-constructor, 120 | performance-trivially-destructible, 121 | performance-type-promotion-in-math-fn, 122 | performance-unnecessary-copy-initialization, 123 | performance-unnecessary-value-param, 124 | portability-simd-intrinsics, 125 | readability-avoid-const-params-in-decls, 126 | readability-const-return-type, 127 | readability-container-size-empty, 128 | readability-convert-member-functions-to-static, 129 | readability-delete-null-pointer, 130 | readability-deleted-default, 131 | readability-inconsistent-declaration-parameter-name, 132 | readability-make-member-function-const, 133 | readability-misleading-indentation, 134 | readability-misplaced-array-index, 135 | readability-non-const-parameter, 136 | readability-redundant-control-flow, 137 | readability-redundant-declaration, 138 | readability-redundant-function-ptr-dereference, 139 | readability-redundant-smartptr-get, 140 | readability-redundant-string-cstr, 141 | readability-redundant-string-init, 142 | readability-simplify-subscript-expr, 143 | readability-static-accessed-through-instance, 144 | readability-static-definition-in-anonymous-namespace, 145 | readability-string-compare, 146 | readability-uniqueptr-delete-release, 147 | readability-use-anyofallof' -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | max_line_length = 150 11 | 12 | [*.md] 13 | max_line_length = off 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/intellij,c++,cmake,platformio,visualstudiocode 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=intellij,c++,cmake,platformio,visualstudiocode 3 | 4 | ### C++ ### 5 | # Prerequisites 6 | *.d 7 | 8 | # Compiled Object files 9 | *.slo 10 | *.lo 11 | *.o 12 | *.obj 13 | 14 | # Precompiled Headers 15 | *.gch 16 | *.pch 17 | 18 | # Compiled Dynamic libraries 19 | *.so 20 | *.dylib 21 | *.dll 22 | 23 | # Fortran module files 24 | *.mod 25 | *.smod 26 | 27 | # Compiled Static libraries 28 | *.lai 29 | *.la 30 | *.a 31 | *.lib 32 | 33 | # Executables 34 | *.exe 35 | *.out 36 | *.app 37 | 38 | ### CMake ### 39 | CMakeLists.txt.user 40 | CMakeCache.txt 41 | CMakeFiles 42 | CMakeScripts 43 | Testing 44 | Makefile 45 | cmake_install.cmake 46 | install_manifest.txt 47 | compile_commands.json 48 | CTestTestfile.cmake 49 | _deps 50 | 51 | ### CMake Patch ### 52 | # External projects 53 | *-prefix/ 54 | 55 | ### Intellij ### 56 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 57 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 58 | 59 | # User-specific stuff 60 | .idea/**/workspace.xml 61 | .idea/**/tasks.xml 62 | .idea/**/usage.statistics.xml 63 | .idea/**/dictionaries 64 | .idea/**/shelf 65 | 66 | # AWS User-specific 67 | .idea/**/aws.xml 68 | 69 | # Generated files 70 | .idea/**/contentModel.xml 71 | 72 | # Sensitive or high-churn files 73 | .idea/**/dataSources/ 74 | .idea/**/dataSources.ids 75 | .idea/**/dataSources.local.xml 76 | .idea/**/sqlDataSources.xml 77 | .idea/**/dynamic.xml 78 | .idea/**/uiDesigner.xml 79 | .idea/**/dbnavigator.xml 80 | 81 | # Gradle 82 | .idea/**/gradle.xml 83 | .idea/**/libraries 84 | 85 | # Gradle and Maven with auto-import 86 | # When using Gradle or Maven with auto-import, you should exclude module files, 87 | # since they will be recreated, and may cause churn. Uncomment if using 88 | # auto-import. 89 | # .idea/artifacts 90 | # .idea/compiler.xml 91 | # .idea/jarRepositories.xml 92 | # .idea/modules.xml 93 | # .idea/*.iml 94 | # .idea/modules 95 | # *.iml 96 | # *.ipr 97 | 98 | # CMake 99 | cmake-build-*/ 100 | 101 | # Mongo Explorer plugin 102 | .idea/**/mongoSettings.xml 103 | 104 | # File-based project format 105 | *.iws 106 | 107 | # IntelliJ 108 | out/ 109 | 110 | # mpeltonen/sbt-idea plugin 111 | .idea_modules/ 112 | 113 | # JIRA plugin 114 | atlassian-ide-plugin.xml 115 | 116 | # Cursive Clojure plugin 117 | .idea/replstate.xml 118 | 119 | # SonarLint plugin 120 | .idea/sonarlint/ 121 | 122 | # Crashlytics plugin (for Android Studio and IntelliJ) 123 | com_crashlytics_export_strings.xml 124 | crashlytics.properties 125 | crashlytics-build.properties 126 | fabric.properties 127 | 128 | # Editor-based Rest Client 129 | .idea/httpRequests 130 | 131 | # Android studio 3.1+ serialized cache file 132 | .idea/caches/build_file_checksums.ser 133 | 134 | ### Intellij Patch ### 135 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 136 | 137 | # *.iml 138 | # modules.xml 139 | # .idea/misc.xml 140 | # *.ipr 141 | 142 | # Sonarlint plugin 143 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 144 | .idea/**/sonarlint/ 145 | 146 | # SonarQube Plugin 147 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 148 | .idea/**/sonarIssues.xml 149 | 150 | # Markdown Navigator plugin 151 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 152 | .idea/**/markdown-navigator.xml 153 | .idea/**/markdown-navigator-enh.xml 154 | .idea/**/markdown-navigator/ 155 | 156 | # Cache file creation bug 157 | # See https://youtrack.jetbrains.com/issue/JBR-2257 158 | .idea/$CACHE_FILE$ 159 | 160 | # CodeStream plugin 161 | # https://plugins.jetbrains.com/plugin/12206-codestream 162 | .idea/codestream.xml 163 | 164 | # Azure Toolkit for IntelliJ plugin 165 | # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij 166 | .idea/**/azureSettings.xml 167 | .idea/ 168 | 169 | ### PlatformIO ### 170 | .pioenvs 171 | .piolibdeps 172 | .clang_complete 173 | .gcc-flags.json 174 | .pio 175 | 176 | ### VisualStudioCode ### 177 | .vscode/* 178 | !.vscode/settings.json 179 | !.vscode/tasks.json 180 | !.vscode/launch.json 181 | !.vscode/extensions.json 182 | !.vscode/*.code-snippets 183 | 184 | # Local History for Visual Studio Code 185 | .history/ 186 | 187 | # Built Visual Studio Code Extensions 188 | *.vsix 189 | 190 | ### VisualStudioCode Patch ### 191 | # Ignore all local history of files 192 | .history 193 | .ionide 194 | 195 | # syncthing 196 | .stfolder 197 | 198 | build/ 199 | library.zip 200 | 201 | # End of https://www.toptal.com/developers/gitignore/api/intellij,c++,cmake,platformio,visualstudiocode -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | include(FetchContent) 3 | project(TeslaBLE 4 | VERSION 1.0.0 5 | DESCRIPTION "CPP Tesla BLE Library" 6 | LANGUAGES CXX C 7 | ) 8 | 9 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -g -O0") 10 | set(MBEDTLS_AS_SUBPROJECT ON) 11 | set(ENABLE_PROGRAMS OFF) 12 | set(ENABLE_TESTING OFF) 13 | set(BUILD_EXAMPLES ON) 14 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 15 | 16 | FetchContent_Declare( 17 | nanopb 18 | GIT_REPOSITORY https://github.com/nanopb/nanopb.git 19 | GIT_TAG master 20 | GIT_SHALLOW TRUE 21 | ) 22 | FetchContent_Declare( 23 | mbedtls 24 | GIT_REPOSITORY https://github.com/Mbed-TLS/mbedtls.git 25 | GIT_TAG v3.6.0 26 | GIT_SHALLOW TRUE 27 | ) 28 | 29 | FetchContent_MakeAvailable(nanopb mbedtls) 30 | 31 | # Common source files 32 | set(COMMON_SOURCES 33 | src/authenticator.cpp 34 | src/metadata.cpp 35 | src/session.cpp 36 | src/shared.cpp 37 | src/carserver.cpp 38 | src/security.cpp 39 | ) 40 | 41 | # NanoPB source files 42 | set(NANOPB_SOURCES 43 | ${nanopb_SOURCE_DIR}/pb_decode.c 44 | ${nanopb_SOURCE_DIR}/pb_encode.c 45 | ${nanopb_SOURCE_DIR}/pb_common.c 46 | ) 47 | 48 | # Protobuf source files 49 | set(PROTOBUF_SOURCES 50 | src/car_server.pb.c 51 | src/common.pb.c 52 | src/errors.pb.c 53 | src/keys.pb.c 54 | src/signatures.pb.c 55 | src/universal_message.pb.c 56 | src/vcsec.pb.c 57 | src/vehicle.pb.c 58 | ) 59 | 60 | set(ALL_SOURCES ${COMMON_SOURCES} ${NANOPB_SOURCES} ${PROTOBUF_SOURCES}) 61 | add_library(TeslaBLE STATIC ${ALL_SOURCES}) 62 | 63 | # Include directories 64 | target_include_directories(TeslaBLE 65 | PRIVATE 66 | src/ 67 | include/ 68 | PUBLIC 69 | include/ 70 | include/protobuf/ 71 | ${nanopb_SOURCE_DIR} 72 | ${NANOPB_INCLUDE_DIRS} 73 | ${CMAKE_CURRENT_BINARY_DIR} 74 | ) 75 | 76 | # Link libraries 77 | target_link_libraries(TeslaBLE PUBLIC mbedcrypto mbedtls mbedx509) 78 | 79 | # Public headers 80 | set(PUBLIC_HEADERS 81 | include/authenticator.h 82 | include/metadata.h 83 | include/shared.h 84 | include/carserver.h 85 | include/security.h 86 | include/car_server.pb.h 87 | include/common.pb.h 88 | include/errors.pb.h 89 | include/keys.pb.h 90 | include/signatures.pb.h 91 | include/universal_message.pb.h 92 | include/vcsec.pb.h 93 | include/vehicle.pb.h 94 | ) 95 | 96 | # Set public headers 97 | foreach (HEADER ${PUBLIC_HEADERS}) 98 | set_target_properties(TeslaBLE PROPERTIES PUBLIC_HEADER ${HEADER}) 99 | endforeach () 100 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: pmdroid -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Tesla Vehicle Command (CPP/Arduino) 2 | Copyright (C) 2024 Pascal Matthiesen 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU Affero General Public License as published 6 | by the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | 14 | You should have received a copy of the GNU Affero General Public License 15 | along with this program. If not, see . -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Tesla Vehicle Command (CPP/Arduino) / TeslaBLE 2 | 3 | This library is the successor of the [TeslaBLE](https://github.com/pmdroid/tesla-ble) library. Tesla vehicles now 4 | support a new protocol that ensures secure, 5 | end-to-end command authentication. 6 | This C++ and Arduino library uses this protocol to control vehicle functions, such as climate control and charging. 7 | 8 | It is available under the terms of the GNU Affero General Public License (AGPL), with an option for businesses to 9 | purchase a commercial license. 10 | 11 | ### Initialization 12 | 13 | To begin using the TeslaBLE library, create instances of `Authenticator` and `Session`. 14 | 15 | ```c++ 16 | TeslaBLE::Authenticator authenticator; 17 | TeslaBLE::Session session; 18 | ``` 19 | 20 | ### Private Key 21 | 22 | A private key is essential for communication with the Tesla vehicle. You can either load an existing key or generate a 23 | new one. This key must be securely stored for future use. 24 | 25 | ```c++ 26 | unsigned char private_key[227] = 27 | "-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----"; 28 | 29 | client.LoadPrivateKey(private_key, sizeof private_key); 30 | ``` 31 | 32 | If the private key hasn't been whitelisted with the car, generate a whitelist message and send it. Be aware that the car 33 | may require NFC card confirmation or display a UI prompt. 34 | 35 | ```c++ 36 | authenticator.BuildKeyWhitelistMessage(Keys_Role_ROLE_OWNER, sessionInfoRequestBuffer, 37 | &sessionInfoRequestBufferLength); 38 | ``` 39 | 40 | **Upon message transmission, the vehicle will return an OperationStatus_E enumeration:** 41 | 42 | * **OPERATIONSTATUS_OK:** Operation successful. 43 | * **OPERATIONSTATUS_WAIT:** Vehicle awaiting NFC card presentation. 44 | * **OPERATIONSTATUS_ERROR:** Operation failed. 45 | 46 | **A `OPERATIONSTATUS_WAIT` response indicates that the NFC card has not yet been presented to the vehicle.** 47 | 48 | ### Session Setup 49 | 50 | To utilize the `Session`, provide the Vehicle Identification Number (VIN), routing address, and loaded `Authenticator`. 51 | The Session class will track essential data such as counter, epoch, and the vehicle's current time. 52 | 53 | ```c++ 54 | unsigned char vin[18]; // XP7YGCEL9NB000000 55 | strcpy((char *) vin, "XP7YGCEL9NB000000"); 56 | 57 | session.SetVIN(vin); 58 | session.GenerateRoutingAddress(); // Or session.SetRoutingAddress() 59 | session.LoadAuthenticator(&authenticator); 60 | ``` 61 | 62 | To conserve memory, a single Authenticator instance is shared among all system domains. This instance stores the 63 | exchanged secret essential for message encryption. 64 | 65 | ### Obtaining Session Information 66 | 67 | **Prior to transmitting any messages to the vehicle, it is imperative to obtain the latest session information by 68 | constructing and dispatching a `RequestSessionInfo` message.** The acquired session data should be retained for 69 | subsequent interactions. It is essential to note that the vehicle may provide updated session details, particularly 70 | following software revisions. 71 | 72 | ```c++ 73 | unsigned char securitySessionInfoRequestBuffer[200]; 74 | size_t securitySessionInfoRequestBufferLength = 0; 75 | 76 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY, 77 | securitySessionInfoRequestBuffer, 78 | &sessionInfoRequestBufferLength); 79 | 80 | session.UpdateSessionInfo(UniversalMessage_Domain_DOMAIN_INFOTAINMENT, session_info, 92); 81 | ``` 82 | 83 | This command exports the session data to the specified session_buffer and populates the session_size variable with the 84 | exported data's length. 85 | 86 | ```c++ 87 | session.ExportSessionInfo(UniversalMessage_Domain_DOMAIN_INFOTAINMENT, session_buffer, 88 | &session_size); 89 | ``` 90 | 91 | ### Sending Commands 92 | 93 | To execute commands on the car: 94 | 95 | 1. Use the `CarServer` or `Security` class to construct the appropriate command message. 96 | 2. Encapsulate the command message within a `RoutableMessage` using the `BuildRoutableMessage` function. 97 | 3. Send the resulting `RoutableMessage` to the car. 98 | 99 | ```c++ 100 | TeslaBLE::CarServer::TurnOnClimate(action_message_buffer, &action_message_buffer_size); 101 | 102 | size_t output_message_buffer_size = 0; 103 | unsigned char output_message_buffer[UniversalMessage_RoutableMessage_size]; 104 | 105 | session.BuildRoutableMessage(domain, action_message_buffer, 106 | action_message_buffer_size, output_message_buffer, 107 | &output_message_buffer_size); 108 | ``` 109 | 110 | The `BuildRoutableMessage` function processes the message by encrypting its content, computing a checksum for integrity, 111 | and prepending the message length. The resulting output is a transmission-ready message formatted for the vehicle. 112 | 113 | ## Protocol Details 114 | 115 | For a deeper understanding of the protocol, please refer to the official 116 | documentation: [vehicle-command](https://github.com/teslamotors/vehicle-command/blob/main/pkg/protocol/protocol.md). 117 | 118 | ## Expanding Functionality 119 | 120 | While not all `VehicleAction` commands are currently implemented, adding new functionality is straightforward. Simply 121 | incorporate additional commands into the `carserver.cpp` class, following the established pattern of existing commands. 122 | 123 | ## Protocol Buffers (Protobuf) 124 | 125 | **Updating Protobuf Files:** 126 | 127 | 1. Download the latest Protobuf files from the Tesla vehicle-command project on GitHub. 128 | 2. Navigate to the library's root directory and execute the `proto.sh` shell script. 129 | 130 | **Important Considerations:** 131 | 132 | * Tesla may occasionally remove fields from messages, potentially causing compatibility issues. Remain vigilant for 133 | updates. 134 | * Advanced users can customize Protobuf generation through the `.options` files. Refer to the documentation for more 135 | information: [here](https://jpa.kapsi.fi/nanopb/docs/reference.html#generator-options) 136 | 137 | ### Troubleshooting Protobuf Generation Issues 138 | 139 | **Potential Causes:** 140 | 141 | * **Missing nanopb installation:** Ensure nanopb is installed correctly by running `pip install nanopb`. 142 | * **Incorrect PATH environment variable:** Verify that the `PATH` variable includes the directory containing 143 | the `protoc` executable. Use the following command to add it to your `.bashrc` file (adjust for zsh if necessary): 144 | 145 | ```bash 146 | echo "export PATH=\"`python3 -m site --user-base`/bin:\$PATH\"" >> ~/.bashrc 147 | source ~/.bashrc 148 | ``` 149 | 150 | **Additional Tips:** 151 | 152 | * Consider using a virtual environment to isolate project dependencies. 153 | * Check for any conflicting installations of `protoc` or `nanopb`. 154 | * Verify that the protobuf files are formatted correctly. 155 | * Refer to the nanopb documentation for more in-depth troubleshooting guidance. 156 | 157 | ## Examples 158 | 159 | The library includes two example projects demonstrating direct car communication: 160 | 161 | ### Simple Example 162 | 163 | This example can be executed on a standard computer. To build and run: 164 | 165 | 1. Navigate to the `examples/simple` directory. 166 | 2. Create a build directory: `mkdir -p build` 167 | 3. Change to the build directory: `cd build` 168 | 4. Generate build files using CMake: `cmake ..` 169 | 5. Compile the project: `make` 170 | 171 | ### ESP32 Example 172 | 173 | This example requires a Seeed Studio XIAO ESP32C3 board and the PlatformIO development environment. Detailed 174 | instructions for setting up PlatformIO and configuring the project are available in the `examples/esp32` directory. 175 | 176 | ## Arduino Example: Automated Setup with `arduino.sh` Script 177 | 178 | **Automated Setup with `arduino.sh` Script** 179 | 180 | The Arduino example offers a convenient way to interact with your car, potentially through an automated setup script 181 | called `arduino.sh`. This script likely handles the following tasks: 182 | 183 | * **mbedtls Downgrade (Caution Advised):** Be aware that downgrading mbedTLS is not recommended due to security 184 | concerns. Consider exploring alternative approaches that support newer mbedTLS versions. 185 | * **Custom Library Creation:** The script might create a library file (`.zip`) compatible with the Arduino IDE. This 186 | library likely provides essential functionalities for communication with the car. 187 | * **Dependency Management:** It might manage the installation of additional dependencies such as nanopb version 0.4.8 188 | and the NimBLE library. 189 | 190 | **Important Considerations:** 191 | 192 | * **Security Risks:** Downgrading mbedTLS can expose your vehicle to security vulnerabilities. Prioritize secure 193 | solutions whenever possible. 194 | 195 | **Utilizing `arduino.sh` (if applicable):** 196 | 197 | 1. Ensure you understand the potential security risks associated with downgrading mbedTLS. 198 | 2. Review the script's functionality (if possible) to understand the steps it performs. 199 | 3. Execute the script according to its instructions (likely by running `./arduino.sh` in the terminal). 200 | 201 | ## License 202 | 203 | This project is dual-licensed under: 204 | 205 | 1. **GNU General Public License (AGPL) v3.0 or later**: 206 | Under this license, you are free to use, modify, and distribute the software, provided that any modifications or 207 | derivative works are also licensed under the GPL and the source code is made available. 208 | 209 | For the full text of the AGPL v3.0, see the [LICENSE](./LICENSE.md) file in this repository. 210 | 211 | 2. **Commercial License**: 212 | For organizations or individuals who wish to use this software in a proprietary product or without adhering to the 213 | terms of the AGPL, a commercial license is available. 214 | 215 | Please contact us at [me@pascal.sh](mailto://me@pascal.sh) for more information on purchasing a commercial 216 | license. 217 | 218 | ## Commercial License Information 219 | 220 | If you would like to use Tesla Vehicle Command (CPP/Arduino) in a commercial environment without the restrictions of the 221 | AGPL, you can purchase 222 | a commercial license. This license allows you to integrate Tesla Vehicle Command (CPP/Arduino) into your proprietary 223 | applications without the 224 | need to disclose your source code or comply with the AGPL's copyleft requirements. 225 | 226 | ## Contact 227 | 228 | For questions, support, or to inquire about a commercial license, please contact: 229 | 230 | - **Email**: [me@pascal.sh](mailto:me@pascal.sh) 231 | - **Website**: [https://pascal.sh](https://pascal.sh) 232 | 233 | # IMPORTANT 234 | 235 | Please take note that this library does not have official backing from Tesla, and its operational capabilities may be 236 | discontinued without prior notice. It's essential to recognize that this library retains private keys and other 237 | sensitive data on your device without encryption. I would like to stress that I assume no liability for any possible ( 238 | although highly unlikely) harm that may befall your vehicle. 239 | 240 | -------------------------------------------------------------------------------- /arduino.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | git apply --ignore-space-change --ignore-whitespace patches/mbedtls2.patch 4 | echo "updated code to run with mbedtls2" 5 | 6 | mkdir TeslaBLE 7 | cp src/* TeslaBLE/ 8 | cp include/* TeslaBLE/ 9 | zip -r library.zip TeslaBLE 10 | rm -rf TeslaBLE 11 | 12 | echo "created library.zip" -------------------------------------------------------------------------------- /docs/metadata.md: -------------------------------------------------------------------------------- 1 | ## TeslaBLE::MetaData Class Documentation 2 | 3 | ### Overview 4 | The `TeslaBLE::MetaData` class is responsible for generating metadata for Tesla BLE communication. It utilizes the `mbedtls` library for SHA256 hashing and the `universal_message.pb.h` and `signatures.pb.h` protocol buffers for message structuring. 5 | 6 | ### Public Methods 7 | 8 | #### `int Start()` 9 | * **Purpose:** Initializes the metadata generation process. 10 | * **Returns:** An integer indicating success or failure. 11 | 12 | #### `int BuildMetadata(UniversalMessage_Domain destination, Signatures_SignatureType method, unsigned char *vin, uint32_t expiresAt, uint32_t counter, unsigned char *epoch)` 13 | * **Purpose:** Constructs metadata for a specific message. 14 | * **Parameters:** 15 | * `destination`: The destination domain of the message. 16 | * `method`: The signature method used for the message. 17 | * `vin`: The vehicle identification number (VIN). 18 | * `expiresAt`: The expiration timestamp of the metadata. 19 | * `counter`: A message counter. 20 | * `epoch`: An epoch value. 21 | * **Returns:** An integer indicating success or failure. 22 | 23 | #### `void Checksum(unsigned char *output, unsigned char end_tag)` 24 | * **Purpose:** Calculates the checksum of the generated metadata. 25 | * **Parameters:** 26 | * `output`: A buffer to store the calculated checksum. 27 | * `end_tag`: The ending tag for the checksum calculation. 28 | 29 | ### Private Members 30 | 31 | #### `mbedtls_sha256_context sha256_context_` 32 | * **Purpose:** Stores the SHA256 context for checksum calculation. 33 | 34 | #### `uint8_t last_tag` 35 | * **Purpose:** Tracks the last used tag for metadata construction. 36 | 37 | #### `int Add(unsigned char signatures_tag, unsigned char *value, unsigned char value_length)` 38 | * **Purpose:** Adds data to the metadata with a specific tag. 39 | * **Parameters:** 40 | * `signatures_tag`: The tag identifying the data. 41 | * `value`: The data to be added. 42 | * `value_length`: The length of the data. 43 | * **Returns:** An integer indicating success or failure. 44 | 45 | #### `int AddUint32(unsigned char signatures_tag, uint32_t value)` 46 | * **Purpose:** Adds a 32-bit unsigned integer to the metadata with a specific tag. 47 | * **Parameters:** 48 | * `signatures_tag`: The tag identifying the data. 49 | * `value`: The 32-bit unsigned integer value. 50 | * **Returns:** An integer indicating success or failure. -------------------------------------------------------------------------------- /docs/security.md: -------------------------------------------------------------------------------- 1 | ## TeslaBLE::Security Class 2 | 3 | ### Overview 4 | The `TeslaBLE::Security` class encapsulates security-related operations within the TeslaBLE library. Based on the provided header, it appears to handle message encryption and decryption processes, likely using the `VCSEC_UnsignedMessage` structure defined in the `vcsec.pb.h` file. 5 | 6 | ### Public Methods 7 | 8 | #### `static int Unlock(unsigned char *buffer, size_t *buffer_size)` 9 | * **Purpose:** Decrypts the provided buffer. 10 | * **Parameters:** 11 | * `buffer`: The encrypted data buffer. 12 | * `buffer_size`: The size of the input buffer and output buffer. 13 | * **Returns:** An integer indicating success or failure. 14 | 15 | #### `static int Lock(unsigned char *buffer, size_t *buffer_size)` 16 | * **Purpose:** Encrypts the provided buffer. 17 | * **Parameters:** 18 | * `buffer`: The plaintext data buffer. 19 | * `buffer_size`: The size of the input buffer and output buffer. 20 | * **Returns:** An integer indicating success or failure. 21 | 22 | ### Private Methods 23 | #### `static int BuildUnsignedMessage(const VCSEC_UnsignedMessage *unsigned_message, unsigned char *buffer, size_t *buffer_size)` 24 | * **Purpose:** Builds an unsigned message based on the provided `VCSEC_UnsignedMessage` structure. 25 | * **Parameters:** 26 | * `unsigned_message`: The unsigned message structure. 27 | * `buffer`: The output buffer to hold the encoded message. 28 | * `buffer_size`: The size of the output buffer. 29 | * **Returns:** An integer indicating success or failure. 30 | -------------------------------------------------------------------------------- /docs/session.md: -------------------------------------------------------------------------------- 1 | ## TeslaBLE::Session Class 2 | 3 | ### Overview 4 | The `TeslaBLE::Session` class appears to manage session-related data and operations for Tesla BLE communication. It handles session information, metadata generation, authentication, and message construction. 5 | 6 | ### Key Components and Responsibilities 7 | * **Session Data:** Manages session-specific data for different domains, including time zeros, counters, epochs, clock times, car keys, and car key sizes. 8 | * **Metadata:** Instantiates a `MetaData` object for generating metadata. 9 | * **Authentication:** Collaborates with an `Authenticator` object for authentication tasks. 10 | * **Message Construction:** Builds routable and action messages, as well as request session info messages. 11 | 12 | ### Public Methods 13 | 14 | * **LoadAuthenticator:** Sets the `authenticator_` member to the provided `Authenticator` object. 15 | * **LoadPrivateKey:** Loads a private key for authentication purposes. 16 | * **LoadPrivateKeyContext:** Loads a pre-initialized MbedTLS private key context. 17 | * **GenerateRoutingAddress:** Generates a routing address. 18 | * **SetRoutingAddress:** Sets the routing address manually. 19 | * **UpdateSessionInfo:** Updates session information for a specific domain. 20 | * **BuildRoutableMessage:** Builds a routable message for a given domain and action message. 21 | * **BuildActionMessage:** Builds an action message based on a `CarServer_VehicleAction` structure. 22 | * **BuildRequestSessionInfoMessage:** Builds a request session info message for a given domain. 23 | * **ExpiresAt:** Calculates the expiration time for a given domain and expiration time in seconds. 24 | * **Counter:** Returns the counter for a given domain. 25 | * **Epoch:** Returns the epoch for a given domain. 26 | * **SetVIN:** Sets the vehicle identification number (VIN). 27 | * **ExportSessionInfo:** Exports session information for a given domain. 28 | -------------------------------------------------------------------------------- /docs/shared.md: -------------------------------------------------------------------------------- 1 | ## TeslaBLE::Shared Header File 2 | 3 | ### Overview 4 | The `shared.h` header file provides common definitions, enums, and utility functions used across the TeslaBLE library. It serves as a central repository for shared data types and helper functions. 5 | 6 | ### Detailed Breakdown 7 | 8 | #### Includes 9 | * Includes necessary header files for standard data types (`cstdint`, `cstddef`), protocol buffer definitions (`universal_message.pb.h`, `vcsec.pb.h`), and possibly other shared definitions (`shared.h`). 10 | 11 | #### ResultCode Enum 12 | * Defines a set of error codes used throughout the library for indicating success or failure of various operations. 13 | * `SUCCESS`: Operation successful. 14 | * `ERROR`: Generic error. 15 | * `TAG_OUT_OF_ORDER`: Tag used in metadata is out of order. 16 | * `TAG_VALUE_EMPTY`: Tag value is empty. 17 | * `TAG_VALUE_TOO_LONG`: Tag value exceeds maximum length. 18 | * `MBEDTLS_ERROR`: Error occurred in MbedTLS library. 19 | * `NANOPB_ENCODE_ERROR`: Error occurred during NanoPB encoding. 20 | * `NANOPB_DECODE_ERROR`: Error occurred during NanoPB decoding. 21 | * `PRIVATE_KEY_NOT_LOADED`: Private key is not loaded. 22 | * `SESSION_INFO_NOT_LOADED`: Session information is not loaded. 23 | * `SESSION_INFO_KEY_NOT_WHITELISTED`: Session info key is not whitelisted. 24 | 25 | #### Common Class 26 | * Provides static utility functions for various operations. 27 | * `HexStrToUint8`: Converts a hexadecimal string to a byte array. 28 | * `DumpHexBuffer`: Prints a hexadecimal representation of a byte buffer to the console for debugging purposes. 29 | * `PrependLength`: Prepends the length of an input buffer to an output buffer. 30 | * `calculateIdentifier`: Calculates an identifier based on a VIN. 31 | * `ExtractLength`: Extracts the length from an input buffer. 32 | * `GenerateUUID`: Generates a UUID. 33 | * `DecodeRoutableMessage`: Decodes a routable message from a byte buffer using NanoPB. 34 | * `DecodeFromVCSECMessage`: Decodes a VCSEC message from a byte buffer using NanoPB. 35 | * `EncodeRoutableMessage`: Encodes a routable message to a byte buffer using NanoPB. 36 | * `ResultCodeToMessage`: Converts a result code to a human-readable message. 37 | * `ErrorCodeToMessage`: Converts a message fault enum to a human-readable message. 38 | * `OperationStatusToMessage`: Converts an operation status enum to a human-readable message. 39 | * `PrintErrorFromMbedTlsErrorCode`: Prints an error message based on an MbedTLS error code. 40 | -------------------------------------------------------------------------------- /examples/arduino/arduino.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "NimBLEDevice.h" 8 | #include 9 | 10 | static NimBLEUUID serviceUUID("00000211-b2d1-43f0-9b88-960cebf8b91e"); 11 | static NimBLEUUID readUUID("00000213-b2d1-43f0-9b88-960cebf8b91e"); 12 | static NimBLEUUID writeUUID("00000212-b2d1-43f0-9b88-960cebf8b91e"); 13 | 14 | static NimBLEAddress car("b0:d2:78:87:26:72"); 15 | 16 | static NimBLERemoteCharacteristic *readCharacteristic; 17 | static NimBLERemoteCharacteristic *writeCharacteristic; 18 | 19 | TeslaBLE::Authenticator authenticator = TeslaBLE::Authenticator{}; 20 | TeslaBLE::Session session = TeslaBLE::Session{}; 21 | 22 | static boolean doConnect = true; 23 | unsigned char ble_buffer[200]; 24 | size_t current_message_size = 0; 25 | 26 | size_t action_message_buffer_size = 0; 27 | unsigned char action_message_buffer[50]; 28 | UniversalMessage_Domain domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT; 29 | 30 | size_t output_message_buffer_size = 0; 31 | unsigned char output_message_buffer[UniversalMessage_RoutableMessage_size]; 32 | 33 | 34 | void setup() { 35 | Serial.begin(9600); 36 | NimBLEDevice::init("TeslaBLE"); 37 | 38 | const char *vin = "XP7YGCEL0NB000000"; 39 | unsigned char private_key[227] = 40 | "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEICrUkL0StUxZNhVRkK+QmeGDXVQvyjB6Iar8WQu3dDrloAoGCCqGSM49\nAwEHoUQDQgAEsvEtszFQqp8a83gIXsRBaS3UhOf6dgQDBoZWXSXIozABiawOfNF/\nOydB4e9zX5DiZYwTnUbWYlpqMk08cn4ZeA==\n-----END EC PRIVATE KEY-----"; 41 | 42 | authenticator.LoadPrivateKey(private_key, sizeof private_key); 43 | 44 | session.SetVIN((unsigned char*)vin); 45 | session.GenerateRoutingAddress(); 46 | session.LoadAuthenticator(&authenticator); 47 | } 48 | 49 | 50 | void handleMessage() { 51 | TeslaBLE::Common::DumpHexBuffer("RX: ", ble_buffer, current_message_size); 52 | 53 | UniversalMessage_RoutableMessage routable_message = UniversalMessage_RoutableMessage_init_zero; 54 | TeslaBLE::Common::DecodeRoutableMessage(ble_buffer, current_message_size, &routable_message); 55 | 56 | current_message_size = 0; 57 | 58 | if (routable_message.has_to_destination && routable_message.to_destination.sub_destination. 59 | domain == UniversalMessage_Domain_DOMAIN_BROADCAST) { 60 | // printf("Dropping broadcast message\n"); 61 | 62 | VCSEC_FromVCSECMessage vcsec_from_vcsec_message = VCSEC_FromVCSECMessage_init_default; 63 | TeslaBLE::Common::DecodeFromVCSECMessage(routable_message.payload.protobuf_message_as_bytes.bytes, 64 | routable_message.payload.protobuf_message_as_bytes.size, 65 | &vcsec_from_vcsec_message); 66 | 67 | Serial.printf("Sub Message: %d\n", vcsec_from_vcsec_message.which_sub_message); 68 | if (vcsec_from_vcsec_message.which_sub_message == VCSEC_FromVCSECMessage_vehicleStatus_tag) { 69 | Serial.printf("User Present: %d\n", vcsec_from_vcsec_message.sub_message.vehicleStatus.userPresence); 70 | Serial.printf("Charging Port Open: %d\n", 71 | vcsec_from_vcsec_message.sub_message.vehicleStatus.closureStatuses.chargePort); 72 | } 73 | 74 | return; 75 | } 76 | 77 | if (routable_message.has_signedMessageStatus) { 78 | TeslaBLE::Common::ErrorCodeToMessage(routable_message.signedMessageStatus.signed_message_fault); 79 | TeslaBLE::Common::OperationStatusToMessage(routable_message.signedMessageStatus.operation_status); 80 | return; 81 | } 82 | 83 | if (routable_message.which_payload == UniversalMessage_RoutableMessage_session_info_tag) { 84 | session.UpdateSessionInfo(routable_message.from_destination.sub_destination. 85 | domain, 86 | routable_message.payload.session_info.bytes, 87 | routable_message.payload.session_info.size); 88 | } 89 | } 90 | 91 | void notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, 92 | size_t length, bool isNotify) 93 | { 94 | unsigned char input_buffer[length]; 95 | memcpy(&input_buffer, pData, length); 96 | const size_t size = TeslaBLE::Common::ExtractLength(input_buffer); 97 | 98 | if (current_message_size == 0 && size > length) { 99 | current_message_size = 0; 100 | 101 | size_t payload_size = length - 2; 102 | memcpy(ble_buffer, input_buffer + 2, payload_size); 103 | current_message_size = payload_size; 104 | return; 105 | } 106 | 107 | if (current_message_size > 0) { 108 | memcpy(ble_buffer + current_message_size, input_buffer, length); 109 | current_message_size = current_message_size + length; 110 | handleMessage(); 111 | return; 112 | } 113 | 114 | size_t payload_size = length - 2; 115 | memcpy(ble_buffer, input_buffer + 2, payload_size); 116 | current_message_size = payload_size; 117 | handleMessage(); 118 | } 119 | 120 | 121 | bool connectToCar() 122 | { 123 | Serial.printf("Connecting to car!\n"); 124 | 125 | BLEClient *pClient = BLEDevice::createClient(); 126 | pClient->connect(car); 127 | 128 | if (!pClient->isConnected()) 129 | { 130 | Serial.printf("Failed to connect!\n"); 131 | return false; 132 | } 133 | 134 | Serial.printf("Connected to car!\n"); 135 | 136 | BLERemoteService *pRemoteService = pClient->getService(serviceUUID); 137 | if (pRemoteService == nullptr) 138 | { 139 | Serial.printf("Failed to find our service UUID: %s\n", 140 | serviceUUID.toString().c_str()); 141 | pClient->disconnect(); 142 | return false; 143 | } 144 | 145 | readCharacteristic = pRemoteService->getCharacteristic(readUUID); 146 | if (readCharacteristic == nullptr) 147 | { 148 | Serial.printf("Failed to find our read characteristic UUID: %s\n", 149 | readUUID.toString().c_str()); 150 | pClient->disconnect(); 151 | return false; 152 | } 153 | 154 | if (!readCharacteristic->subscribe(false, notifyCB)) 155 | { 156 | Serial.printf("Failed to subscribe to characteristic UUID: %s\n", 157 | readUUID.toString().c_str()); 158 | return false; 159 | } 160 | 161 | writeCharacteristic = pRemoteService->getCharacteristic(writeUUID); 162 | if (writeCharacteristic == nullptr) 163 | { 164 | Serial.printf("Failed to find our write characteristic UUID: %s\n", 165 | writeUUID.toString().c_str()); 166 | pClient->disconnect(); 167 | return false; 168 | } 169 | 170 | return true; 171 | } 172 | 173 | void loop() { 174 | if (doConnect) { 175 | if (connectToCar()) { 176 | Serial.printf("We are now connected to the Car.\n"); 177 | 178 | unsigned char sessionInfoRequestBuffer[200]; 179 | size_t sessionInfoRequestBufferLength = 0; 180 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_INFOTAINMENT, 181 | sessionInfoRequestBuffer, &sessionInfoRequestBufferLength); 182 | writeCharacteristic->writeValue(sessionInfoRequestBuffer, sessionInfoRequestBufferLength); 183 | 184 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY, 185 | sessionInfoRequestBuffer, &sessionInfoRequestBufferLength); 186 | writeCharacteristic->writeValue(sessionInfoRequestBuffer, sessionInfoRequestBufferLength); 187 | doConnect = false; 188 | } else { 189 | Serial.printf("We have failed to connect to the server; there is nothin more we will do.\n"); 190 | } 191 | } else { 192 | Serial.printf("\nclimate-on: Climate ON\nclimate-ff: Climate OFF\nmedia-next: Media Next Track\nlock: Lock\nunlock: UnLock\n\nPlease select a command: "); 193 | 194 | while (Serial.available() == 0) {} 195 | String input = Serial.readString(); 196 | input.trim(); 197 | 198 | if (input == "climate-on") { 199 | Serial.printf("Sending climate on command\n"); 200 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT; 201 | TeslaBLE::CarServer::TurnOnClimate(action_message_buffer, &action_message_buffer_size); 202 | } else if (input == "climate-off") { 203 | Serial.printf("Sending climate off command\n"); 204 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT; 205 | TeslaBLE::CarServer::TurnOffClimate(action_message_buffer, &action_message_buffer_size); 206 | } else if (input == "media-next") { 207 | Serial.printf("Sending media next track command\n"); 208 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT; 209 | TeslaBLE::CarServer::NextMediaTrack(action_message_buffer, &action_message_buffer_size); 210 | } else if (input == "lock") { 211 | Serial.printf("Sending lock command\n"); 212 | domain = UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY; 213 | TeslaBLE::Security::Lock(action_message_buffer, &action_message_buffer_size); 214 | } else if (input == "unlock") { 215 | Serial.printf("Sending unlock command\n"); 216 | domain = UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY; 217 | TeslaBLE::Security::Unlock(action_message_buffer, &action_message_buffer_size); 218 | } else { 219 | action_message_buffer_size = 0; 220 | Serial.printf("Please select a valid command.\n"); 221 | } 222 | 223 | if (action_message_buffer_size > 0) { 224 | session.BuildRoutableMessage(domain, action_message_buffer, 225 | action_message_buffer_size, output_message_buffer, 226 | &output_message_buffer_size); 227 | 228 | writeCharacteristic->writeValue(output_message_buffer, output_message_buffer_size); 229 | 230 | action_message_buffer_size = 0; 231 | output_message_buffer_size = 0; 232 | } 233 | } 234 | } -------------------------------------------------------------------------------- /examples/esp32/.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /examples/esp32/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /examples/esp32/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.0) 2 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 3 | project(BLETesla) 4 | -------------------------------------------------------------------------------- /examples/esp32/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:seeed_xiao_esp32c3] 12 | platform = espressif32 13 | board = seeed_xiao_esp32c3 14 | framework = espidf 15 | lib_deps = 16 | nanopb/Nanopb@^0.4.8 17 | https://github.com/h2zero/esp-nimble-cpp.git#3c5a2fd4a9c327e87f9b5cdcf9b7382f66bc11be 18 | symlink://../../ 19 | -------------------------------------------------------------------------------- /examples/esp32/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file was automatically generated for projects 2 | # without default 'CMakeLists.txt' file. 3 | 4 | FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) 5 | 6 | idf_component_register(SRCS ${app_sources}) 7 | -------------------------------------------------------------------------------- /examples/esp32/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "esp_system.h" 6 | #include "freertos/FreeRTOS.h" 7 | #include "freertos/task.h" 8 | #include 9 | #include 10 | #include 11 | #include "NimBLEDevice.h" 12 | #include 13 | 14 | static NimBLEUUID serviceUUID("00000211-b2d1-43f0-9b88-960cebf8b91e"); 15 | static NimBLEUUID readUUID("00000213-b2d1-43f0-9b88-960cebf8b91e"); 16 | static NimBLEUUID writeUUID("00000212-b2d1-43f0-9b88-960cebf8b91e"); 17 | 18 | static NimBLERemoteCharacteristic *readCharacteristic; 19 | static NimBLERemoteCharacteristic *writeCharacteristic; 20 | 21 | TeslaBLE::Authenticator authenticator = TeslaBLE::Authenticator{}; 22 | TeslaBLE::Session session = TeslaBLE::Session{}; 23 | 24 | unsigned char ble_buffer[200]; 25 | size_t current_message_size = 0; 26 | 27 | extern "C" 28 | { 29 | void app_main(void); 30 | } 31 | 32 | void readInput(void *pvParameter) 33 | { 34 | uint8_t rxbuf[1]; 35 | 36 | printf("\n1: Climate ON\n2: Climate OFF\n3: Media Next Track\n4: Lock\n5: UnLock\n\nPlease select a command: "); 37 | size_t action_message_buffer_size = 0; 38 | unsigned char action_message_buffer[50]; 39 | UniversalMessage_Domain domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT; 40 | 41 | size_t output_message_buffer_size = 0; 42 | unsigned char output_message_buffer[UniversalMessage_RoutableMessage_size]; 43 | 44 | while (1) 45 | { 46 | if (usb_serial_jtag_ll_rxfifo_data_available()) 47 | { 48 | usb_serial_jtag_ll_read_rxfifo(rxbuf, 1); 49 | 50 | switch (rxbuf[0]) 51 | { 52 | case 49: 53 | printf("Sending climate on command\n"); 54 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT; 55 | TeslaBLE::CarServer::TurnOnClimate(action_message_buffer, &action_message_buffer_size); 56 | break; 57 | case 50: 58 | printf("Sending climate off command\n"); 59 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT; 60 | TeslaBLE::CarServer::TurnOffClimate(action_message_buffer, &action_message_buffer_size); 61 | break; 62 | case 51: 63 | printf("Sending media next track command\n"); 64 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT; 65 | TeslaBLE::CarServer::NextMediaTrack(action_message_buffer, &action_message_buffer_size); 66 | break; 67 | case 52: 68 | printf("Sending lock command\n"); 69 | domain = UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY; 70 | TeslaBLE::Security::Lock(action_message_buffer, &action_message_buffer_size); 71 | break; 72 | case 53: 73 | printf("Sending unlock command\n"); 74 | domain = UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY; 75 | TeslaBLE::Security::Unlock(action_message_buffer, &action_message_buffer_size); 76 | break; 77 | default: 78 | action_message_buffer_size = 0; 79 | printf("Please select a valid command.\n"); 80 | break; 81 | } 82 | 83 | if (action_message_buffer_size > 0) 84 | { 85 | session.BuildRoutableMessage(domain, action_message_buffer, 86 | action_message_buffer_size, output_message_buffer, 87 | &output_message_buffer_size); 88 | 89 | writeCharacteristic->writeValue(output_message_buffer, output_message_buffer_size); 90 | 91 | action_message_buffer_size = 0; 92 | output_message_buffer_size = 0; 93 | } 94 | } 95 | 96 | vTaskDelay(pdMS_TO_TICKS(10)); 97 | } 98 | 99 | free(rxbuf); 100 | vTaskDelete(NULL); 101 | } 102 | 103 | void handleMessage() { 104 | TeslaBLE::Common::DumpHexBuffer("RX: ", ble_buffer, current_message_size); 105 | 106 | UniversalMessage_RoutableMessage routable_message = UniversalMessage_RoutableMessage_init_zero; 107 | TeslaBLE::Common::DecodeRoutableMessage(ble_buffer, current_message_size, &routable_message); 108 | 109 | current_message_size = 0; 110 | 111 | if (routable_message.has_to_destination && routable_message.to_destination.sub_destination. 112 | domain == UniversalMessage_Domain_DOMAIN_BROADCAST) { 113 | // printf("Dropping broadcast message\n"); 114 | 115 | VCSEC_FromVCSECMessage vcsec_from_vcsec_message = VCSEC_FromVCSECMessage_init_default; 116 | TeslaBLE::Common::DecodeFromVCSECMessage(routable_message.payload.protobuf_message_as_bytes.bytes, 117 | routable_message.payload.protobuf_message_as_bytes.size, 118 | &vcsec_from_vcsec_message); 119 | 120 | printf("Sub Message: %d\n", vcsec_from_vcsec_message.which_sub_message); 121 | if (vcsec_from_vcsec_message.which_sub_message == VCSEC_FromVCSECMessage_vehicleStatus_tag) { 122 | printf("User Present: %d\n", vcsec_from_vcsec_message.sub_message.vehicleStatus.userPresence); 123 | printf("Charging Port Open: %d\n", 124 | vcsec_from_vcsec_message.sub_message.vehicleStatus.closureStatuses.chargePort); 125 | } 126 | 127 | return; 128 | } 129 | 130 | if (routable_message.has_signedMessageStatus) { 131 | TeslaBLE::Common::ErrorCodeToMessage(routable_message.signedMessageStatus.signed_message_fault); 132 | TeslaBLE::Common::OperationStatusToMessage(routable_message.signedMessageStatus.operation_status); 133 | return; 134 | } 135 | 136 | if (routable_message.which_payload == UniversalMessage_RoutableMessage_session_info_tag) { 137 | session.UpdateSessionInfo(routable_message.from_destination.sub_destination. 138 | domain, 139 | routable_message.payload.session_info.bytes, 140 | routable_message.payload.session_info.size); 141 | } 142 | } 143 | 144 | void notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, 145 | size_t length, bool isNotify) 146 | { 147 | unsigned char input_buffer[length]; 148 | memcpy(&input_buffer, pData, length); 149 | const size_t size = TeslaBLE::Common::ExtractLength(input_buffer); 150 | 151 | if (current_message_size == 0 && size > length) { 152 | current_message_size = 0; 153 | 154 | size_t payload_size = length - 2; 155 | memcpy(ble_buffer, input_buffer + 2, payload_size); 156 | current_message_size = payload_size; 157 | return; 158 | } 159 | 160 | if (current_message_size > 0) { 161 | memcpy(ble_buffer + current_message_size, input_buffer, length); 162 | current_message_size = current_message_size + length; 163 | handleMessage(); 164 | return; 165 | } 166 | 167 | size_t payload_size = length - 2; 168 | memcpy(ble_buffer, input_buffer + 2, payload_size); 169 | current_message_size = payload_size; 170 | handleMessage(); 171 | } 172 | 173 | bool connectToCar() 174 | { 175 | printf("Connect to car!\n"); 176 | 177 | BLEClient *pClient = BLEDevice::createClient(); 178 | pClient->connect(NimBLEAddress("b0:d2:78:87:26:72")); 179 | 180 | if (!pClient->isConnected()) 181 | { 182 | printf("Failed to connect!\n"); 183 | return false; 184 | } 185 | 186 | printf("Connected to car!\n"); 187 | 188 | BLERemoteService *pRemoteService = pClient->getService(serviceUUID); 189 | if (pRemoteService == nullptr) 190 | { 191 | printf("Failed to find our service UUID: %s\n", 192 | serviceUUID.toString().c_str()); 193 | pClient->disconnect(); 194 | return false; 195 | } 196 | 197 | readCharacteristic = pRemoteService->getCharacteristic(readUUID); 198 | if (readCharacteristic == nullptr) 199 | { 200 | printf("Failed to find our read characteristic UUID: %s\n", 201 | readUUID.toString().c_str()); 202 | pClient->disconnect(); 203 | return false; 204 | } 205 | 206 | if (!readCharacteristic->subscribe(false, notifyCB)) 207 | { 208 | printf("Failed to subscribe to characteristic UUID: %s\n", 209 | readUUID.toString().c_str()); 210 | return false; 211 | } 212 | 213 | writeCharacteristic = pRemoteService->getCharacteristic(writeUUID); 214 | if (writeCharacteristic == nullptr) 215 | { 216 | printf("Failed to find our write characteristic UUID: %s\n", 217 | writeUUID.toString().c_str()); 218 | pClient->disconnect(); 219 | return false; 220 | } 221 | 222 | return true; 223 | } 224 | 225 | void connectTask(void *parameter) 226 | { 227 | if (connectToCar()) 228 | { 229 | printf("We are now connected to the Car.\n"); 230 | 231 | unsigned char sessionInfoRequestBuffer[200]; 232 | size_t sessionInfoRequestBufferLength = 0; 233 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_INFOTAINMENT, 234 | sessionInfoRequestBuffer, &sessionInfoRequestBufferLength); 235 | writeCharacteristic->writeValue(sessionInfoRequestBuffer, sessionInfoRequestBufferLength); 236 | 237 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY, 238 | sessionInfoRequestBuffer, &sessionInfoRequestBufferLength); 239 | writeCharacteristic->writeValue(sessionInfoRequestBuffer, sessionInfoRequestBufferLength); 240 | 241 | xTaskCreate(readInput, "readInput", 10000, NULL, 1, NULL); 242 | } 243 | else 244 | { 245 | printf("We have failed to connect to the server; there is nothin more we will do.\n"); 246 | } 247 | 248 | vTaskDelete(NULL); 249 | } 250 | 251 | void app_main() 252 | { 253 | NimBLEDevice::init("TeslaBLE"); 254 | 255 | const char *vin = "XP7YGCEL0NB000000"; 256 | unsigned char private_key[227] = 257 | "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEICrUkL0StUxZNhVRkK+QmeGDXVQvyjB6Iar8WQu3dDrloAoGCCqGSM49\nAwEHoUQDQgAEsvEtszFQqp8a83gIXsRBaS3UhOf6dgQDBoZWXSXIozABiawOfNF/\nOydB4e9zX5DiZYwTnUbWYlpqMk08cn4ZeA==\n-----END EC PRIVATE KEY-----"; 258 | 259 | authenticator.LoadPrivateKey(private_key, sizeof private_key); 260 | 261 | session.SetVIN((unsigned char*)vin); 262 | session.GenerateRoutingAddress(); 263 | session.LoadAuthenticator(&authenticator); 264 | 265 | xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL); 266 | } -------------------------------------------------------------------------------- /examples/simple/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(simple) 3 | 4 | include(FetchContent) 5 | FetchContent_Declare( 6 | simpleble 7 | GIT_REPOSITORY https://github.com/OpenBluetoothToolbox/SimpleBLE.git 8 | GIT_TAG v0.7.3 9 | GIT_SHALLOW YES 10 | ) 11 | 12 | set(CMAKE_CXX_STANDARD 17) 13 | 14 | FetchContent_GetProperties(simpleble) 15 | if (NOT simpleble_POPULATED) 16 | FetchContent_Populate(simpleble) 17 | list(APPEND CMAKE_MODULE_PATH "${simpleble_SOURCE_DIR}/cmake/find") 18 | add_subdirectory("${simpleble_SOURCE_DIR}/simpleble" "${simpleble_BINARY_DIR}") 19 | endif () 20 | 21 | set(simpleble_FOUND 1) 22 | 23 | add_subdirectory(../../ TeslaBLE) 24 | add_executable(bluetooth ble.cpp src/ble.cpp) 25 | target_link_libraries(bluetooth PRIVATE TeslaBLE simpleble::simpleble) 26 | target_include_directories(bluetooth PRIVATE include/) 27 | 28 | -------------------------------------------------------------------------------- /examples/simple/ble.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "ble.h" 12 | 13 | TeslaBLE::BLE ble = TeslaBLE::BLE(false); 14 | TeslaBLE::Session session = TeslaBLE::Session{}; 15 | 16 | void message_handler(UniversalMessage_RoutableMessage routable_message) { 17 | if (routable_message.has_to_destination && routable_message.to_destination.sub_destination. 18 | domain == UniversalMessage_Domain_DOMAIN_BROADCAST) { 19 | // printf("Dropping broadcast message\n"); 20 | 21 | VCSEC_FromVCSECMessage vcsec_from_vcsec_message = VCSEC_FromVCSECMessage_init_default; 22 | TeslaBLE::Common::DecodeFromVCSECMessage(routable_message.payload.protobuf_message_as_bytes.bytes, 23 | routable_message.payload.protobuf_message_as_bytes.size, 24 | &vcsec_from_vcsec_message); 25 | 26 | // printf("Sub Message: %d\n", vcsec_from_vcsec_message.which_sub_message); 27 | if (vcsec_from_vcsec_message.which_sub_message == VCSEC_FromVCSECMessage_vehicleStatus_tag) { 28 | printf("User Present: %d\n", vcsec_from_vcsec_message.sub_message.vehicleStatus.userPresence); 29 | printf("Charging Port Open: %d\n", 30 | vcsec_from_vcsec_message.sub_message.vehicleStatus.closureStatuses.chargePort); 31 | } 32 | 33 | return; 34 | } 35 | 36 | if (routable_message.has_signedMessageStatus) { 37 | TeslaBLE::Common::ErrorCodeToMessage(routable_message.signedMessageStatus.signed_message_fault); 38 | TeslaBLE::Common::OperationStatusToMessage(routable_message.signedMessageStatus.operation_status); 39 | return; 40 | } 41 | 42 | if (routable_message.which_payload == UniversalMessage_RoutableMessage_session_info_tag) { 43 | session.UpdateSessionInfo(routable_message.from_destination.sub_destination. 44 | domain, 45 | routable_message.payload.session_info.bytes, 46 | routable_message.payload.session_info.size); 47 | } 48 | } 49 | 50 | int main() { 51 | const char *vin = "XP7YGCEL0NB000000"; 52 | unsigned char private_key[227] = 53 | "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEICrUkL0StUxZNhVRkK+QmeGDXVQvyjB6Iar8WQu3dDrloAoGCCqGSM49\nAwEHoUQDQgAEsvEtszFQqp8a83gIXsRBaS3UhOf6dgQDBoZWXSXIozABiawOfNF/\nOydB4e9zX5DiZYwTnUbWYlpqMk08cn4ZeA==\n-----END EC PRIVATE KEY-----"; 54 | 55 | TeslaBLE::Authenticator authenticator = TeslaBLE::Authenticator{}; 56 | authenticator.LoadPrivateKey(private_key, sizeof private_key); 57 | 58 | ble.registerMessageHandler(message_handler); 59 | int result = ble.connect((unsigned char *) vin); 60 | if (result != 0) { 61 | printf("Failed to connect to the vehicle.\n"); 62 | return 1; 63 | } 64 | 65 | session.SetVIN((unsigned char *) vin); 66 | session.GenerateRoutingAddress(); 67 | session.LoadAuthenticator(&authenticator); 68 | 69 | unsigned char sessionInfoRequestBuffer[200]; 70 | size_t sessionInfoRequestBufferLength = 0; 71 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_INFOTAINMENT, 72 | sessionInfoRequestBuffer, &sessionInfoRequestBufferLength); 73 | 74 | unsigned char securitySessionInfoRequestBuffer[200]; 75 | size_t securitySessionInfoRequestBufferLength = 0; 76 | session.BuildRequestSessionInfoMessage(UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY, 77 | securitySessionInfoRequestBuffer, 78 | &securitySessionInfoRequestBufferLength); 79 | 80 | 81 | //authenticator.CreatePrivateKey(); 82 | //authenticator.BuildKeyWhitelistMessage(Keys_Role_ROLE_OWNER, sessionInfoRequestBuffer, 83 | // &sessionInfoRequestBufferLength); 84 | 85 | int userInput; 86 | bool runLoop = true; 87 | bool hasUpdatedSessionInfo = false; 88 | 89 | // Prompt the user to enter a number 90 | std::cout << "\n\n1: Whitelist Private Key\n"; 91 | std::cout << "2: Update SessionInfo\n"; 92 | std::cout << "3: Climate ON\n"; 93 | std::cout << "4: Climate OFF\n"; 94 | std::cout << "5: Media Next Track\n"; 95 | std::cout << "6: Lock\n"; 96 | std::cout << "7: UnLock\n"; 97 | std::cout << "8: Disconnect\n"; 98 | std::cout << "9: Export VEHICLE_SECURITY SessionInfo\n"; 99 | std::cout << "10: Export DOMAIN_INFOTAINMENT SessionInfo\n\n"; 100 | 101 | while (runLoop) { 102 | std::cout << "Please select a command: "; 103 | std::cin >> userInput; 104 | 105 | size_t action_message_buffer_size = 0; 106 | unsigned char action_message_buffer[50]; 107 | UniversalMessage_Domain domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT; 108 | 109 | size_t session_size = 0; 110 | unsigned char session_buffer[Signatures_SessionInfo_size]; 111 | 112 | size_t whitelist_size = 0; 113 | unsigned char whitelist_buffer[UniversalMessage_RoutableMessage_size]; 114 | 115 | if (!hasUpdatedSessionInfo && userInput != 2 && userInput != 1) { 116 | std::cout << "\n\n\nYou have to execute command 2: to update the SessionInfo!\n\n\n"; 117 | } 118 | 119 | std::cout << "\n"; 120 | switch (userInput) { 121 | case 1: 122 | authenticator.BuildKeyWhitelistMessage(Keys_Role_ROLE_OWNER, whitelist_buffer, &whitelist_size); 123 | std::cout << "\n\n\nTouch NFC Card now!\n\n\n"; 124 | break; 125 | case 2: 126 | ble.send((char *) sessionInfoRequestBuffer, sessionInfoRequestBufferLength); 127 | ble.send((char *) securitySessionInfoRequestBuffer, securitySessionInfoRequestBufferLength); 128 | hasUpdatedSessionInfo = true; 129 | std::cout << "SessionInfo updated\n"; 130 | break; 131 | case 3: 132 | std::cout << "Sending climate on command\n"; 133 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT; 134 | TeslaBLE::CarServer::TurnOnClimate(action_message_buffer, &action_message_buffer_size); 135 | break; 136 | case 4: 137 | std::cout << "Sending climate off command\n"; 138 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT; 139 | TeslaBLE::CarServer::TurnOffClimate(action_message_buffer, &action_message_buffer_size); 140 | break; 141 | case 5: 142 | std::cout << "Sending media next track command\n"; 143 | domain = UniversalMessage_Domain_DOMAIN_INFOTAINMENT; 144 | TeslaBLE::CarServer::NextMediaTrack(action_message_buffer, &action_message_buffer_size); 145 | break; 146 | case 6: 147 | std::cout << "Sending media next track command\n"; 148 | domain = UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY; 149 | TeslaBLE::Security::Lock(action_message_buffer, &action_message_buffer_size); 150 | break; 151 | case 7: 152 | std::cout << "Sending media next track command\n"; 153 | domain = UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY; 154 | TeslaBLE::Security::Unlock(action_message_buffer, &action_message_buffer_size); 155 | break; 156 | case 8: 157 | ble.close(); 158 | runLoop = false; 159 | break; 160 | case 9: 161 | session.ExportSessionInfo(UniversalMessage_Domain_DOMAIN_INFOTAINMENT, session_buffer, 162 | &session_size); 163 | TeslaBLE::Common::DumpHexBuffer("SessionInfo: ", session_buffer, session_size); 164 | break; 165 | case 10: 166 | session.ExportSessionInfo(UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY, session_buffer, 167 | &session_size); 168 | TeslaBLE::Common::DumpHexBuffer("SessionInfo: ", session_buffer, session_size); 169 | break; 170 | default: 171 | std::cout << "Please select a valid command.\n"; 172 | break; 173 | } 174 | 175 | if (whitelist_size > 0) { 176 | ble.send((char *) whitelist_buffer, whitelist_size); 177 | whitelist_size = 0; 178 | } 179 | 180 | if (action_message_buffer_size > 0) { 181 | size_t output_message_buffer_size = 0; 182 | unsigned char output_message_buffer[UniversalMessage_RoutableMessage_size]; 183 | 184 | session.BuildRoutableMessage(domain, action_message_buffer, 185 | action_message_buffer_size, output_message_buffer, 186 | &output_message_buffer_size); 187 | 188 | ble.send((char *) output_message_buffer, output_message_buffer_size); 189 | action_message_buffer_size = 0; 190 | } 191 | } 192 | 193 | return 0; 194 | } 195 | -------------------------------------------------------------------------------- /examples/simple/include/ble.h: -------------------------------------------------------------------------------- 1 | #ifndef BLE_H 2 | #define BLE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace TeslaBLE { 10 | class BLE { 11 | private: 12 | size_t MAX_MESSAGE_SIZE = 1024; 13 | size_t current_message_size = 0; 14 | std::vector message_buffer; 15 | std::function message_handler; 16 | SimpleBLE::Peripheral peripheral; 17 | bool debug_enabled = false; 18 | 19 | void handleMessage(std::vector message); 20 | 21 | void callback(SimpleBLE::ByteArray payload); 22 | 23 | public: 24 | explicit BLE(bool debug_enabled) { 25 | this->debug_enabled = debug_enabled; 26 | } 27 | 28 | SimpleBLE::Peripheral search(const unsigned char *vin); 29 | 30 | int connect(unsigned char *vin); 31 | 32 | void registerMessageHandler(std::function message_handler); 33 | 34 | void send(char *buffer, size_t buffer_size); 35 | 36 | void close(); 37 | }; 38 | } // TeslaBLE 39 | 40 | #endif //BLE_H 41 | -------------------------------------------------------------------------------- /examples/simple/src/ble.cpp: -------------------------------------------------------------------------------- 1 | #include "ble.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "pb_decode.h" 9 | 10 | auto CAR_SERVICE_UUID = "00000211-b2d1-43f0-9b88-960cebf8b91e"; 11 | auto CAR_WRITE_CHAR_UUID = "00000212-b2d1-43f0-9b88-960cebf8b91e"; 12 | auto CAR_READ_CHAR_UUID = "00000213-b2d1-43f0-9b88-960cebf8b91e"; 13 | 14 | namespace TeslaBLE { 15 | void BLE::handleMessage(std::vector message) { 16 | if (this->debug_enabled) { 17 | TeslaBLE::Common::DumpHexBuffer("RX: ", message.data(), message.size()); 18 | } 19 | 20 | UniversalMessage_RoutableMessage output_message = UniversalMessage_RoutableMessage_init_zero; 21 | Common::DecodeRoutableMessage(message.data(), message.size(), &output_message); 22 | 23 | current_message_size = 0; 24 | this->message_buffer.clear(); 25 | this->message_buffer.resize(0); 26 | this->message_buffer.shrink_to_fit(); 27 | 28 | this->message_handler(output_message); 29 | } 30 | 31 | void BLE::callback(SimpleBLE::ByteArray payload) { 32 | pb_byte_t input_buffer[payload.size()]; 33 | memcpy(input_buffer, payload.data(), payload.size()); 34 | 35 | const size_t size = TeslaBLE::Common::ExtractLength(input_buffer); 36 | if (current_message_size == 0 && size > payload.size()) { 37 | message_buffer.clear(); 38 | message_buffer.resize(size); 39 | message_buffer.shrink_to_fit(); 40 | current_message_size = 0; 41 | 42 | size_t payload_size = payload.size() - 2; 43 | memcpy(message_buffer.data(), input_buffer + 2, payload_size); 44 | current_message_size = payload_size; 45 | return; 46 | } 47 | 48 | if (current_message_size > 0) { 49 | memcpy(message_buffer.data() + current_message_size, input_buffer, payload.size()); 50 | current_message_size = current_message_size + payload.size(); 51 | this->handleMessage(message_buffer); 52 | return; 53 | } 54 | 55 | message_buffer.clear(); 56 | message_buffer.resize(size); 57 | message_buffer.shrink_to_fit(); 58 | current_message_size = 0; 59 | 60 | size_t payload_size = payload.size() - 2; 61 | memcpy(message_buffer.data(), input_buffer + 2, payload_size); 62 | this->handleMessage(message_buffer); 63 | } 64 | 65 | int BLE::connect(unsigned char *vin) { 66 | const auto adapters = SimpleBLE::Adapter::get_adapters(); 67 | auto adapter = adapters[0]; 68 | 69 | printf("Using adapter MAC: %s\n", adapter.address().c_str()); 70 | 71 | char identifier[21]; 72 | int result = TeslaBLE::Common::calculateIdentifier(vin, identifier); 73 | if (result != ResultCode::SUCCESS) { 74 | return result; 75 | } 76 | 77 | printf("Connecting to Tesla (%s)...\n", identifier); 78 | adapter.scan_for(5000); 79 | auto peripherals = adapter.scan_get_results(); 80 | 81 | for (auto peripheral: peripherals) { 82 | if (peripheral.identifier() == identifier) { 83 | peripheral.connect(); 84 | printf("Found Tesla \"%s\"\n", peripheral.identifier().c_str()); 85 | printf("MAC: %s", peripheral.address().c_str()); 86 | peripheral.indicate(CAR_SERVICE_UUID, CAR_READ_CHAR_UUID, [this](SimpleBLE::ByteArray payload) { 87 | this->callback(payload); 88 | }); 89 | this->peripheral = peripheral; 90 | return 0; 91 | } 92 | } 93 | 94 | printf("Found Tesla not found \"%s\"\n", identifier); 95 | return 1; 96 | } 97 | 98 | void BLE::registerMessageHandler(std::function message_handler) { 99 | this->message_handler = std::move(message_handler); 100 | } 101 | 102 | void BLE::send(char *buffer, size_t buffer_size) { 103 | SimpleBLE::ByteArray payload = SimpleBLE::ByteArray((char *) buffer, buffer_size); 104 | this->peripheral.write_request(CAR_SERVICE_UUID, CAR_WRITE_CHAR_UUID, payload); 105 | } 106 | 107 | void BLE::close() { 108 | this->peripheral.unsubscribe(CAR_SERVICE_UUID, CAR_READ_CHAR_UUID); 109 | this->peripheral.disconnect(); 110 | } 111 | } // TeslaBLE 112 | -------------------------------------------------------------------------------- /include/authenticator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TeslaBLE © 2024 by Pascal Matthiesen 3 | */ 4 | 5 | #ifndef TESLA_BLE_AUTHENTICATOR_H_INCLUDED 6 | #define TESLA_BLE_AUTHENTICATOR_H_INCLUDED 7 | 8 | #ifdef ESP_PLATFORM 9 | #define MBEDTLS_CONFIG_FILE "mbedtls/esp_config.h" 10 | #endif 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "pb.h" 17 | #include "mbedtls/ctr_drbg.h" 18 | #include "mbedtls/ecdh.h" 19 | #include "mbedtls/pk.h" 20 | #include "mbedtls/sha1.h" 21 | 22 | namespace TeslaBLE { 23 | class Authenticator { 24 | mbedtls_pk_context private_key_context_{}; 25 | mbedtls_ecdh_context ecdh_context_{}; 26 | mbedtls_ctr_drbg_context drbg_context_{}; 27 | 28 | size_t public_key_size_ = 0; 29 | unsigned char public_key_[65]{}; 30 | unsigned char nonce_[12] = {}; 31 | bool private_key_loaded_ = false; 32 | std::map shared_secrets_; 33 | 34 | int GeneratePublicKey(); 35 | 36 | void UpdateNonce(); 37 | 38 | public: 39 | int BuildKeyWhitelistMessage(Keys_Role role, unsigned char *output_buffer, size_t *output_size); 40 | 41 | int CreatePrivateKey(); 42 | 43 | int LoadPrivateKey(mbedtls_pk_context *shared_private_key_context, unsigned char *private_key_buffer, 44 | size_t private_key_size); 45 | 46 | int Encrypt(UniversalMessage_Domain domain, unsigned char *input_buffer, 47 | size_t input_buffer_size, 48 | unsigned char *checksum, unsigned char *output_buffer, size_t output_buffer_size, 49 | size_t *output_size, unsigned char *tag_buffer); 50 | 51 | int LoadPrivateKey(const uint8_t *private_key_buffer, size_t key_size); 52 | 53 | int GetPrivateKey(unsigned char *output_buffer, size_t buffer_size, size_t *output_size); 54 | 55 | int LoadTeslaPublicKey(UniversalMessage_Domain domain, const uint8_t *public_key_buffer, 56 | size_t public_key_size); 57 | 58 | void GetNonce(unsigned char *nonce); 59 | 60 | void GetPublicKey(unsigned char *output_buffer, pb_size_t *output_size); 61 | 62 | void Cleanup(); 63 | }; 64 | } // namespace TeslaBLE 65 | #endif // TESLA_BLE_AUTHENTICATOR_H_INCLUDED 66 | -------------------------------------------------------------------------------- /include/carserver.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TeslaBLE © 2024 by Pascal Matthiesen 3 | */ 4 | 5 | #ifndef TESLA_BLE_CARSERVER_H 6 | #define TESLA_BLE_CARSERVER_H 7 | #include 8 | #include 9 | 10 | namespace TeslaBLE { 11 | class CarServer { 12 | static int BuildActionMessage( 13 | CarServer_Action *car_server_action, unsigned char *buffer, size_t *buffer_size); 14 | 15 | static int ToggleClimate(bool status, unsigned char *buffer, size_t *buffer_size); 16 | 17 | public: 18 | static int TurnOnClimate(unsigned char *buffer, size_t *buffer_size); 19 | 20 | static int TurnOffClimate(unsigned char *buffer, size_t *buffer_size); 21 | 22 | static int NextMediaTrack(unsigned char *buffer, size_t *buffer_size); 23 | 24 | static int PlayMedia(unsigned char *buffer, size_t *buffer_size); 25 | 26 | static int SetVolume(float absolute, unsigned char *buffer, size_t *buffer_size); 27 | 28 | static int SetChargingLimit(int32_t percent, unsigned char *buffer, size_t *buffer_size); 29 | 30 | static int Vent(unsigned char *buffer, size_t *buffer_size); 31 | 32 | static int StartCharging(unsigned char *buffer, size_t *buffer_size); 33 | 34 | static int StopCharging(unsigned char *buffer, size_t *buffer_size); 35 | 36 | static int OpenChargePort(unsigned char *buffer, size_t *buffer_size); 37 | 38 | static int CloseChargePort(unsigned char *buffer, size_t *buffer_size); 39 | }; 40 | } // TeslaBLE 41 | 42 | #endif //TESLA_BLE_CARSERVER_H 43 | -------------------------------------------------------------------------------- /include/common.pb.h: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb header */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #ifndef PB_CARSERVER_COMMON_PB_H_INCLUDED 5 | #define PB_CARSERVER_COMMON_PB_H_INCLUDED 6 | #include 7 | 8 | #if PB_PROTO_HEADER_VERSION != 40 9 | #error Regenerate this file with the current version of nanopb generator. 10 | #endif 11 | 12 | /* Enum definitions */ 13 | typedef enum _CarServer_Invalid { 14 | CarServer_Invalid_INVALID = 0 15 | } CarServer_Invalid; 16 | 17 | /* Struct definitions */ 18 | typedef struct _CarServer_Void { 19 | char dummy_field; 20 | } CarServer_Void; 21 | 22 | typedef struct _CarServer_LatLong { 23 | float latitude; 24 | float longitude; 25 | } CarServer_LatLong; 26 | 27 | typedef struct _CarServer_PreconditioningTimes { 28 | pb_size_t which_times; 29 | union { 30 | CarServer_Void all_week; 31 | CarServer_Void weekdays; 32 | } times; 33 | } CarServer_PreconditioningTimes; 34 | 35 | typedef struct _CarServer_OffPeakChargingTimes { 36 | pb_size_t which_times; 37 | union { 38 | CarServer_Void all_week; 39 | CarServer_Void weekdays; 40 | } times; 41 | } CarServer_OffPeakChargingTimes; 42 | 43 | 44 | #ifdef __cplusplus 45 | extern "C" { 46 | #endif 47 | 48 | /* Helper constants for enums */ 49 | #define _CarServer_Invalid_MIN CarServer_Invalid_INVALID 50 | #define _CarServer_Invalid_MAX CarServer_Invalid_INVALID 51 | #define _CarServer_Invalid_ARRAYSIZE ((CarServer_Invalid)(CarServer_Invalid_INVALID+1)) 52 | 53 | 54 | 55 | 56 | 57 | 58 | /* Initializer values for message structs */ 59 | #define CarServer_Void_init_default {0} 60 | #define CarServer_LatLong_init_default {0, 0} 61 | #define CarServer_PreconditioningTimes_init_default {0, {CarServer_Void_init_default}} 62 | #define CarServer_OffPeakChargingTimes_init_default {0, {CarServer_Void_init_default}} 63 | #define CarServer_Void_init_zero {0} 64 | #define CarServer_LatLong_init_zero {0, 0} 65 | #define CarServer_PreconditioningTimes_init_zero {0, {CarServer_Void_init_zero}} 66 | #define CarServer_OffPeakChargingTimes_init_zero {0, {CarServer_Void_init_zero}} 67 | 68 | /* Field tags (for use in manual encoding/decoding) */ 69 | #define CarServer_LatLong_latitude_tag 1 70 | #define CarServer_LatLong_longitude_tag 2 71 | #define CarServer_PreconditioningTimes_all_week_tag 1 72 | #define CarServer_PreconditioningTimes_weekdays_tag 2 73 | #define CarServer_OffPeakChargingTimes_all_week_tag 1 74 | #define CarServer_OffPeakChargingTimes_weekdays_tag 2 75 | 76 | /* Struct field encoding specification for nanopb */ 77 | #define CarServer_Void_FIELDLIST(X, a) \ 78 | 79 | #define CarServer_Void_CALLBACK NULL 80 | #define CarServer_Void_DEFAULT NULL 81 | 82 | #define CarServer_LatLong_FIELDLIST(X, a) \ 83 | X(a, STATIC, SINGULAR, FLOAT, latitude, 1) \ 84 | X(a, STATIC, SINGULAR, FLOAT, longitude, 2) 85 | #define CarServer_LatLong_CALLBACK NULL 86 | #define CarServer_LatLong_DEFAULT NULL 87 | 88 | #define CarServer_PreconditioningTimes_FIELDLIST(X, a) \ 89 | X(a, STATIC, ONEOF, MESSAGE, (times,all_week,times.all_week), 1) \ 90 | X(a, STATIC, ONEOF, MESSAGE, (times,weekdays,times.weekdays), 2) 91 | #define CarServer_PreconditioningTimes_CALLBACK NULL 92 | #define CarServer_PreconditioningTimes_DEFAULT NULL 93 | #define CarServer_PreconditioningTimes_times_all_week_MSGTYPE CarServer_Void 94 | #define CarServer_PreconditioningTimes_times_weekdays_MSGTYPE CarServer_Void 95 | 96 | #define CarServer_OffPeakChargingTimes_FIELDLIST(X, a) \ 97 | X(a, STATIC, ONEOF, MESSAGE, (times,all_week,times.all_week), 1) \ 98 | X(a, STATIC, ONEOF, MESSAGE, (times,weekdays,times.weekdays), 2) 99 | #define CarServer_OffPeakChargingTimes_CALLBACK NULL 100 | #define CarServer_OffPeakChargingTimes_DEFAULT NULL 101 | #define CarServer_OffPeakChargingTimes_times_all_week_MSGTYPE CarServer_Void 102 | #define CarServer_OffPeakChargingTimes_times_weekdays_MSGTYPE CarServer_Void 103 | 104 | extern const pb_msgdesc_t CarServer_Void_msg; 105 | extern const pb_msgdesc_t CarServer_LatLong_msg; 106 | extern const pb_msgdesc_t CarServer_PreconditioningTimes_msg; 107 | extern const pb_msgdesc_t CarServer_OffPeakChargingTimes_msg; 108 | 109 | /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ 110 | #define CarServer_Void_fields &CarServer_Void_msg 111 | #define CarServer_LatLong_fields &CarServer_LatLong_msg 112 | #define CarServer_PreconditioningTimes_fields &CarServer_PreconditioningTimes_msg 113 | #define CarServer_OffPeakChargingTimes_fields &CarServer_OffPeakChargingTimes_msg 114 | 115 | /* Maximum encoded size of messages (where known) */ 116 | #define CARSERVER_COMMON_PB_H_MAX_SIZE CarServer_LatLong_size 117 | #define CarServer_LatLong_size 10 118 | #define CarServer_OffPeakChargingTimes_size 2 119 | #define CarServer_PreconditioningTimes_size 2 120 | #define CarServer_Void_size 0 121 | 122 | #ifdef __cplusplus 123 | } /* extern "C" */ 124 | #endif 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /include/errors.pb.h: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb header */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #ifndef PB_ERRORS_ERRORS_PB_H_INCLUDED 5 | #define PB_ERRORS_ERRORS_PB_H_INCLUDED 6 | #include 7 | 8 | #if PB_PROTO_HEADER_VERSION != 40 9 | #error Regenerate this file with the current version of nanopb generator. 10 | #endif 11 | 12 | /* Enum definitions */ 13 | typedef enum _Errors_GenericError_E { 14 | Errors_GenericError_E_GENERICERROR_NONE = 0, 15 | Errors_GenericError_E_GENERICERROR_UNKNOWN = 1, 16 | Errors_GenericError_E_GENERICERROR_CLOSURES_OPEN = 2, 17 | Errors_GenericError_E_GENERICERROR_ALREADY_ON = 3, 18 | Errors_GenericError_E_GENERICERROR_DISABLED_FOR_USER_COMMAND = 4, 19 | Errors_GenericError_E_GENERICERROR_VEHICLE_NOT_IN_PARK = 5, 20 | Errors_GenericError_E_GENERICERROR_UNAUTHORIZED = 6, 21 | Errors_GenericError_E_GENERICERROR_NOT_ALLOWED_OVER_TRANSPORT = 7 22 | } Errors_GenericError_E; 23 | 24 | /* Struct definitions */ 25 | typedef struct _Errors_NominalError { 26 | Errors_GenericError_E genericError; 27 | } Errors_NominalError; 28 | 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | /* Helper constants for enums */ 35 | #define _Errors_GenericError_E_MIN Errors_GenericError_E_GENERICERROR_NONE 36 | #define _Errors_GenericError_E_MAX Errors_GenericError_E_GENERICERROR_NOT_ALLOWED_OVER_TRANSPORT 37 | #define _Errors_GenericError_E_ARRAYSIZE ((Errors_GenericError_E)(Errors_GenericError_E_GENERICERROR_NOT_ALLOWED_OVER_TRANSPORT+1)) 38 | 39 | #define Errors_NominalError_genericError_ENUMTYPE Errors_GenericError_E 40 | 41 | 42 | /* Initializer values for message structs */ 43 | #define Errors_NominalError_init_default {_Errors_GenericError_E_MIN} 44 | #define Errors_NominalError_init_zero {_Errors_GenericError_E_MIN} 45 | 46 | /* Field tags (for use in manual encoding/decoding) */ 47 | #define Errors_NominalError_genericError_tag 1 48 | 49 | /* Struct field encoding specification for nanopb */ 50 | #define Errors_NominalError_FIELDLIST(X, a) \ 51 | X(a, STATIC, SINGULAR, UENUM, genericError, 1) 52 | #define Errors_NominalError_CALLBACK NULL 53 | #define Errors_NominalError_DEFAULT NULL 54 | 55 | extern const pb_msgdesc_t Errors_NominalError_msg; 56 | 57 | /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ 58 | #define Errors_NominalError_fields &Errors_NominalError_msg 59 | 60 | /* Maximum encoded size of messages (where known) */ 61 | #define ERRORS_ERRORS_PB_H_MAX_SIZE Errors_NominalError_size 62 | #define Errors_NominalError_size 2 63 | 64 | #ifdef __cplusplus 65 | } /* extern "C" */ 66 | #endif 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /include/keys.pb.h: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb header */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #ifndef PB_KEYS_KEYS_PB_H_INCLUDED 5 | #define PB_KEYS_KEYS_PB_H_INCLUDED 6 | #include 7 | 8 | #if PB_PROTO_HEADER_VERSION != 40 9 | #error Regenerate this file with the current version of nanopb generator. 10 | #endif 11 | 12 | /* Enum definitions */ 13 | typedef enum _Keys_Role { 14 | Keys_Role_ROLE_NONE = 0, 15 | Keys_Role_ROLE_SERVICE = 1, 16 | Keys_Role_ROLE_OWNER = 2, 17 | Keys_Role_ROLE_DRIVER = 3, 18 | Keys_Role_ROLE_FM = 4, 19 | Keys_Role_ROLE_VEHICLE_MONITOR = 5, 20 | Keys_Role_ROLE_CHARGING_MANAGER = 6 21 | } Keys_Role; 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | /* Helper constants for enums */ 28 | #define _Keys_Role_MIN Keys_Role_ROLE_NONE 29 | #define _Keys_Role_MAX Keys_Role_ROLE_CHARGING_MANAGER 30 | #define _Keys_Role_ARRAYSIZE ((Keys_Role)(Keys_Role_ROLE_CHARGING_MANAGER+1)) 31 | 32 | 33 | #ifdef __cplusplus 34 | } /* extern "C" */ 35 | #endif 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /include/metadata.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TeslaBLE © 2024 by Pascal Matthiesen 3 | */ 4 | 5 | #ifndef TESLA_BLE_METADATA_H 6 | #define TESLA_BLE_METADATA_H 7 | 8 | #ifdef ESP_PLATFORM 9 | #define MBEDTLS_CONFIG_FILE "mbedtls/esp_config.h" 10 | #endif 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | namespace TeslaBLE { 18 | class MetaData { 19 | mbedtls_sha256_context sha256_context_{}; 20 | uint8_t last_tag = 0; 21 | 22 | int Add(unsigned char signatures_tag, unsigned char *value, unsigned char value_length); 23 | 24 | int AddUint32(unsigned char signatures_tag, uint32_t value); 25 | 26 | public: 27 | int Start(); 28 | 29 | int BuildMetadata(UniversalMessage_Domain destination, Signatures_SignatureType method, 30 | unsigned char *vin, 31 | uint32_t expiresAt, uint32_t counter, unsigned char *epoch); 32 | 33 | void Checksum(unsigned char *output, unsigned char end_tag); 34 | }; 35 | } // TeslaBLE 36 | 37 | #endif //TESLA_BLE_METADATA_H 38 | -------------------------------------------------------------------------------- /include/security.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TeslaBLE © 2024 by Pascal Matthiesen 3 | */ 4 | 5 | #ifndef TESLA_BLE_SECURITY_H 6 | #define TESLA_BLE_SECURITY_H 7 | 8 | #include 9 | 10 | namespace TeslaBLE { 11 | class Security { 12 | static int BuildUnsignedMessage(const VCSEC_UnsignedMessage *unsigned_message, unsigned char *buffer, 13 | size_t *buffer_size); 14 | 15 | public: 16 | static int Unlock(unsigned char *buffer, size_t *buffer_size); 17 | 18 | static int Lock(unsigned char *buffer, size_t *buffer_size); 19 | 20 | static int Wake(unsigned char *buffer, size_t *buffer_size); 21 | }; 22 | } // TeslaBLE 23 | 24 | #endif //TESLA_BLE_SECURITY_H 25 | -------------------------------------------------------------------------------- /include/session.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TeslaBLE © 2024 by Pascal Matthiesen 3 | */ 4 | 5 | #ifndef TESLA_BLE_SESSION_H 6 | #define TESLA_BLE_SESSION_H 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | namespace TeslaBLE { 15 | class Session { 16 | std::map time_zeros_; 17 | std::map counters_; 18 | std::map epochs_; 19 | std::map clock_times_; 20 | std::map car_keys; 21 | std::map car_key_sizes; 22 | 23 | unsigned char vin_[17]{}; 24 | unsigned char routing_address_[16]{}; 25 | bool has_valid_session_info = false; 26 | 27 | MetaData meta_data_ = MetaData{}; 28 | Authenticator *authenticator_ = nullptr; 29 | 30 | public: 31 | void LoadAuthenticator(Authenticator *authenticator); 32 | 33 | int LoadPrivateKey(unsigned char *private_key, size_t private_key_size); 34 | 35 | void LoadPrivateKeyContext(mbedtls_pk_context *shared_private_key_context_); 36 | 37 | void GenerateRoutingAddress(); 38 | 39 | void SetRoutingAddress(unsigned char *routing_address); 40 | 41 | int UpdateSessionInfo(UniversalMessage_Domain domain, unsigned char *session_info_message, 42 | size_t session_info_length); 43 | 44 | int BuildRoutableMessage(UniversalMessage_Domain domain, unsigned char *action_message_buffer, 45 | size_t action_message_buffer_size, unsigned char *output_buffer, 46 | size_t *output_buffer_size); 47 | 48 | int BuildActionMessage(UniversalMessage_Domain domain, const CarServer_VehicleAction *vehicle_action, 49 | unsigned char *buffer, size_t *buffer_size); 50 | 51 | int BuildRequestSessionInfoMessage(UniversalMessage_Domain domain, 52 | unsigned char *output_buffer, size_t *output_length); 53 | 54 | uint32_t ExpiresAt(UniversalMessage_Domain domain, uint8_t expiresInSeconds); 55 | 56 | uint32_t Counter(UniversalMessage_Domain domain); 57 | 58 | void Epoch(UniversalMessage_Domain domain, unsigned char *output_buffer); 59 | 60 | void SetVIN(unsigned char *vin); 61 | 62 | int ExportSessionInfo(UniversalMessage_Domain domain, unsigned char *output_buffer, size_t *output_size); 63 | }; 64 | } // TeslaBLE 65 | 66 | 67 | #endif //TESLA_BLE_SESSION_H 68 | -------------------------------------------------------------------------------- /include/shared.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TeslaBLE © 2024 by Pascal Matthiesen 3 | */ 4 | 5 | #ifndef TESLA_BLE_SHARED_H 6 | #define TESLA_BLE_SHARED_H 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | 17 | enum ResultCode : int { 18 | SUCCESS = 0, 19 | ERROR = 1, 20 | TAG_OUT_OF_ORDER = 10, 21 | TAG_VALUE_EMPTY = 11, 22 | TAG_VALUE_TOO_LONG = 12, 23 | MBEDTLS_ERROR = 20, 24 | NANOPB_ENCODE_ERROR = 30, 25 | NANOPB_DECODE_ERROR = 31, 26 | PRIVATE_KEY_NOT_LOADED = 40, 27 | SESSION_INFO_NOT_LOADED = 50, 28 | SESSION_INFO_KEY_NOT_WHITELISTED = 51, 29 | }; 30 | 31 | namespace TeslaBLE { 32 | class Common { 33 | public: 34 | static unsigned char *HexStrToUint8(const char *string); 35 | 36 | static void DumpHexBuffer(const char *title, unsigned char *buf, size_t len); 37 | 38 | // static void ParseVIN(const std::string &vin, char *buffer); 39 | 40 | static void PrependLength(unsigned char *input_buffer, size_t input_buffer_length, unsigned char *output_buffer, 41 | size_t *output_buffer_size); 42 | 43 | static int calculateIdentifier(unsigned char *vin, char *output); 44 | 45 | static size_t ExtractLength(unsigned char *input_buffer); 46 | 47 | static void GenerateUUID(unsigned char *output_buffer, uint16_t *output_size); 48 | 49 | static int DecodeRoutableMessage(unsigned char *buffer, size_t buffer_size, 50 | UniversalMessage_RoutableMessage *output_message); 51 | 52 | static int DecodeFromVCSECMessage(unsigned char *buffer, size_t buffer_size, 53 | VCSEC_FromVCSECMessage *output_message); 54 | 55 | static int EncodeRoutableMessage(UniversalMessage_RoutableMessage routable_message, 56 | unsigned char *output_buffer, 57 | size_t *output_size); 58 | 59 | static void ResultCodeToMessage(int result_code); 60 | 61 | static void ErrorCodeToMessage(UniversalMessage_MessageFault_E message_fault); 62 | 63 | static void OperationStatusToMessage(UniversalMessage_OperationStatus_E operation_status); 64 | 65 | static void PrintErrorFromMbedTlsErrorCode(int result); 66 | }; 67 | } 68 | 69 | 70 | #endif //TESLA_BLE_SHARED_H 71 | -------------------------------------------------------------------------------- /include/signatures.pb.h: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb header */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #ifndef PB_SIGNATURES_SIGNATURES_PB_H_INCLUDED 5 | #define PB_SIGNATURES_SIGNATURES_PB_H_INCLUDED 6 | #include 7 | 8 | #if PB_PROTO_HEADER_VERSION != 40 9 | #error Regenerate this file with the current version of nanopb generator. 10 | #endif 11 | 12 | /* Enum definitions */ 13 | typedef enum _Signatures_Tag { 14 | Signatures_Tag_TAG_SIGNATURE_TYPE = 0, 15 | Signatures_Tag_TAG_DOMAIN = 1, 16 | Signatures_Tag_TAG_PERSONALIZATION = 2, 17 | Signatures_Tag_TAG_EPOCH = 3, 18 | Signatures_Tag_TAG_EXPIRES_AT = 4, 19 | Signatures_Tag_TAG_COUNTER = 5, 20 | Signatures_Tag_TAG_CHALLENGE = 6, 21 | Signatures_Tag_TAG_FLAGS = 7, 22 | Signatures_Tag_TAG_END = 255 23 | } Signatures_Tag; 24 | 25 | typedef enum _Signatures_SignatureType { 26 | Signatures_SignatureType_SIGNATURE_TYPE_AES_GCM = 0, 27 | Signatures_SignatureType_SIGNATURE_TYPE_AES_GCM_PERSONALIZED = 5, 28 | Signatures_SignatureType_SIGNATURE_TYPE_HMAC = 6, 29 | Signatures_SignatureType_SIGNATURE_TYPE_HMAC_PERSONALIZED = 8 30 | } Signatures_SignatureType; 31 | 32 | typedef enum _Signatures_Session_Info_Status { 33 | Signatures_Session_Info_Status_SESSION_INFO_STATUS_OK = 0, 34 | Signatures_Session_Info_Status_SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST = 1 35 | } Signatures_Session_Info_Status; 36 | 37 | /* Struct definitions */ 38 | typedef PB_BYTES_ARRAY_T(65) Signatures_KeyIdentity_public_key_t; 39 | typedef struct _Signatures_KeyIdentity { 40 | pb_size_t which_identity_type; 41 | union { 42 | Signatures_KeyIdentity_public_key_t public_key; 43 | } identity_type; 44 | } Signatures_KeyIdentity; 45 | 46 | typedef PB_BYTES_ARRAY_T(16) Signatures_AES_GCM_Personalized_Signature_Data_epoch_t; 47 | typedef PB_BYTES_ARRAY_T(12) Signatures_AES_GCM_Personalized_Signature_Data_nonce_t; 48 | typedef PB_BYTES_ARRAY_T(16) Signatures_AES_GCM_Personalized_Signature_Data_tag_t; 49 | typedef struct _Signatures_AES_GCM_Personalized_Signature_Data { 50 | Signatures_AES_GCM_Personalized_Signature_Data_epoch_t epoch; 51 | Signatures_AES_GCM_Personalized_Signature_Data_nonce_t nonce; 52 | uint32_t counter; 53 | uint32_t expires_at; 54 | Signatures_AES_GCM_Personalized_Signature_Data_tag_t tag; 55 | } Signatures_AES_GCM_Personalized_Signature_Data; 56 | 57 | typedef PB_BYTES_ARRAY_T(34) Signatures_HMAC_Signature_Data_tag_t; 58 | typedef struct _Signatures_HMAC_Signature_Data { 59 | Signatures_HMAC_Signature_Data_tag_t tag; 60 | } Signatures_HMAC_Signature_Data; 61 | 62 | typedef PB_BYTES_ARRAY_T(16) Signatures_HMAC_Personalized_Signature_Data_epoch_t; 63 | typedef PB_BYTES_ARRAY_T(16) Signatures_HMAC_Personalized_Signature_Data_tag_t; 64 | typedef struct _Signatures_HMAC_Personalized_Signature_Data { 65 | Signatures_HMAC_Personalized_Signature_Data_epoch_t epoch; 66 | uint32_t counter; 67 | uint32_t expires_at; 68 | Signatures_HMAC_Personalized_Signature_Data_tag_t tag; 69 | } Signatures_HMAC_Personalized_Signature_Data; 70 | 71 | typedef struct _Signatures_SignatureData { 72 | bool has_signer_identity; 73 | Signatures_KeyIdentity signer_identity; 74 | pb_size_t which_sig_type; 75 | union { 76 | Signatures_AES_GCM_Personalized_Signature_Data AES_GCM_Personalized_data; 77 | Signatures_HMAC_Signature_Data session_info_tag; 78 | Signatures_HMAC_Personalized_Signature_Data HMAC_Personalized_data; 79 | } sig_type; 80 | } Signatures_SignatureData; 81 | 82 | typedef struct _Signatures_GetSessionInfoRequest { 83 | bool has_key_identity; 84 | Signatures_KeyIdentity key_identity; 85 | } Signatures_GetSessionInfoRequest; 86 | 87 | typedef PB_BYTES_ARRAY_T(65) Signatures_SessionInfo_publicKey_t; 88 | typedef struct _Signatures_SessionInfo { 89 | uint32_t counter; 90 | Signatures_SessionInfo_publicKey_t publicKey; 91 | pb_byte_t epoch[16]; 92 | uint32_t clock_time; 93 | Signatures_Session_Info_Status status; 94 | } Signatures_SessionInfo; 95 | 96 | 97 | #ifdef __cplusplus 98 | extern "C" { 99 | #endif 100 | 101 | /* Helper constants for enums */ 102 | #define _Signatures_Tag_MIN Signatures_Tag_TAG_SIGNATURE_TYPE 103 | #define _Signatures_Tag_MAX Signatures_Tag_TAG_END 104 | #define _Signatures_Tag_ARRAYSIZE ((Signatures_Tag)(Signatures_Tag_TAG_END+1)) 105 | 106 | #define _Signatures_SignatureType_MIN Signatures_SignatureType_SIGNATURE_TYPE_AES_GCM 107 | #define _Signatures_SignatureType_MAX Signatures_SignatureType_SIGNATURE_TYPE_HMAC_PERSONALIZED 108 | #define _Signatures_SignatureType_ARRAYSIZE ((Signatures_SignatureType)(Signatures_SignatureType_SIGNATURE_TYPE_HMAC_PERSONALIZED+1)) 109 | 110 | #define _Signatures_Session_Info_Status_MIN Signatures_Session_Info_Status_SESSION_INFO_STATUS_OK 111 | #define _Signatures_Session_Info_Status_MAX Signatures_Session_Info_Status_SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST 112 | #define _Signatures_Session_Info_Status_ARRAYSIZE ((Signatures_Session_Info_Status)(Signatures_Session_Info_Status_SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST+1)) 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | #define Signatures_SessionInfo_status_ENUMTYPE Signatures_Session_Info_Status 121 | 122 | 123 | /* Initializer values for message structs */ 124 | #define Signatures_KeyIdentity_init_default {0, {{0, {0}}}} 125 | #define Signatures_AES_GCM_Personalized_Signature_Data_init_default {{0, {0}}, {0, {0}}, 0, 0, {0, {0}}} 126 | #define Signatures_HMAC_Signature_Data_init_default {{0, {0}}} 127 | #define Signatures_HMAC_Personalized_Signature_Data_init_default {{0, {0}}, 0, 0, {0, {0}}} 128 | #define Signatures_SignatureData_init_default {false, Signatures_KeyIdentity_init_default, 0, {Signatures_AES_GCM_Personalized_Signature_Data_init_default}} 129 | #define Signatures_GetSessionInfoRequest_init_default {false, Signatures_KeyIdentity_init_default} 130 | #define Signatures_SessionInfo_init_default {0, {0, {0}}, {0}, 0, _Signatures_Session_Info_Status_MIN} 131 | #define Signatures_KeyIdentity_init_zero {0, {{0, {0}}}} 132 | #define Signatures_AES_GCM_Personalized_Signature_Data_init_zero {{0, {0}}, {0, {0}}, 0, 0, {0, {0}}} 133 | #define Signatures_HMAC_Signature_Data_init_zero {{0, {0}}} 134 | #define Signatures_HMAC_Personalized_Signature_Data_init_zero {{0, {0}}, 0, 0, {0, {0}}} 135 | #define Signatures_SignatureData_init_zero {false, Signatures_KeyIdentity_init_zero, 0, {Signatures_AES_GCM_Personalized_Signature_Data_init_zero}} 136 | #define Signatures_GetSessionInfoRequest_init_zero {false, Signatures_KeyIdentity_init_zero} 137 | #define Signatures_SessionInfo_init_zero {0, {0, {0}}, {0}, 0, _Signatures_Session_Info_Status_MIN} 138 | 139 | /* Field tags (for use in manual encoding/decoding) */ 140 | #define Signatures_KeyIdentity_public_key_tag 1 141 | #define Signatures_AES_GCM_Personalized_Signature_Data_epoch_tag 1 142 | #define Signatures_AES_GCM_Personalized_Signature_Data_nonce_tag 2 143 | #define Signatures_AES_GCM_Personalized_Signature_Data_counter_tag 3 144 | #define Signatures_AES_GCM_Personalized_Signature_Data_expires_at_tag 4 145 | #define Signatures_AES_GCM_Personalized_Signature_Data_tag_tag 5 146 | #define Signatures_HMAC_Signature_Data_tag_tag 1 147 | #define Signatures_HMAC_Personalized_Signature_Data_epoch_tag 1 148 | #define Signatures_HMAC_Personalized_Signature_Data_counter_tag 2 149 | #define Signatures_HMAC_Personalized_Signature_Data_expires_at_tag 3 150 | #define Signatures_HMAC_Personalized_Signature_Data_tag_tag 4 151 | #define Signatures_SignatureData_signer_identity_tag 1 152 | #define Signatures_SignatureData_AES_GCM_Personalized_data_tag 5 153 | #define Signatures_SignatureData_session_info_tag_tag 6 154 | #define Signatures_SignatureData_HMAC_Personalized_data_tag 8 155 | #define Signatures_GetSessionInfoRequest_key_identity_tag 1 156 | #define Signatures_SessionInfo_counter_tag 1 157 | #define Signatures_SessionInfo_publicKey_tag 2 158 | #define Signatures_SessionInfo_epoch_tag 3 159 | #define Signatures_SessionInfo_clock_time_tag 4 160 | #define Signatures_SessionInfo_status_tag 5 161 | 162 | /* Struct field encoding specification for nanopb */ 163 | #define Signatures_KeyIdentity_FIELDLIST(X, a) \ 164 | X(a, STATIC, ONEOF, BYTES, (identity_type,public_key,identity_type.public_key), 1) 165 | #define Signatures_KeyIdentity_CALLBACK NULL 166 | #define Signatures_KeyIdentity_DEFAULT NULL 167 | 168 | #define Signatures_AES_GCM_Personalized_Signature_Data_FIELDLIST(X, a) \ 169 | X(a, STATIC, SINGULAR, BYTES, epoch, 1) \ 170 | X(a, STATIC, SINGULAR, BYTES, nonce, 2) \ 171 | X(a, STATIC, SINGULAR, UINT32, counter, 3) \ 172 | X(a, STATIC, SINGULAR, FIXED32, expires_at, 4) \ 173 | X(a, STATIC, SINGULAR, BYTES, tag, 5) 174 | #define Signatures_AES_GCM_Personalized_Signature_Data_CALLBACK NULL 175 | #define Signatures_AES_GCM_Personalized_Signature_Data_DEFAULT NULL 176 | 177 | #define Signatures_HMAC_Signature_Data_FIELDLIST(X, a) \ 178 | X(a, STATIC, SINGULAR, BYTES, tag, 1) 179 | #define Signatures_HMAC_Signature_Data_CALLBACK NULL 180 | #define Signatures_HMAC_Signature_Data_DEFAULT NULL 181 | 182 | #define Signatures_HMAC_Personalized_Signature_Data_FIELDLIST(X, a) \ 183 | X(a, STATIC, SINGULAR, BYTES, epoch, 1) \ 184 | X(a, STATIC, SINGULAR, UINT32, counter, 2) \ 185 | X(a, STATIC, SINGULAR, FIXED32, expires_at, 3) \ 186 | X(a, STATIC, SINGULAR, BYTES, tag, 4) 187 | #define Signatures_HMAC_Personalized_Signature_Data_CALLBACK NULL 188 | #define Signatures_HMAC_Personalized_Signature_Data_DEFAULT NULL 189 | 190 | #define Signatures_SignatureData_FIELDLIST(X, a) \ 191 | X(a, STATIC, OPTIONAL, MESSAGE, signer_identity, 1) \ 192 | X(a, STATIC, ONEOF, MESSAGE, (sig_type,AES_GCM_Personalized_data,sig_type.AES_GCM_Personalized_data), 5) \ 193 | X(a, STATIC, ONEOF, MESSAGE, (sig_type,session_info_tag,sig_type.session_info_tag), 6) \ 194 | X(a, STATIC, ONEOF, MESSAGE, (sig_type,HMAC_Personalized_data,sig_type.HMAC_Personalized_data), 8) 195 | #define Signatures_SignatureData_CALLBACK NULL 196 | #define Signatures_SignatureData_DEFAULT NULL 197 | #define Signatures_SignatureData_signer_identity_MSGTYPE Signatures_KeyIdentity 198 | #define Signatures_SignatureData_sig_type_AES_GCM_Personalized_data_MSGTYPE Signatures_AES_GCM_Personalized_Signature_Data 199 | #define Signatures_SignatureData_sig_type_session_info_tag_MSGTYPE Signatures_HMAC_Signature_Data 200 | #define Signatures_SignatureData_sig_type_HMAC_Personalized_data_MSGTYPE Signatures_HMAC_Personalized_Signature_Data 201 | 202 | #define Signatures_GetSessionInfoRequest_FIELDLIST(X, a) \ 203 | X(a, STATIC, OPTIONAL, MESSAGE, key_identity, 1) 204 | #define Signatures_GetSessionInfoRequest_CALLBACK NULL 205 | #define Signatures_GetSessionInfoRequest_DEFAULT NULL 206 | #define Signatures_GetSessionInfoRequest_key_identity_MSGTYPE Signatures_KeyIdentity 207 | 208 | #define Signatures_SessionInfo_FIELDLIST(X, a) \ 209 | X(a, STATIC, SINGULAR, UINT32, counter, 1) \ 210 | X(a, STATIC, SINGULAR, BYTES, publicKey, 2) \ 211 | X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, epoch, 3) \ 212 | X(a, STATIC, SINGULAR, FIXED32, clock_time, 4) \ 213 | X(a, STATIC, SINGULAR, UENUM, status, 5) 214 | #define Signatures_SessionInfo_CALLBACK NULL 215 | #define Signatures_SessionInfo_DEFAULT NULL 216 | 217 | extern const pb_msgdesc_t Signatures_KeyIdentity_msg; 218 | extern const pb_msgdesc_t Signatures_AES_GCM_Personalized_Signature_Data_msg; 219 | extern const pb_msgdesc_t Signatures_HMAC_Signature_Data_msg; 220 | extern const pb_msgdesc_t Signatures_HMAC_Personalized_Signature_Data_msg; 221 | extern const pb_msgdesc_t Signatures_SignatureData_msg; 222 | extern const pb_msgdesc_t Signatures_GetSessionInfoRequest_msg; 223 | extern const pb_msgdesc_t Signatures_SessionInfo_msg; 224 | 225 | /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ 226 | #define Signatures_KeyIdentity_fields &Signatures_KeyIdentity_msg 227 | #define Signatures_AES_GCM_Personalized_Signature_Data_fields &Signatures_AES_GCM_Personalized_Signature_Data_msg 228 | #define Signatures_HMAC_Signature_Data_fields &Signatures_HMAC_Signature_Data_msg 229 | #define Signatures_HMAC_Personalized_Signature_Data_fields &Signatures_HMAC_Personalized_Signature_Data_msg 230 | #define Signatures_SignatureData_fields &Signatures_SignatureData_msg 231 | #define Signatures_GetSessionInfoRequest_fields &Signatures_GetSessionInfoRequest_msg 232 | #define Signatures_SessionInfo_fields &Signatures_SessionInfo_msg 233 | 234 | /* Maximum encoded size of messages (where known) */ 235 | #define SIGNATURES_SIGNATURES_PB_H_MAX_SIZE Signatures_SignatureData_size 236 | #define Signatures_AES_GCM_Personalized_Signature_Data_size 61 237 | #define Signatures_GetSessionInfoRequest_size 69 238 | #define Signatures_HMAC_Personalized_Signature_Data_size 47 239 | #define Signatures_HMAC_Signature_Data_size 36 240 | #define Signatures_KeyIdentity_size 67 241 | #define Signatures_SessionInfo_size 98 242 | #define Signatures_SignatureData_size 132 243 | 244 | #ifdef __cplusplus 245 | } /* extern "C" */ 246 | #endif 247 | 248 | #endif 249 | -------------------------------------------------------------------------------- /include/universal_message.pb.h: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb header */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #ifndef PB_UNIVERSALMESSAGE_UNIVERSAL_MESSAGE_PB_H_INCLUDED 5 | #define PB_UNIVERSALMESSAGE_UNIVERSAL_MESSAGE_PB_H_INCLUDED 6 | #include 7 | #include "signatures.pb.h" 8 | 9 | #if PB_PROTO_HEADER_VERSION != 40 10 | #error Regenerate this file with the current version of nanopb generator. 11 | #endif 12 | 13 | /* Enum definitions */ 14 | typedef enum _UniversalMessage_Domain { 15 | UniversalMessage_Domain_DOMAIN_BROADCAST = 0, 16 | UniversalMessage_Domain_DOMAIN_VEHICLE_SECURITY = 2, 17 | UniversalMessage_Domain_DOMAIN_INFOTAINMENT = 3 18 | } UniversalMessage_Domain; 19 | 20 | typedef enum _UniversalMessage_OperationStatus_E { 21 | UniversalMessage_OperationStatus_E_OPERATIONSTATUS_OK = 0, 22 | UniversalMessage_OperationStatus_E_OPERATIONSTATUS_WAIT = 1, 23 | UniversalMessage_OperationStatus_E_OPERATIONSTATUS_ERROR = 2 24 | } UniversalMessage_OperationStatus_E; 25 | 26 | typedef enum _UniversalMessage_MessageFault_E { 27 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_NONE = 0, /* Request succeeded. */ 28 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_BUSY = 1, /* Required vehicle subsystem is busy. Try again. */ 29 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_TIMEOUT = 2, /* Vehicle subsystem did not respond. Try again. */ 30 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_UNKNOWN_KEY_ID = 3, /* Vehicle did not recognize the key used to authorize command. Make sure your key is paired with the vehicle. */ 31 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INACTIVE_KEY = 4, /* Key used to authorize command has been disabled. */ 32 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INVALID_SIGNATURE = 5, /* Command signature/MAC is incorrect. Use included session info to update session and try again. */ 33 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INVALID_TOKEN_OR_COUNTER = 6, /* Command anti-replay counter has been used before. Use included session info to update session and try again. */ 34 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INSUFFICIENT_PRIVILEGES = 7, /* User is not authorized to execute command. This can be because of the role or because of vehicle state. */ 35 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INVALID_DOMAINS = 8, /* Command was malformed or addressed to an unrecognized vehicle system. May indicate client error or older vehicle firmware. */ 36 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INVALID_COMMAND = 9, /* Unrecognized command. May indicate client error or unsupported vehicle firmware. */ 37 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_DECODING = 10, /* Could not parse command. Indicates client error. */ 38 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INTERNAL = 11, /* Internal vehicle error. Try again. Most commonly encountered when the vehicle has not finished booting. */ 39 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_WRONG_PERSONALIZATION = 12, /* Command sent to wrong VIN. */ 40 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_BAD_PARAMETER = 13, /* Command was malformed or used a deprecated parameter. */ 41 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_KEYCHAIN_IS_FULL = 14, /* Vehicle's keychain is full. You must delete a key before you can add another. */ 42 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INCORRECT_EPOCH = 15, /* Session ID mismatch. Use included session info to update session and try again. */ 43 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_IV_INCORRECT_LENGTH = 16, /* Initialization Value length is incorrect (AES-GCM must use 12-byte IVs). Indicates a client programming error. */ 44 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_TIME_EXPIRED = 17, /* Command expired. Use included session info to determine if clocks have desynchronized and try again. */ 45 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_NOT_PROVISIONED_WITH_IDENTITY = 18, /* Vehicle has not been provisioned with a VIN and may require service. */ 46 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_COULD_NOT_HASH_METADATA = 19, /* Internal vehicle error. */ 47 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_TIME_TO_LIVE_TOO_LONG = 20, /* Vehicle rejected command because its expiration time was too far in the future. This is a security precaution. */ 48 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_REMOTE_ACCESS_DISABLED = 21, /* The vehicle owner has disabled Mobile access. */ 49 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_REMOTE_SERVICE_ACCESS_DISABLED = 22, /* The command was authorized with a Service key, but the vehicle has not been configured to permit remote service commands. */ 50 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_COMMAND_REQUIRES_ACCOUNT_CREDENTIALS = 23 /* The command requires proof of Tesla account credentials but was not sent over a channel that provides this proof. Resend the command using Fleet API. */ 51 | } UniversalMessage_MessageFault_E; 52 | 53 | typedef enum _UniversalMessage_Flags { 54 | UniversalMessage_Flags_FLAG_USER_COMMAND = 0 55 | } UniversalMessage_Flags; 56 | 57 | /* Struct definitions */ 58 | typedef PB_BYTES_ARRAY_T(16) UniversalMessage_Destination_routing_address_t; 59 | typedef struct _UniversalMessage_Destination { 60 | pb_size_t which_sub_destination; 61 | union { 62 | UniversalMessage_Domain domain; 63 | UniversalMessage_Destination_routing_address_t routing_address; 64 | } sub_destination; 65 | } UniversalMessage_Destination; 66 | 67 | typedef struct _UniversalMessage_MessageStatus { 68 | UniversalMessage_OperationStatus_E operation_status; 69 | UniversalMessage_MessageFault_E signed_message_fault; 70 | } UniversalMessage_MessageStatus; 71 | 72 | typedef PB_BYTES_ARRAY_T(65) UniversalMessage_SessionInfoRequest_public_key_t; 73 | typedef PB_BYTES_ARRAY_T(32) UniversalMessage_SessionInfoRequest_challenge_t; 74 | typedef struct _UniversalMessage_SessionInfoRequest { 75 | UniversalMessage_SessionInfoRequest_public_key_t public_key; 76 | UniversalMessage_SessionInfoRequest_challenge_t challenge; 77 | } UniversalMessage_SessionInfoRequest; 78 | 79 | typedef PB_BYTES_ARRAY_T(100) UniversalMessage_RoutableMessage_protobuf_message_as_bytes_t; 80 | typedef PB_BYTES_ARRAY_T(100) UniversalMessage_RoutableMessage_session_info_t; 81 | typedef PB_BYTES_ARRAY_T(16) UniversalMessage_RoutableMessage_request_uuid_t; 82 | typedef PB_BYTES_ARRAY_T(16) UniversalMessage_RoutableMessage_uuid_t; 83 | typedef struct _UniversalMessage_RoutableMessage { 84 | bool has_to_destination; 85 | UniversalMessage_Destination to_destination; 86 | bool has_from_destination; 87 | UniversalMessage_Destination from_destination; 88 | pb_size_t which_payload; 89 | union { 90 | UniversalMessage_RoutableMessage_protobuf_message_as_bytes_t protobuf_message_as_bytes; 91 | UniversalMessage_SessionInfoRequest session_info_request; 92 | UniversalMessage_RoutableMessage_session_info_t session_info; 93 | } payload; 94 | bool has_signedMessageStatus; 95 | UniversalMessage_MessageStatus signedMessageStatus; 96 | pb_size_t which_sub_sigData; 97 | union { 98 | Signatures_SignatureData signature_data; 99 | } sub_sigData; 100 | UniversalMessage_RoutableMessage_request_uuid_t request_uuid; 101 | UniversalMessage_RoutableMessage_uuid_t uuid; 102 | uint32_t flags; 103 | } UniversalMessage_RoutableMessage; 104 | 105 | 106 | #ifdef __cplusplus 107 | extern "C" { 108 | #endif 109 | 110 | /* Helper constants for enums */ 111 | #define _UniversalMessage_Domain_MIN UniversalMessage_Domain_DOMAIN_BROADCAST 112 | #define _UniversalMessage_Domain_MAX UniversalMessage_Domain_DOMAIN_INFOTAINMENT 113 | #define _UniversalMessage_Domain_ARRAYSIZE ((UniversalMessage_Domain)(UniversalMessage_Domain_DOMAIN_INFOTAINMENT+1)) 114 | 115 | #define _UniversalMessage_OperationStatus_E_MIN UniversalMessage_OperationStatus_E_OPERATIONSTATUS_OK 116 | #define _UniversalMessage_OperationStatus_E_MAX UniversalMessage_OperationStatus_E_OPERATIONSTATUS_ERROR 117 | #define _UniversalMessage_OperationStatus_E_ARRAYSIZE ((UniversalMessage_OperationStatus_E)(UniversalMessage_OperationStatus_E_OPERATIONSTATUS_ERROR+1)) 118 | 119 | #define _UniversalMessage_MessageFault_E_MIN UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_NONE 120 | #define _UniversalMessage_MessageFault_E_MAX UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_COMMAND_REQUIRES_ACCOUNT_CREDENTIALS 121 | #define _UniversalMessage_MessageFault_E_ARRAYSIZE ((UniversalMessage_MessageFault_E)(UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_COMMAND_REQUIRES_ACCOUNT_CREDENTIALS+1)) 122 | 123 | #define _UniversalMessage_Flags_MIN UniversalMessage_Flags_FLAG_USER_COMMAND 124 | #define _UniversalMessage_Flags_MAX UniversalMessage_Flags_FLAG_USER_COMMAND 125 | #define _UniversalMessage_Flags_ARRAYSIZE ((UniversalMessage_Flags)(UniversalMessage_Flags_FLAG_USER_COMMAND+1)) 126 | 127 | #define UniversalMessage_Destination_sub_destination_domain_ENUMTYPE UniversalMessage_Domain 128 | 129 | #define UniversalMessage_MessageStatus_operation_status_ENUMTYPE UniversalMessage_OperationStatus_E 130 | #define UniversalMessage_MessageStatus_signed_message_fault_ENUMTYPE UniversalMessage_MessageFault_E 131 | 132 | 133 | 134 | 135 | /* Initializer values for message structs */ 136 | #define UniversalMessage_Destination_init_default {0, {_UniversalMessage_Domain_MIN}} 137 | #define UniversalMessage_MessageStatus_init_default {_UniversalMessage_OperationStatus_E_MIN, _UniversalMessage_MessageFault_E_MIN} 138 | #define UniversalMessage_SessionInfoRequest_init_default {{0, {0}}, {0, {0}}} 139 | #define UniversalMessage_RoutableMessage_init_default {false, UniversalMessage_Destination_init_default, false, UniversalMessage_Destination_init_default, 0, {{0, {0}}}, false, UniversalMessage_MessageStatus_init_default, 0, {Signatures_SignatureData_init_default}, {0, {0}}, {0, {0}}, 0} 140 | #define UniversalMessage_Destination_init_zero {0, {_UniversalMessage_Domain_MIN}} 141 | #define UniversalMessage_MessageStatus_init_zero {_UniversalMessage_OperationStatus_E_MIN, _UniversalMessage_MessageFault_E_MIN} 142 | #define UniversalMessage_SessionInfoRequest_init_zero {{0, {0}}, {0, {0}}} 143 | #define UniversalMessage_RoutableMessage_init_zero {false, UniversalMessage_Destination_init_zero, false, UniversalMessage_Destination_init_zero, 0, {{0, {0}}}, false, UniversalMessage_MessageStatus_init_zero, 0, {Signatures_SignatureData_init_zero}, {0, {0}}, {0, {0}}, 0} 144 | 145 | /* Field tags (for use in manual encoding/decoding) */ 146 | #define UniversalMessage_Destination_domain_tag 1 147 | #define UniversalMessage_Destination_routing_address_tag 2 148 | #define UniversalMessage_MessageStatus_operation_status_tag 1 149 | #define UniversalMessage_MessageStatus_signed_message_fault_tag 2 150 | #define UniversalMessage_SessionInfoRequest_public_key_tag 1 151 | #define UniversalMessage_SessionInfoRequest_challenge_tag 2 152 | #define UniversalMessage_RoutableMessage_to_destination_tag 6 153 | #define UniversalMessage_RoutableMessage_from_destination_tag 7 154 | #define UniversalMessage_RoutableMessage_protobuf_message_as_bytes_tag 10 155 | #define UniversalMessage_RoutableMessage_session_info_request_tag 14 156 | #define UniversalMessage_RoutableMessage_session_info_tag 15 157 | #define UniversalMessage_RoutableMessage_signedMessageStatus_tag 12 158 | #define UniversalMessage_RoutableMessage_signature_data_tag 13 159 | #define UniversalMessage_RoutableMessage_request_uuid_tag 50 160 | #define UniversalMessage_RoutableMessage_uuid_tag 51 161 | #define UniversalMessage_RoutableMessage_flags_tag 52 162 | 163 | /* Struct field encoding specification for nanopb */ 164 | #define UniversalMessage_Destination_FIELDLIST(X, a) \ 165 | X(a, STATIC, ONEOF, UENUM, (sub_destination,domain,sub_destination.domain), 1) \ 166 | X(a, STATIC, ONEOF, BYTES, (sub_destination,routing_address,sub_destination.routing_address), 2) 167 | #define UniversalMessage_Destination_CALLBACK NULL 168 | #define UniversalMessage_Destination_DEFAULT NULL 169 | 170 | #define UniversalMessage_MessageStatus_FIELDLIST(X, a) \ 171 | X(a, STATIC, SINGULAR, UENUM, operation_status, 1) \ 172 | X(a, STATIC, SINGULAR, UENUM, signed_message_fault, 2) 173 | #define UniversalMessage_MessageStatus_CALLBACK NULL 174 | #define UniversalMessage_MessageStatus_DEFAULT NULL 175 | 176 | #define UniversalMessage_SessionInfoRequest_FIELDLIST(X, a) \ 177 | X(a, STATIC, SINGULAR, BYTES, public_key, 1) \ 178 | X(a, STATIC, SINGULAR, BYTES, challenge, 2) 179 | #define UniversalMessage_SessionInfoRequest_CALLBACK NULL 180 | #define UniversalMessage_SessionInfoRequest_DEFAULT NULL 181 | 182 | #define UniversalMessage_RoutableMessage_FIELDLIST(X, a) \ 183 | X(a, STATIC, OPTIONAL, MESSAGE, to_destination, 6) \ 184 | X(a, STATIC, OPTIONAL, MESSAGE, from_destination, 7) \ 185 | X(a, STATIC, ONEOF, BYTES, (payload,protobuf_message_as_bytes,payload.protobuf_message_as_bytes), 10) \ 186 | X(a, STATIC, OPTIONAL, MESSAGE, signedMessageStatus, 12) \ 187 | X(a, STATIC, ONEOF, MESSAGE, (sub_sigData,signature_data,sub_sigData.signature_data), 13) \ 188 | X(a, STATIC, ONEOF, MESSAGE, (payload,session_info_request,payload.session_info_request), 14) \ 189 | X(a, STATIC, ONEOF, BYTES, (payload,session_info,payload.session_info), 15) \ 190 | X(a, STATIC, SINGULAR, BYTES, request_uuid, 50) \ 191 | X(a, STATIC, SINGULAR, BYTES, uuid, 51) \ 192 | X(a, STATIC, SINGULAR, UINT32, flags, 52) 193 | #define UniversalMessage_RoutableMessage_CALLBACK NULL 194 | #define UniversalMessage_RoutableMessage_DEFAULT NULL 195 | #define UniversalMessage_RoutableMessage_to_destination_MSGTYPE UniversalMessage_Destination 196 | #define UniversalMessage_RoutableMessage_from_destination_MSGTYPE UniversalMessage_Destination 197 | #define UniversalMessage_RoutableMessage_signedMessageStatus_MSGTYPE UniversalMessage_MessageStatus 198 | #define UniversalMessage_RoutableMessage_sub_sigData_signature_data_MSGTYPE Signatures_SignatureData 199 | #define UniversalMessage_RoutableMessage_payload_session_info_request_MSGTYPE UniversalMessage_SessionInfoRequest 200 | 201 | extern const pb_msgdesc_t UniversalMessage_Destination_msg; 202 | extern const pb_msgdesc_t UniversalMessage_MessageStatus_msg; 203 | extern const pb_msgdesc_t UniversalMessage_SessionInfoRequest_msg; 204 | extern const pb_msgdesc_t UniversalMessage_RoutableMessage_msg; 205 | 206 | /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ 207 | #define UniversalMessage_Destination_fields &UniversalMessage_Destination_msg 208 | #define UniversalMessage_MessageStatus_fields &UniversalMessage_MessageStatus_msg 209 | #define UniversalMessage_SessionInfoRequest_fields &UniversalMessage_SessionInfoRequest_msg 210 | #define UniversalMessage_RoutableMessage_fields &UniversalMessage_RoutableMessage_msg 211 | 212 | /* Maximum encoded size of messages (where known) */ 213 | #define UNIVERSALMESSAGE_UNIVERSAL_MESSAGE_PB_H_MAX_SIZE UniversalMessage_RoutableMessage_size 214 | #define UniversalMessage_Destination_size 18 215 | #define UniversalMessage_MessageStatus_size 4 216 | #define UniversalMessage_RoutableMessage_size 329 217 | #define UniversalMessage_SessionInfoRequest_size 101 218 | 219 | #ifdef __cplusplus 220 | } /* extern "C" */ 221 | #endif 222 | 223 | #endif 224 | -------------------------------------------------------------------------------- /include/vehicle.pb.h: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb header */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #ifndef PB_CARSERVER_VEHICLE_PB_H_INCLUDED 5 | #define PB_CARSERVER_VEHICLE_PB_H_INCLUDED 6 | #include 7 | 8 | #if PB_PROTO_HEADER_VERSION != 40 9 | #error Regenerate this file with the current version of nanopb generator. 10 | #endif 11 | 12 | /* Enum definitions */ 13 | typedef enum _CarServer_ClimateState_CopActivationTemp { 14 | CarServer_ClimateState_CopActivationTemp_CopActivationTempUnspecified = 0, 15 | CarServer_ClimateState_CopActivationTemp_CopActivationTempLow = 1, 16 | CarServer_ClimateState_CopActivationTemp_CopActivationTempMedium = 2, 17 | CarServer_ClimateState_CopActivationTemp_CopActivationTempHigh = 3 18 | } CarServer_ClimateState_CopActivationTemp; 19 | 20 | /* Struct definitions */ 21 | typedef struct _CarServer_VehicleState_GuestMode { 22 | bool GuestModeActive; 23 | } CarServer_VehicleState_GuestMode; 24 | 25 | typedef struct _CarServer_VehicleState { 26 | bool has_guestMode; 27 | CarServer_VehicleState_GuestMode guestMode; 28 | } CarServer_VehicleState; 29 | 30 | typedef struct _CarServer_ClimateState { 31 | char dummy_field; 32 | } CarServer_ClimateState; 33 | 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | /* Helper constants for enums */ 40 | #define _CarServer_ClimateState_CopActivationTemp_MIN CarServer_ClimateState_CopActivationTemp_CopActivationTempUnspecified 41 | #define _CarServer_ClimateState_CopActivationTemp_MAX CarServer_ClimateState_CopActivationTemp_CopActivationTempHigh 42 | #define _CarServer_ClimateState_CopActivationTemp_ARRAYSIZE ((CarServer_ClimateState_CopActivationTemp)(CarServer_ClimateState_CopActivationTemp_CopActivationTempHigh+1)) 43 | 44 | 45 | 46 | 47 | 48 | /* Initializer values for message structs */ 49 | #define CarServer_VehicleState_init_default {false, CarServer_VehicleState_GuestMode_init_default} 50 | #define CarServer_VehicleState_GuestMode_init_default {0} 51 | #define CarServer_ClimateState_init_default {0} 52 | #define CarServer_VehicleState_init_zero {false, CarServer_VehicleState_GuestMode_init_zero} 53 | #define CarServer_VehicleState_GuestMode_init_zero {0} 54 | #define CarServer_ClimateState_init_zero {0} 55 | 56 | /* Field tags (for use in manual encoding/decoding) */ 57 | #define CarServer_VehicleState_GuestMode_GuestModeActive_tag 1 58 | #define CarServer_VehicleState_guestMode_tag 74 59 | 60 | /* Struct field encoding specification for nanopb */ 61 | #define CarServer_VehicleState_FIELDLIST(X, a) \ 62 | X(a, STATIC, OPTIONAL, MESSAGE, guestMode, 74) 63 | #define CarServer_VehicleState_CALLBACK NULL 64 | #define CarServer_VehicleState_DEFAULT NULL 65 | #define CarServer_VehicleState_guestMode_MSGTYPE CarServer_VehicleState_GuestMode 66 | 67 | #define CarServer_VehicleState_GuestMode_FIELDLIST(X, a) \ 68 | X(a, STATIC, SINGULAR, BOOL, GuestModeActive, 1) 69 | #define CarServer_VehicleState_GuestMode_CALLBACK NULL 70 | #define CarServer_VehicleState_GuestMode_DEFAULT NULL 71 | 72 | #define CarServer_ClimateState_FIELDLIST(X, a) \ 73 | 74 | #define CarServer_ClimateState_CALLBACK NULL 75 | #define CarServer_ClimateState_DEFAULT NULL 76 | 77 | extern const pb_msgdesc_t CarServer_VehicleState_msg; 78 | extern const pb_msgdesc_t CarServer_VehicleState_GuestMode_msg; 79 | extern const pb_msgdesc_t CarServer_ClimateState_msg; 80 | 81 | /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ 82 | #define CarServer_VehicleState_fields &CarServer_VehicleState_msg 83 | #define CarServer_VehicleState_GuestMode_fields &CarServer_VehicleState_GuestMode_msg 84 | #define CarServer_ClimateState_fields &CarServer_ClimateState_msg 85 | 86 | /* Maximum encoded size of messages (where known) */ 87 | #define CARSERVER_VEHICLE_PB_H_MAX_SIZE CarServer_VehicleState_size 88 | #define CarServer_ClimateState_size 0 89 | #define CarServer_VehicleState_GuestMode_size 2 90 | #define CarServer_VehicleState_size 5 91 | 92 | #ifdef __cplusplus 93 | } /* extern "C" */ 94 | #endif 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tesla-vehicle-command-cpp", 3 | "version": "1.0.0", 4 | "description": "This CPP library facilitates direct communication with Tesla vehicles via the BLE API. It offers fundamental features such as unlocking/locking, opening the trunk, and more. The library's capabilities are contingent on the range of actions implemented by Tesla, which is the only limitation at present.", 5 | "keywords": "tesla, ble, vehicle control", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/pmdroid/tesla-vehicle-command.git" 9 | }, 10 | "authors": [ 11 | { 12 | "name": "Pascal Matthiesen", 13 | "email": "me@pascal.sh", 14 | "url": "https://pascal.sh", 15 | "maintainer": true 16 | } 17 | ], 18 | "license": "AGPL-3.0-or-later", 19 | "homepage": "https://github.com/pmdroid/tesla-vehicle-command", 20 | "frameworks": "*", 21 | "platforms": [ 22 | "espidf" 23 | ], 24 | "dependencies": { 25 | "nanopb/Nanopb": "^0.4.8" 26 | }, 27 | "export": { 28 | "exclude": [ 29 | "cmake-build-debug/", 30 | "**/cmake-build-debug/", 31 | "build/", 32 | "**/build/", 33 | ".pio/", 34 | "**/.pio/", 35 | ".idea/", 36 | "**/.idea/", 37 | ".vscode/", 38 | "**/.vscode/", 39 | ".stfolder/", 40 | "**/.stfolder/", 41 | "library.zip", 42 | "**/library.zip" 43 | ] 44 | } 45 | } -------------------------------------------------------------------------------- /patches/mbedtls2.patch: -------------------------------------------------------------------------------- 1 | diff --git a/CMakeLists.txt b/CMakeLists.txt 2 | index 077b3ed..2f4caa8 100644 3 | --- a/CMakeLists.txt 4 | +++ b/CMakeLists.txt 5 | @@ -22,7 +22,7 @@ FetchContent_Declare( 6 | FetchContent_Declare( 7 | mbedtls 8 | GIT_REPOSITORY https://github.com/Mbed-TLS/mbedtls.git 9 | - GIT_TAG v3.6.0 10 | + GIT_TAG v2.28.8 11 | GIT_SHALLOW TRUE 12 | ) 13 | 14 | diff --git a/src/authenticator.cpp b/src/authenticator.cpp 15 | index 3a5cde0..597e560 100644 16 | --- a/src/authenticator.cpp 17 | +++ b/src/authenticator.cpp 18 | @@ -153,7 +153,7 @@ namespace TeslaBLE { 19 | unsigned char password[0]; 20 | return_code = mbedtls_pk_parse_key( 21 | &this->private_key_context_, private_key_buffer, private_key_size, 22 | - password, 0, mbedtls_ctr_drbg_random, &this->drbg_context_); 23 | + password, 0); 24 | 25 | if (return_code != 0) { 26 | Common::PrintErrorFromMbedTlsErrorCode(return_code); 27 | @@ -185,8 +185,8 @@ namespace TeslaBLE { 28 | 29 | int Authenticator::GeneratePublicKey() { 30 | int return_code = mbedtls_ecp_point_write_binary( 31 | - &mbedtls_pk_ec(this->private_key_context_)->private_grp, 32 | - &mbedtls_pk_ec(this->private_key_context_)->private_Q, 33 | + &mbedtls_pk_ec(this->private_key_context_)->grp, 34 | + &mbedtls_pk_ec(this->private_key_context_)->Q, 35 | MBEDTLS_ECP_PF_UNCOMPRESSED, &this->public_key_size_, this->public_key_, 36 | sizeof(this->public_key_)); 37 | 38 | @@ -210,7 +210,7 @@ namespace TeslaBLE { 39 | unsigned char temp_shared_secret[32]; 40 | size_t temp_shared_secret_length = 0; 41 | 42 | - int return_code = mbedtls_ecp_group_load(&tesla_key.private_grp, 43 | + int return_code = mbedtls_ecp_group_load(&tesla_key.grp, 44 | MBEDTLS_ECP_DP_SECP256R1); 45 | if (return_code != 0) { 46 | Common::PrintErrorFromMbedTlsErrorCode(return_code); 47 | @@ -218,7 +218,7 @@ namespace TeslaBLE { 48 | } 49 | 50 | return_code = mbedtls_ecp_point_read_binary( 51 | - &tesla_key.private_grp, &tesla_key.private_Q, 52 | + &tesla_key.grp, &tesla_key.Q, 53 | public_key_buffer, public_key_size); 54 | 55 | if (return_code != 0) { 56 | @@ -257,25 +257,10 @@ namespace TeslaBLE { 57 | mbedtls_sha1_context sha1_context; 58 | mbedtls_sha1_init(&sha1_context); 59 | 60 | - return_code = mbedtls_sha1_starts(&sha1_context); 61 | - if (return_code != 0) { 62 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 63 | - return ResultCode::MBEDTLS_ERROR; 64 | - } 65 | - 66 | - return_code = mbedtls_sha1_update(&sha1_context, temp_shared_secret, 67 | - temp_shared_secret_length); 68 | - if (return_code != 0) { 69 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 70 | - return ResultCode::MBEDTLS_ERROR; 71 | - } 72 | - 73 | - return_code = mbedtls_sha1_finish(&sha1_context, this->shared_secrets_[domain]); 74 | - if (return_code != 0) { 75 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 76 | - return ResultCode::MBEDTLS_ERROR; 77 | - } 78 | - 79 | + mbedtls_sha1_starts(&sha1_context); 80 | + mbedtls_sha1_update(&sha1_context, temp_shared_secret, 81 | + temp_shared_secret_length); 82 | + mbedtls_sha1_finish(&sha1_context, this->shared_secrets_[domain]); 83 | mbedtls_sha1_free(&sha1_context); 84 | mbedtls_ecp_keypair_free(&tesla_key); 85 | return ResultCode::SUCCESS; 86 | @@ -299,45 +284,19 @@ namespace TeslaBLE { 87 | } 88 | 89 | return_code = mbedtls_gcm_starts(&aes_context, MBEDTLS_GCM_ENCRYPT, this->nonce_, 90 | - 12); 91 | + 12, checksum, 32); 92 | if (return_code != 0) { 93 | Common::PrintErrorFromMbedTlsErrorCode(return_code); 94 | return ResultCode::MBEDTLS_ERROR; 95 | } 96 | 97 | - return_code = mbedtls_gcm_update_ad(&aes_context, checksum, 32); 98 | - if (return_code != 0) { 99 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 100 | - return ResultCode::MBEDTLS_ERROR; 101 | - } 102 | - 103 | - return_code = 104 | - mbedtls_gcm_update(&aes_context, input_buffer, input_buffer_size, 105 | - output_buffer, output_buffer_size, output_size); 106 | - 107 | - if (return_code != 0) { 108 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 109 | - return ResultCode::MBEDTLS_ERROR; 110 | - } 111 | - 112 | - size_t finish_buffer_length = 0; 113 | - unsigned char finish_buffer[15]; 114 | - 115 | - return_code = 116 | - mbedtls_gcm_finish(&aes_context, finish_buffer, sizeof(finish_buffer), 117 | - &finish_buffer_length, tag_buffer, 16); 118 | - 119 | - if (return_code != 0) { 120 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 121 | - return ResultCode::MBEDTLS_ERROR; 122 | - } 123 | + mbedtls_gcm_update(&aes_context, input_buffer_size, input_buffer, output_buffer); 124 | + mbedtls_gcm_finish(&aes_context, tag_buffer, 16); 125 | + mbedtls_gcm_free(&aes_context); 126 | 127 | - if (finish_buffer_length > 0) { 128 | - memcpy(output_buffer + *output_size, finish_buffer, finish_buffer_length); 129 | - *output_size = output_buffer_size + finish_buffer_length; 130 | - } 131 | + // hack for old medbtls implementation 132 | + *output_size = input_buffer_size; 133 | 134 | - mbedtls_gcm_free(&aes_context); 135 | return ResultCode::SUCCESS; 136 | } 137 | 138 | diff --git a/src/metadata.cpp b/src/metadata.cpp 139 | index 3eefddd..fc268d4 100644 140 | --- a/src/metadata.cpp 141 | +++ b/src/metadata.cpp 142 | @@ -18,11 +18,7 @@ namespace TeslaBLE { 143 | int MetaData::Start() { 144 | this->last_tag = 0; 145 | mbedtls_sha256_init(&this->sha256_context_); 146 | - int return_code = mbedtls_sha256_starts(&this->sha256_context_, 0); 147 | - if (return_code != 0) { 148 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 149 | - return ResultCode::MBEDTLS_ERROR; 150 | - } 151 | + mbedtls_sha256_starts(&this->sha256_context_, 0); 152 | 153 | return ResultCode::SUCCESS; 154 | } 155 | @@ -76,23 +72,9 @@ namespace TeslaBLE { 156 | 157 | this->last_tag = signatures_tag; 158 | 159 | - int return_code = mbedtls_sha256_update(&this->sha256_context_, &signatures_tag, 1); 160 | - if (return_code != 0) { 161 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 162 | - return ResultCode::MBEDTLS_ERROR; 163 | - } 164 | - 165 | - return_code = mbedtls_sha256_update(&this->sha256_context_, &value_size, 1); 166 | - if (return_code != 0) { 167 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 168 | - return ResultCode::MBEDTLS_ERROR; 169 | - } 170 | - 171 | - return_code = mbedtls_sha256_update(&this->sha256_context_, value, value_size); 172 | - if (return_code != 0) { 173 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 174 | - return ResultCode::MBEDTLS_ERROR; 175 | - } 176 | + mbedtls_sha256_update(&this->sha256_context_, &signatures_tag, 1); 177 | + mbedtls_sha256_update(&this->sha256_context_, &value_size, 1); 178 | + mbedtls_sha256_update(&this->sha256_context_, value, value_size); 179 | 180 | return ResultCode::SUCCESS; 181 | } 182 | @@ -109,16 +91,8 @@ namespace TeslaBLE { 183 | } 184 | 185 | void MetaData::Checksum(unsigned char *output, const unsigned char end_tag) { 186 | - int return_code = mbedtls_sha256_update(&this->sha256_context_, &end_tag, 1); 187 | - if (return_code != 0) { 188 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 189 | - } 190 | - 191 | - return_code = mbedtls_sha256_finish(&this->sha256_context_, output); 192 | - if (return_code != 0) { 193 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 194 | - } 195 | - 196 | + mbedtls_sha256_update(&this->sha256_context_, &end_tag, 1); 197 | + mbedtls_sha256_finish(&this->sha256_context_, output); 198 | mbedtls_sha256_free(&this->sha256_context_); 199 | } 200 | } 201 | diff --git a/src/shared.cpp b/src/shared.cpp 202 | index dadef5e..e1a212b 100644 203 | --- a/src/shared.cpp 204 | +++ b/src/shared.cpp 205 | @@ -79,11 +79,7 @@ namespace TeslaBLE { 206 | strcpy((char *) parsed_vin, (char *) vin); 207 | 208 | unsigned char hashed_vin[20]; 209 | - const int return_code = mbedtls_sha1(parsed_vin, 17, hashed_vin); 210 | - if (return_code != 0) { 211 | - Common::PrintErrorFromMbedTlsErrorCode(return_code); 212 | - return 1; 213 | - } 214 | + mbedtls_sha1(parsed_vin, 17, hashed_vin); 215 | 216 | output[0] = 'S'; 217 | for (int i = 0; i < 8; ++i) { 218 | -------------------------------------------------------------------------------- /proto.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd protobuf/ 3 | nanopb_generator ./*.proto 4 | cd .. 5 | cp protobuf/*.c src/ 6 | cp protobuf/*.h include/ 7 | sed -i '/#include "google\/protobuf\/timestamp.pb.h"/d' include/car_server.pb.h 8 | rm protobuf/*.c 9 | rm protobuf/*.h -------------------------------------------------------------------------------- /protobuf/car_server.options: -------------------------------------------------------------------------------- 1 | CarServer.NearbyChargingSites.timestamp type:FT_IGNORE 2 | CarServer.Ping.local_timestamp type:FT_IGNORE 3 | CarServer.Ping.last_remote_timestamp type:FT_IGNORE 4 | 5 | CarServer.ResultReason.plain_text max_size:200 6 | 7 | CarServer.HvacSeatHeaterActions.hvacSeatHeaterAction max_count:5 -------------------------------------------------------------------------------- /protobuf/car_server.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package CarServer; 4 | 5 | option java_package = "com.tesla.generated.carserver.server"; 6 | option go_package = "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/carserver"; 7 | 8 | import "vehicle.proto"; 9 | import "signatures.proto"; 10 | import "common.proto"; 11 | import "google/protobuf/timestamp.proto"; 12 | 13 | message Action { 14 | reserved 3 to 5; 15 | oneof action_msg { 16 | VehicleAction vehicleAction = 2; 17 | } 18 | } 19 | 20 | message VehicleAction { 21 | reserved 11; 22 | reserved 60; 23 | reserved 76; 24 | oneof vehicle_action_msg { 25 | ChargingSetLimitAction chargingSetLimitAction = 5; 26 | ChargingStartStopAction chargingStartStopAction = 6; 27 | DrivingClearSpeedLimitPinAction drivingClearSpeedLimitPinAction = 7; 28 | DrivingSetSpeedLimitAction drivingSetSpeedLimitAction = 8; 29 | DrivingSpeedLimitAction drivingSpeedLimitAction = 9; 30 | HvacAutoAction hvacAutoAction = 10; 31 | HvacSetPreconditioningMaxAction hvacSetPreconditioningMaxAction = 12; 32 | HvacSteeringWheelHeaterAction hvacSteeringWheelHeaterAction = 13; 33 | HvacTemperatureAdjustmentAction hvacTemperatureAdjustmentAction = 14; 34 | MediaPlayAction mediaPlayAction = 15; 35 | MediaUpdateVolume mediaUpdateVolume = 16; 36 | MediaNextFavorite mediaNextFavorite = 17; 37 | MediaPreviousFavorite mediaPreviousFavorite = 18; 38 | MediaNextTrack mediaNextTrack = 19; 39 | MediaPreviousTrack mediaPreviousTrack = 20; 40 | GetNearbyChargingSites getNearbyChargingSites = 23; 41 | VehicleControlCancelSoftwareUpdateAction vehicleControlCancelSoftwareUpdateAction = 25; 42 | VehicleControlFlashLightsAction vehicleControlFlashLightsAction = 26; 43 | VehicleControlHonkHornAction vehicleControlHonkHornAction = 27; 44 | VehicleControlResetValetPinAction vehicleControlResetValetPinAction = 28; 45 | VehicleControlScheduleSoftwareUpdateAction vehicleControlScheduleSoftwareUpdateAction = 29; 46 | VehicleControlSetSentryModeAction vehicleControlSetSentryModeAction = 30; 47 | VehicleControlSetValetModeAction vehicleControlSetValetModeAction = 31; 48 | VehicleControlSunroofOpenCloseAction vehicleControlSunroofOpenCloseAction = 32; 49 | VehicleControlTriggerHomelinkAction vehicleControlTriggerHomelinkAction = 33; 50 | VehicleControlWindowAction vehicleControlWindowAction = 34; 51 | HvacBioweaponModeAction hvacBioweaponModeAction = 35; 52 | HvacSeatHeaterActions hvacSeatHeaterActions = 36; 53 | ScheduledChargingAction scheduledChargingAction = 41; 54 | ScheduledDepartureAction scheduledDepartureAction = 42; 55 | SetChargingAmpsAction setChargingAmpsAction = 43; 56 | HvacClimateKeeperAction hvacClimateKeeperAction = 44; 57 | Ping ping = 46; 58 | AutoSeatClimateAction autoSeatClimateAction = 48; 59 | HvacSeatCoolerActions hvacSeatCoolerActions = 49; 60 | SetCabinOverheatProtectionAction setCabinOverheatProtectionAction = 50; 61 | SetVehicleNameAction setVehicleNameAction = 54; 62 | ChargePortDoorClose chargePortDoorClose = 61; 63 | ChargePortDoorOpen chargePortDoorOpen = 62; 64 | VehicleState.GuestMode guestModeAction = 65; 65 | SetCopTempAction setCopTempAction = 66; 66 | EraseUserDataAction eraseUserDataAction = 72; 67 | VehicleControlSetPinToDriveAction vehicleControlSetPinToDriveAction = 77; 68 | VehicleControlResetPinToDriveAction vehicleControlResetPinToDriveAction = 78; 69 | } 70 | } 71 | 72 | message EraseUserDataAction { 73 | string reason = 1; 74 | } 75 | 76 | message Response { 77 | ActionStatus actionStatus = 1; 78 | oneof response_msg { 79 | Signatures.SessionInfo getSessionInfoResponse = 3; 80 | NearbyChargingSites getNearbyChargingSites = 5; 81 | Ping ping = 9; 82 | } 83 | } 84 | 85 | message ActionStatus { 86 | OperationStatus_E result = 1; 87 | ResultReason result_reason = 2; 88 | } 89 | 90 | enum OperationStatus_E 91 | { 92 | OPERATIONSTATUS_OK = 0; 93 | OPERATIONSTATUS_ERROR = 1; 94 | } 95 | 96 | message ResultReason { 97 | oneof reason { 98 | string plain_text = 1; 99 | } 100 | } 101 | 102 | message EncryptedData { 103 | int32 field_number = 1; 104 | bytes ciphertext = 2; 105 | 106 | bytes tag = 3; 107 | } 108 | 109 | message ChargingSetLimitAction { 110 | int32 percent = 1; 111 | } 112 | 113 | message ChargingStartStopAction { 114 | oneof charging_action { 115 | Void unknown = 1; 116 | Void start = 2; 117 | Void start_standard = 3; 118 | Void start_max_range = 4; 119 | Void stop = 5; 120 | } 121 | } 122 | 123 | message DrivingClearSpeedLimitPinAction { 124 | string pin = 1; 125 | } 126 | 127 | message DrivingSetSpeedLimitAction { 128 | double limit_mph = 1; 129 | } 130 | 131 | message DrivingSpeedLimitAction { 132 | bool activate = 1; 133 | string pin = 2; 134 | } 135 | 136 | message HvacAutoAction { 137 | bool power_on = 1; 138 | bool manual_override = 2; 139 | } 140 | 141 | message HvacSeatHeaterActions { 142 | message HvacSeatHeaterAction { 143 | oneof seat_heater_level { 144 | Void SEAT_HEATER_UNKNOWN = 1; 145 | Void SEAT_HEATER_OFF = 2; 146 | Void SEAT_HEATER_LOW = 3; 147 | Void SEAT_HEATER_MED = 4; 148 | Void SEAT_HEATER_HIGH = 5; 149 | } 150 | oneof seat_position { 151 | Void CAR_SEAT_UNKNOWN = 6; 152 | Void CAR_SEAT_FRONT_LEFT = 7; 153 | Void CAR_SEAT_FRONT_RIGHT = 8; 154 | Void CAR_SEAT_REAR_LEFT = 9; 155 | Void CAR_SEAT_REAR_LEFT_BACK = 10; 156 | Void CAR_SEAT_REAR_CENTER = 11; 157 | Void CAR_SEAT_REAR_RIGHT = 12; 158 | Void CAR_SEAT_REAR_RIGHT_BACK = 13; 159 | Void CAR_SEAT_THIRD_ROW_LEFT = 14; 160 | Void CAR_SEAT_THIRD_ROW_RIGHT = 15; 161 | } 162 | } 163 | 164 | repeated HvacSeatHeaterAction hvacSeatHeaterAction = 1; 165 | } 166 | 167 | message HvacSeatCoolerActions { 168 | enum HvacSeatCoolerLevel_E { 169 | HvacSeatCoolerLevel_Unknown = 0; 170 | HvacSeatCoolerLevel_Off = 1; 171 | HvacSeatCoolerLevel_Low = 2; 172 | HvacSeatCoolerLevel_Med = 3; 173 | HvacSeatCoolerLevel_High = 4; 174 | } 175 | enum HvacSeatCoolerPosition_E { 176 | HvacSeatCoolerPosition_Unknown = 0; 177 | HvacSeatCoolerPosition_FrontLeft = 1; 178 | HvacSeatCoolerPosition_FrontRight = 2; 179 | } 180 | message HvacSeatCoolerAction { 181 | HvacSeatCoolerLevel_E seat_cooler_level = 1; 182 | HvacSeatCoolerPosition_E seat_position = 2; 183 | } 184 | repeated HvacSeatCoolerAction hvacSeatCoolerAction = 1; 185 | } 186 | 187 | message HvacSetPreconditioningMaxAction { 188 | bool on = 1; 189 | bool manual_override = 2; 190 | enum ManualOverrideMode_E { 191 | DogMode = 0; 192 | Soc = 1; 193 | Doors = 2; 194 | } 195 | repeated ManualOverrideMode_E manual_override_mode = 3; 196 | } 197 | 198 | message HvacSteeringWheelHeaterAction { 199 | bool power_on = 1; 200 | } 201 | 202 | message HvacTemperatureAdjustmentAction { 203 | message Temperature { 204 | oneof type { 205 | Void TEMP_UNKNOWN = 1; 206 | Void TEMP_MIN = 2; 207 | Void TEMP_MAX = 3; 208 | } 209 | } 210 | message HvacTemperatureZone { 211 | oneof type { 212 | Void TEMP_ZONE_UNKNOWN = 1; 213 | Void TEMP_ZONE_FRONT_LEFT = 2; 214 | Void TEMP_ZONE_FRONT_RIGHT = 3; 215 | Void TEMP_ZONE_REAR = 4; 216 | } 217 | } 218 | float delta_celsius = 1; 219 | sint32 delta_percent = 2; 220 | float absolute_celsius = 3; 221 | Temperature level = 5; 222 | repeated HvacTemperatureZone hvac_temperature_zone = 4; 223 | float driver_temp_celsius = 6; 224 | float passenger_temp_celsius = 7; 225 | } 226 | 227 | message GetNearbyChargingSites { 228 | bool include_meta_data = 1; 229 | int32 radius = 2; 230 | int32 count = 3; 231 | } 232 | 233 | message NearbyChargingSites { 234 | google.protobuf.Timestamp timestamp = 1; 235 | repeated Superchargers superchargers = 3; 236 | int64 congestion_sync_time_utc_secs = 4; 237 | } 238 | 239 | message Superchargers { 240 | int64 id = 1; 241 | string amenities = 2; 242 | int32 available_stalls = 3; 243 | string billing_info = 4; 244 | string billing_time = 5; 245 | string city = 6; 246 | string country = 7; 247 | float distance_miles = 8; 248 | string district = 9; 249 | LatLong location = 10; 250 | string name = 11; 251 | string postal_code = 12; 252 | bool site_closed = 13; 253 | string state = 14; 254 | string street_address = 15; 255 | int32 total_stalls = 16; 256 | bool within_range = 17; 257 | int32 max_power_kw = 18; 258 | int32 out_of_order_stalls_number = 19; 259 | string out_of_order_stalls_names = 20; 260 | } 261 | 262 | message MediaPlayAction { 263 | } 264 | 265 | message MediaUpdateVolume{ 266 | reserved 2; 267 | oneof media_volume { 268 | sint32 volume_delta = 1; 269 | float volume_absolute_float = 3; 270 | } 271 | } 272 | 273 | message MediaNextFavorite { 274 | } 275 | 276 | message MediaPreviousFavorite{ 277 | } 278 | 279 | message MediaNextTrack { 280 | } 281 | 282 | message MediaPreviousTrack{ 283 | } 284 | 285 | message VehicleControlCancelSoftwareUpdateAction { 286 | } 287 | 288 | message VehicleControlFlashLightsAction { 289 | } 290 | 291 | message VehicleControlHonkHornAction { 292 | } 293 | 294 | message VehicleControlResetValetPinAction { 295 | } 296 | 297 | message VehicleControlScheduleSoftwareUpdateAction { 298 | int32 offset_sec = 1; 299 | } 300 | 301 | message VehicleControlSetSentryModeAction { 302 | bool on = 1; 303 | } 304 | 305 | message VehicleControlSetValetModeAction { 306 | bool on = 1; 307 | string password = 2; 308 | } 309 | 310 | message VehicleControlSunroofOpenCloseAction { 311 | oneof sunroof_level { 312 | int32 absolute_level = 1; 313 | sint32 delta_level = 2; 314 | } 315 | 316 | oneof action { 317 | Void vent = 3; 318 | Void close = 4; 319 | Void open = 5; 320 | } 321 | } 322 | 323 | message VehicleControlTriggerHomelinkAction { 324 | LatLong location = 1; 325 | string token = 2; 326 | } 327 | 328 | message VehicleControlWindowAction { 329 | reserved 1; // Location not required for vehicles that support this protocol. 330 | oneof action { 331 | Void unknown = 2; 332 | Void vent = 3; 333 | Void close = 4; 334 | } 335 | } 336 | 337 | message HvacBioweaponModeAction { 338 | bool on = 1; 339 | bool manual_override = 2; 340 | } 341 | 342 | message AutoSeatClimateAction { 343 | enum AutoSeatPosition_E { 344 | AutoSeatPosition_Unknown = 0; 345 | AutoSeatPosition_FrontLeft = 1; 346 | AutoSeatPosition_FrontRight = 2; 347 | } 348 | message CarSeat { 349 | bool on = 1; 350 | AutoSeatPosition_E seat_position = 2; 351 | } 352 | repeated CarSeat carseat = 1; 353 | } 354 | 355 | message Ping { 356 | int32 ping_id = 1; 357 | google.protobuf.Timestamp local_timestamp = 2; 358 | google.protobuf.Timestamp last_remote_timestamp = 3; 359 | } 360 | 361 | message ScheduledChargingAction { 362 | bool enabled = 1; 363 | int32 charging_time = 2; 364 | } 365 | 366 | message ScheduledDepartureAction { 367 | bool enabled = 1; 368 | int32 departure_time = 2; 369 | PreconditioningTimes preconditioning_times = 3; 370 | OffPeakChargingTimes off_peak_charging_times = 4; 371 | int32 off_peak_hours_end_time = 5; 372 | } 373 | 374 | message HvacClimateKeeperAction { 375 | enum ClimateKeeperAction_E 376 | { 377 | ClimateKeeperAction_Off = 0; 378 | ClimateKeeperAction_On = 1; 379 | ClimateKeeperAction_Dog = 2; 380 | ClimateKeeperAction_Camp = 3; 381 | } 382 | 383 | ClimateKeeperAction_E ClimateKeeperAction = 1; 384 | bool manual_override = 2; 385 | } 386 | 387 | 388 | message SetChargingAmpsAction { 389 | int32 charging_amps = 1; 390 | } 391 | 392 | message SetCabinOverheatProtectionAction { 393 | bool on = 1; 394 | bool fan_only = 2; 395 | } 396 | 397 | message SetVehicleNameAction { 398 | string vehicleName = 1; 399 | } 400 | 401 | message ChargePortDoorClose { 402 | } 403 | 404 | message ChargePortDoorOpen { 405 | } 406 | 407 | message SetCopTempAction { 408 | ClimateState.CopActivationTemp copActivationTemp = 1; 409 | } 410 | 411 | message VehicleControlSetPinToDriveAction { 412 | bool on = 1; 413 | string password = 2; 414 | } 415 | 416 | message VehicleControlResetPinToDriveAction { 417 | } 418 | -------------------------------------------------------------------------------- /protobuf/common.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package CarServer; 4 | 5 | option go_package = "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/carserver"; 6 | option java_package = "com.tesla.generated.carserver.common"; 7 | 8 | enum Invalid { 9 | INVALID = 0; 10 | } 11 | 12 | message Void {} 13 | 14 | message LatLong { 15 | float latitude = 1; 16 | float longitude = 2; 17 | } 18 | 19 | message PreconditioningTimes { 20 | oneof times { 21 | Void all_week = 1; 22 | Void weekdays = 2; 23 | } 24 | } 25 | 26 | message OffPeakChargingTimes { 27 | oneof times { 28 | Void all_week = 1; 29 | Void weekdays = 2; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /protobuf/errors.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package Errors; 4 | 5 | option go_package = "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/errors"; 6 | option java_package = "com.tesla.generated.errors"; 7 | 8 | enum GenericError_E 9 | { 10 | GENERICERROR_NONE = 0; 11 | GENERICERROR_UNKNOWN = 1; 12 | GENERICERROR_CLOSURES_OPEN = 2; 13 | GENERICERROR_ALREADY_ON = 3; 14 | GENERICERROR_DISABLED_FOR_USER_COMMAND = 4; 15 | GENERICERROR_VEHICLE_NOT_IN_PARK = 5; 16 | GENERICERROR_UNAUTHORIZED = 6; 17 | GENERICERROR_NOT_ALLOWED_OVER_TRANSPORT = 7; 18 | } 19 | 20 | message NominalError { 21 | GenericError_E genericError = 1; 22 | } 23 | -------------------------------------------------------------------------------- /protobuf/keys.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package Keys; 4 | option java_package = "com.tesla.generated.keys"; 5 | option go_package = "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/keys"; 6 | 7 | enum Role 8 | { 9 | ROLE_NONE = 0; 10 | ROLE_SERVICE = 1; 11 | ROLE_OWNER = 2; 12 | ROLE_DRIVER = 3; 13 | ROLE_FM = 4; 14 | ROLE_VEHICLE_MONITOR = 5; 15 | ROLE_CHARGING_MANAGER = 6; 16 | } 17 | -------------------------------------------------------------------------------- /protobuf/signatures.options: -------------------------------------------------------------------------------- 1 | ## KeyIdentity 2 | Signatures.KeyIdentity.public_key max_size:65 3 | 4 | ## AES_GCM_Personalized_Signature_Data 5 | Signatures.AES_GCM_Personalized_Signature_Data.epoch max_size:16 6 | Signatures.AES_GCM_Personalized_Signature_Data.nonce max_size:12 7 | Signatures.AES_GCM_Personalized_Signature_Data.tag max_size:16 8 | 9 | ## HMAC_Personalized_Signature_Data 10 | Signatures.HMAC_Personalized_Signature_Data.epoch max_size:16 11 | Signatures.HMAC_Personalized_Signature_Data.tag max_size:16 12 | 13 | ## HMAC_Signature_Data 14 | Signatures.HMAC_Signature_Data.tag max_size:34 15 | 16 | ## SessionInfo 17 | Signatures.SessionInfo.publicKey max_size:65 18 | Signatures.SessionInfo.epoch max_size:16 fixed_length:true 19 | -------------------------------------------------------------------------------- /protobuf/signatures.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package Signatures; 4 | 5 | option go_package = "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/signatures"; 6 | option java_package = "com.tesla.generated.signatures"; 7 | 8 | enum Tag 9 | { 10 | TAG_SIGNATURE_TYPE = 0; 11 | TAG_DOMAIN = 1; 12 | TAG_PERSONALIZATION = 2; 13 | TAG_EPOCH = 3; 14 | TAG_EXPIRES_AT = 4; 15 | TAG_COUNTER = 5; 16 | TAG_CHALLENGE = 6; 17 | TAG_FLAGS = 7; 18 | TAG_END = 255; 19 | } 20 | 21 | enum SignatureType 22 | { 23 | SIGNATURE_TYPE_AES_GCM = 0; 24 | SIGNATURE_TYPE_AES_GCM_PERSONALIZED = 5; 25 | SIGNATURE_TYPE_HMAC = 6; 26 | reserved 7; 27 | SIGNATURE_TYPE_HMAC_PERSONALIZED = 8; 28 | } 29 | 30 | message KeyIdentity { 31 | reserved 2; 32 | oneof identity_type { 33 | bytes public_key = 1; 34 | } 35 | } 36 | 37 | message AES_GCM_Personalized_Signature_Data { 38 | bytes epoch = 1; 39 | bytes nonce = 2; 40 | uint32 counter = 3; 41 | fixed32 expires_at = 4; 42 | bytes tag = 5; 43 | } 44 | 45 | message HMAC_Signature_Data { 46 | bytes tag = 1; 47 | } 48 | 49 | message HMAC_Personalized_Signature_Data { 50 | bytes epoch = 1; 51 | uint32 counter = 2; 52 | fixed32 expires_at = 3; 53 | bytes tag = 4; 54 | } 55 | 56 | message SignatureData { 57 | reserved 7; 58 | KeyIdentity signer_identity = 1; 59 | oneof sig_type { 60 | AES_GCM_Personalized_Signature_Data AES_GCM_Personalized_data = 5; 61 | HMAC_Signature_Data session_info_tag = 6; 62 | HMAC_Personalized_Signature_Data HMAC_Personalized_data = 8; 63 | } 64 | } 65 | 66 | message GetSessionInfoRequest { 67 | KeyIdentity key_identity = 1; 68 | } 69 | 70 | enum Session_Info_Status { 71 | SESSION_INFO_STATUS_OK = 0; 72 | SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST = 1; 73 | } 74 | 75 | 76 | message SessionInfo { 77 | uint32 counter = 1; 78 | bytes publicKey = 2; 79 | bytes epoch = 3; 80 | fixed32 clock_time = 4; 81 | Session_Info_Status status = 5; 82 | } 83 | -------------------------------------------------------------------------------- /protobuf/universal_message.options: -------------------------------------------------------------------------------- 1 | ## Destination 2 | UniversalMessage.Destination.routing_address max_size:16 3 | 4 | ## SessionInfoRequest 5 | UniversalMessage.SessionInfoRequest.public_key max_size:65 6 | UniversalMessage.SessionInfoRequest.challenge max_size:32 7 | 8 | ## RoutableMessage 9 | UniversalMessage.RoutableMessage.protobuf_message_as_bytes max_size:100 10 | UniversalMessage.RoutableMessage.session_info max_size:100 11 | UniversalMessage.RoutableMessage.request_uuid max_size:16 12 | UniversalMessage.RoutableMessage.uuid max_size:16 -------------------------------------------------------------------------------- /protobuf/universal_message.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package UniversalMessage; 4 | 5 | option go_package = "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/universalmessage"; 6 | option java_package = "com.tesla.generated.universalmessage"; 7 | 8 | import "signatures.proto"; 9 | 10 | enum Domain { 11 | DOMAIN_BROADCAST = 0; 12 | DOMAIN_VEHICLE_SECURITY = 2; 13 | DOMAIN_INFOTAINMENT = 3; 14 | } 15 | 16 | message Destination { 17 | oneof sub_destination{ 18 | Domain domain = 1; 19 | bytes routing_address = 2; 20 | } 21 | } 22 | 23 | enum OperationStatus_E 24 | { 25 | OPERATIONSTATUS_OK = 0; 26 | OPERATIONSTATUS_WAIT = 1; 27 | OPERATIONSTATUS_ERROR = 2; 28 | } 29 | 30 | enum MessageFault_E 31 | { 32 | MESSAGEFAULT_ERROR_NONE = 0; // Request succeeded. 33 | MESSAGEFAULT_ERROR_BUSY = 1; // Required vehicle subsystem is busy. Try again. 34 | MESSAGEFAULT_ERROR_TIMEOUT = 2; // Vehicle subsystem did not respond. Try again. 35 | MESSAGEFAULT_ERROR_UNKNOWN_KEY_ID = 3; // Vehicle did not recognize the key used to authorize command. Make sure your key is paired with the vehicle. 36 | MESSAGEFAULT_ERROR_INACTIVE_KEY = 4; // Key used to authorize command has been disabled. 37 | MESSAGEFAULT_ERROR_INVALID_SIGNATURE = 5; // Command signature/MAC is incorrect. Use included session info to update session and try again. 38 | MESSAGEFAULT_ERROR_INVALID_TOKEN_OR_COUNTER = 6; // Command anti-replay counter has been used before. Use included session info to update session and try again. 39 | MESSAGEFAULT_ERROR_INSUFFICIENT_PRIVILEGES = 7; // User is not authorized to execute command. This can be because of the role or because of vehicle state. 40 | MESSAGEFAULT_ERROR_INVALID_DOMAINS = 8; // Command was malformed or addressed to an unrecognized vehicle system. May indicate client error or older vehicle firmware. 41 | MESSAGEFAULT_ERROR_INVALID_COMMAND = 9; // Unrecognized command. May indicate client error or unsupported vehicle firmware. 42 | MESSAGEFAULT_ERROR_DECODING = 10; // Could not parse command. Indicates client error. 43 | MESSAGEFAULT_ERROR_INTERNAL = 11; // Internal vehicle error. Try again. Most commonly encountered when the vehicle has not finished booting. 44 | MESSAGEFAULT_ERROR_WRONG_PERSONALIZATION = 12; // Command sent to wrong VIN. 45 | MESSAGEFAULT_ERROR_BAD_PARAMETER = 13; // Command was malformed or used a deprecated parameter. 46 | MESSAGEFAULT_ERROR_KEYCHAIN_IS_FULL = 14; // Vehicle's keychain is full. You must delete a key before you can add another. 47 | MESSAGEFAULT_ERROR_INCORRECT_EPOCH = 15; // Session ID mismatch. Use included session info to update session and try again. 48 | MESSAGEFAULT_ERROR_IV_INCORRECT_LENGTH = 16; // Initialization Value length is incorrect (AES-GCM must use 12-byte IVs). Indicates a client programming error. 49 | MESSAGEFAULT_ERROR_TIME_EXPIRED = 17; // Command expired. Use included session info to determine if clocks have desynchronized and try again. 50 | MESSAGEFAULT_ERROR_NOT_PROVISIONED_WITH_IDENTITY = 18; // Vehicle has not been provisioned with a VIN and may require service. 51 | MESSAGEFAULT_ERROR_COULD_NOT_HASH_METADATA = 19; // Internal vehicle error. 52 | MESSAGEFAULT_ERROR_TIME_TO_LIVE_TOO_LONG = 20; // Vehicle rejected command because its expiration time was too far in the future. This is a security precaution. 53 | MESSAGEFAULT_ERROR_REMOTE_ACCESS_DISABLED = 21; // The vehicle owner has disabled Mobile access. 54 | MESSAGEFAULT_ERROR_REMOTE_SERVICE_ACCESS_DISABLED = 22; // The command was authorized with a Service key, but the vehicle has not been configured to permit remote service commands. 55 | MESSAGEFAULT_ERROR_COMMAND_REQUIRES_ACCOUNT_CREDENTIALS = 23; // The command requires proof of Tesla account credentials but was not sent over a channel that provides this proof. Resend the command using Fleet API. 56 | } 57 | 58 | message MessageStatus 59 | { 60 | OperationStatus_E operation_status = 1; 61 | MessageFault_E signed_message_fault = 2; 62 | } 63 | 64 | message SessionInfoRequest 65 | { 66 | bytes public_key = 1; 67 | bytes challenge = 2; 68 | } 69 | 70 | enum Flags { 71 | FLAG_USER_COMMAND = 0; 72 | } 73 | 74 | message RoutableMessage { 75 | reserved 1 to 5; 76 | reserved 16 to 40; 77 | reserved 11; 78 | Destination to_destination = 6; 79 | Destination from_destination = 7; 80 | 81 | oneof payload { 82 | bytes protobuf_message_as_bytes = 10; 83 | SessionInfoRequest session_info_request = 14; 84 | bytes session_info = 15; 85 | } 86 | 87 | oneof sub_sigData { 88 | Signatures.SignatureData signature_data = 13; 89 | } 90 | 91 | MessageStatus signedMessageStatus = 12; 92 | bytes request_uuid = 50; 93 | bytes uuid = 51; 94 | uint32 flags = 52; 95 | } -------------------------------------------------------------------------------- /protobuf/vcsec.options: -------------------------------------------------------------------------------- 1 | VCSEC.PublicKey.PublicKeyRaw max_size:65 type:FT_STATIC 2 | VCSEC.SignedMessage.protobufMessageAsBytes max_size:100 3 | 4 | VCSEC.KeyIdentifier.publicKeySHA1 max_size:4 type:FT_STATIC 5 | VCSEC.InformationRequest.publicKey max_size:65 type:FT_STATIC 6 | -------------------------------------------------------------------------------- /protobuf/vcsec.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package VCSEC; 4 | option java_package = "com.tesla.generated.vcsec"; 5 | option go_package = "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/vcsec"; 6 | 7 | import "keys.proto"; 8 | import "errors.proto"; 9 | 10 | enum SignatureType 11 | { 12 | SIGNATURE_TYPE_NONE = 0; 13 | SIGNATURE_TYPE_PRESENT_KEY = 2; 14 | } 15 | 16 | message SignedMessage { 17 | bytes protobufMessageAsBytes = 2; 18 | SignatureType signatureType = 3; 19 | } 20 | 21 | message ToVCSECMessage { 22 | SignedMessage signedMessage = 1; 23 | } 24 | 25 | message KeyIdentifier { 26 | bytes publicKeySHA1 = 1; 27 | } 28 | 29 | enum KeyFormFactor 30 | { 31 | KEY_FORM_FACTOR_UNKNOWN = 0; 32 | KEY_FORM_FACTOR_NFC_CARD = 1; 33 | KEY_FORM_FACTOR_IOS_DEVICE = 6; 34 | KEY_FORM_FACTOR_ANDROID_DEVICE = 7; 35 | KEY_FORM_FACTOR_CLOUD_KEY = 9; 36 | } 37 | 38 | message KeyMetadata 39 | { 40 | KeyFormFactor keyFormFactor = 1; 41 | } 42 | 43 | message PublicKey { 44 | bytes PublicKeyRaw = 1; 45 | } 46 | 47 | enum InformationRequestType { 48 | INFORMATION_REQUEST_TYPE_GET_STATUS = 0; 49 | INFORMATION_REQUEST_TYPE_GET_WHITELIST_INFO = 5; 50 | INFORMATION_REQUEST_TYPE_GET_WHITELIST_ENTRY_INFO = 6; 51 | } 52 | 53 | message WhitelistInfo { 54 | uint32 numberOfEntries = 1; 55 | repeated KeyIdentifier whitelistEntries = 2; 56 | uint32 slotMask = 3; 57 | } 58 | 59 | message WhitelistEntryInfo { 60 | KeyIdentifier keyId = 1; 61 | PublicKey publicKey = 2; 62 | KeyMetadata metadataForKey = 4; 63 | uint32 slot = 6; 64 | Keys.Role keyRole = 7; 65 | } 66 | 67 | message InformationRequest { 68 | InformationRequestType informationRequestType = 1; 69 | oneof key { 70 | KeyIdentifier keyId = 2; 71 | bytes publicKey = 3; 72 | uint32 slot = 4; 73 | } 74 | } 75 | 76 | enum RKEAction_E { 77 | RKE_ACTION_UNLOCK = 0; 78 | RKE_ACTION_LOCK = 1; 79 | RKE_ACTION_REMOTE_DRIVE = 20; 80 | RKE_ACTION_AUTO_SECURE_VEHICLE = 29; 81 | RKE_ACTION_WAKE_VEHICLE = 30; 82 | } 83 | 84 | enum ClosureMoveType_E{ 85 | 86 | CLOSURE_MOVE_TYPE_NONE = 0; 87 | CLOSURE_MOVE_TYPE_MOVE = 1; 88 | CLOSURE_MOVE_TYPE_STOP = 2; 89 | CLOSURE_MOVE_TYPE_OPEN = 3; 90 | CLOSURE_MOVE_TYPE_CLOSE = 4; 91 | } 92 | 93 | message ClosureMoveRequest { 94 | ClosureMoveType_E frontDriverDoor = 1; 95 | ClosureMoveType_E frontPassengerDoor = 2; 96 | ClosureMoveType_E rearDriverDoor = 3; 97 | ClosureMoveType_E rearPassengerDoor = 4; 98 | ClosureMoveType_E rearTrunk = 5; 99 | ClosureMoveType_E frontTrunk = 6; 100 | ClosureMoveType_E chargePort = 7; 101 | ClosureMoveType_E tonneau = 8; 102 | } 103 | 104 | message PermissionChange { 105 | PublicKey key = 1; 106 | uint32 secondsToBeActive = 3; 107 | Keys.Role keyRole = 4; 108 | } 109 | 110 | message ReplaceKey { 111 | oneof keyToReplace 112 | { 113 | PublicKey publicKeyToReplace = 1; 114 | uint32 slotToReplace = 2; 115 | } 116 | PublicKey keyToAdd = 3; 117 | Keys.Role keyRole = 4; 118 | bool impermanent = 5; 119 | } 120 | 121 | message WhitelistOperation { 122 | oneof sub_message { 123 | PublicKey addPublicKeyToWhitelist = 1; 124 | PublicKey removePublicKeyFromWhitelist = 2; 125 | PermissionChange addPermissionsToPublicKey = 3; 126 | PermissionChange removePermissionsFromPublicKey = 4; 127 | PermissionChange addKeyToWhitelistAndAddPermissions = 5; 128 | PermissionChange updateKeyAndPermissions = 7; 129 | PermissionChange addImpermanentKey = 8; 130 | PermissionChange addImpermanentKeyAndRemoveExisting = 9; 131 | bool removeAllImpermanentKeys = 16; 132 | 133 | ReplaceKey replaceKey = 17; 134 | } 135 | KeyMetadata metadataForKey = 6; 136 | } 137 | 138 | enum OperationStatus_E 139 | { 140 | OPERATIONSTATUS_OK = 0; 141 | OPERATIONSTATUS_WAIT = 1; 142 | OPERATIONSTATUS_ERROR = 2; 143 | } 144 | 145 | enum SignedMessage_information_E 146 | { 147 | SIGNEDMESSAGE_INFORMATION_NONE = 0; 148 | SIGNEDMESSAGE_INFORMATION_FAULT_UNKNOWN = 1; 149 | SIGNEDMESSAGE_INFORMATION_FAULT_NOT_ON_WHITELIST = 2; 150 | SIGNEDMESSAGE_INFORMATION_FAULT_IV_SMALLER_THAN_EXPECTED = 3; 151 | SIGNEDMESSAGE_INFORMATION_FAULT_INVALID_TOKEN = 4; 152 | SIGNEDMESSAGE_INFORMATION_FAULT_TOKEN_AND_COUNTER_INVALID = 5; 153 | SIGNEDMESSAGE_INFORMATION_FAULT_AES_DECRYPT_AUTH = 6; 154 | SIGNEDMESSAGE_INFORMATION_FAULT_ECDSA_INPUT = 7; 155 | SIGNEDMESSAGE_INFORMATION_FAULT_ECDSA_SIGNATURE = 8; 156 | SIGNEDMESSAGE_INFORMATION_FAULT_LOCAL_ENTITY_START = 9; 157 | SIGNEDMESSAGE_INFORMATION_FAULT_LOCAL_ENTITY_RESULT = 10; 158 | SIGNEDMESSAGE_INFORMATION_FAULT_COULD_NOT_RETRIEVE_KEY = 11; 159 | SIGNEDMESSAGE_INFORMATION_FAULT_COULD_NOT_RETRIEVE_TOKEN = 12; 160 | SIGNEDMESSAGE_INFORMATION_FAULT_SIGNATURE_TOO_SHORT = 13; 161 | SIGNEDMESSAGE_INFORMATION_FAULT_TOKEN_IS_INCORRECT_LENGTH = 14; 162 | SIGNEDMESSAGE_INFORMATION_FAULT_INCORRECT_EPOCH = 15; 163 | SIGNEDMESSAGE_INFORMATION_FAULT_IV_INCORRECT_LENGTH = 16; 164 | SIGNEDMESSAGE_INFORMATION_FAULT_TIME_EXPIRED = 17; 165 | SIGNEDMESSAGE_INFORMATION_FAULT_NOT_PROVISIONED_WITH_IDENTITY = 18; 166 | SIGNEDMESSAGE_INFORMATION_FAULT_COULD_NOT_HASH_METADATA = 19; 167 | } 168 | 169 | enum WhitelistOperation_information_E 170 | { 171 | WHITELISTOPERATION_INFORMATION_NONE = 0; 172 | WHITELISTOPERATION_INFORMATION_UNDOCUMENTED_ERROR = 1; 173 | WHITELISTOPERATION_INFORMATION_NO_PERMISSION_TO_REMOVE_ONESELF = 2; 174 | WHITELISTOPERATION_INFORMATION_KEYFOB_SLOTS_FULL = 3; 175 | WHITELISTOPERATION_INFORMATION_WHITELIST_FULL = 4; 176 | WHITELISTOPERATION_INFORMATION_NO_PERMISSION_TO_ADD = 5; 177 | WHITELISTOPERATION_INFORMATION_INVALID_PUBLIC_KEY = 6; 178 | WHITELISTOPERATION_INFORMATION_NO_PERMISSION_TO_REMOVE = 7; 179 | WHITELISTOPERATION_INFORMATION_NO_PERMISSION_TO_CHANGE_PERMISSIONS = 8; 180 | WHITELISTOPERATION_INFORMATION_ATTEMPTING_TO_ELEVATE_OTHER_ABOVE_ONESELF = 9; 181 | WHITELISTOPERATION_INFORMATION_ATTEMPTING_TO_DEMOTE_SUPERIOR_TO_ONESELF = 10; 182 | WHITELISTOPERATION_INFORMATION_ATTEMPTING_TO_REMOVE_OWN_PERMISSIONS = 11; 183 | WHITELISTOPERATION_INFORMATION_PUBLIC_KEY_NOT_ON_WHITELIST = 12; 184 | WHITELISTOPERATION_INFORMATION_ATTEMPTING_TO_ADD_KEY_THAT_IS_ALREADY_ON_THE_WHITELIST = 13; 185 | WHITELISTOPERATION_INFORMATION_NOT_ALLOWED_TO_ADD_UNLESS_ON_READER = 14; 186 | WHITELISTOPERATION_INFORMATION_FM_MODIFYING_OUTSIDE_OF_F_MODE = 15; 187 | WHITELISTOPERATION_INFORMATION_FM_ATTEMPTING_TO_ADD_PERMANENT_KEY = 16; 188 | WHITELISTOPERATION_INFORMATION_FM_ATTEMPTING_TO_REMOVE_PERMANENT_KEY = 17; 189 | WHITELISTOPERATION_INFORMATION_KEYCHAIN_WHILE_FS_FULL = 18; 190 | WHITELISTOPERATION_INFORMATION_ATTEMPTING_TO_ADD_KEY_WITHOUT_ROLE = 19; 191 | WHITELISTOPERATION_INFORMATION_ATTEMPTING_TO_ADD_KEY_WITH_SERVICE_ROLE = 20; 192 | WHITELISTOPERATION_INFORMATION_NON_SERVICE_KEY_ATTEMPTING_TO_ADD_SERVICE_TECH = 21; 193 | WHITELISTOPERATION_INFORMATION_SERVICE_KEY_ATTEMPTING_TO_ADD_SERVICE_TECH_OUTSIDE_SERVICE_MODE = 22; 194 | } 195 | 196 | message WhitelistOperation_status 197 | { 198 | WhitelistOperation_information_E whitelistOperationInformation = 1; 199 | KeyIdentifier signerOfOperation = 2; 200 | OperationStatus_E operationStatus = 3; 201 | } 202 | 203 | message SignedMessage_status 204 | { 205 | uint32 counter = 1; 206 | SignedMessage_information_E signedMessageInformation = 2; 207 | } 208 | 209 | message CommandStatus 210 | { 211 | OperationStatus_E operationStatus = 1; 212 | oneof sub_message { 213 | SignedMessage_status signedMessageStatus = 2; 214 | WhitelistOperation_status whitelistOperationStatus = 3; 215 | } 216 | } 217 | 218 | message UnsignedMessage { 219 | reserved 6,7,10,12,13; 220 | oneof sub_message { 221 | InformationRequest InformationRequest = 1; 222 | RKEAction_E RKEAction = 2; 223 | ClosureMoveRequest closureMoveRequest = 4; 224 | WhitelistOperation WhitelistOperation = 16; 225 | } 226 | } 227 | 228 | enum ClosureState_E { 229 | CLOSURESTATE_CLOSED = 0; 230 | CLOSURESTATE_OPEN = 1; 231 | CLOSURESTATE_AJAR = 2; 232 | CLOSURESTATE_UNKNOWN = 3; 233 | CLOSURESTATE_FAILED_UNLATCH = 4; 234 | CLOSURESTATE_OPENING = 5; 235 | CLOSURESTATE_CLOSING = 6; 236 | } 237 | 238 | enum VehicleLockState_E { 239 | VEHICLELOCKSTATE_UNLOCKED = 0; 240 | VEHICLELOCKSTATE_LOCKED = 1; 241 | VEHICLELOCKSTATE_INTERNAL_LOCKED = 2; 242 | VEHICLELOCKSTATE_SELECTIVE_UNLOCKED = 3; 243 | } 244 | 245 | message ClosureStatuses 246 | { 247 | ClosureState_E frontDriverDoor = 1; 248 | ClosureState_E frontPassengerDoor = 2; 249 | ClosureState_E rearDriverDoor = 3; 250 | ClosureState_E rearPassengerDoor = 4; 251 | ClosureState_E rearTrunk = 5; 252 | ClosureState_E frontTrunk = 6; 253 | ClosureState_E chargePort = 7; 254 | ClosureState_E tonneau = 8; 255 | } 256 | 257 | enum VehicleSleepStatus_E { 258 | VEHICLE_SLEEP_STATUS_UNKNOWN = 0; 259 | VEHICLE_SLEEP_STATUS_AWAKE = 1; 260 | VEHICLE_SLEEP_STATUS_ASLEEP = 2; 261 | } 262 | 263 | message DetailedClosureStatus { 264 | uint32 tonneauPercentOpen = 1; 265 | } 266 | 267 | enum UserPresence_E { 268 | VEHICLE_USER_PRESENCE_UNKNOWN = 0; 269 | VEHICLE_USER_PRESENCE_NOT_PRESENT = 1; 270 | VEHICLE_USER_PRESENCE_PRESENT = 2; 271 | } 272 | 273 | message VehicleStatus { 274 | ClosureStatuses closureStatuses = 1; 275 | VehicleLockState_E vehicleLockState = 2; 276 | VehicleSleepStatus_E vehicleSleepStatus = 3; 277 | UserPresence_E userPresence = 4; 278 | DetailedClosureStatus detailedClosureStatus = 5; 279 | } 280 | 281 | message FromVCSECMessage { 282 | reserved 6 to 10; 283 | oneof sub_message { 284 | VehicleStatus vehicleStatus = 1; 285 | CommandStatus commandStatus = 4; 286 | WhitelistInfo whitelistInfo = 16; 287 | WhitelistEntryInfo whitelistEntryInfo = 17; 288 | Errors.NominalError nominalError = 46; 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /protobuf/vehicle.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package CarServer; 4 | 5 | option go_package = "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/carserver"; 6 | option java_package = "com.tesla.generated.carserver.vehicle"; 7 | option java_outer_classname = "Vehicle"; 8 | 9 | message VehicleState { 10 | message GuestMode { 11 | bool GuestModeActive = 1; 12 | } 13 | GuestMode guestMode = 74; 14 | } 15 | 16 | message ClimateState { 17 | enum CopActivationTemp { 18 | CopActivationTempUnspecified = 0; 19 | CopActivationTempLow = 1; 20 | CopActivationTempMedium = 2; 21 | CopActivationTempHigh = 3; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/car_server.pb.c: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb constant definitions */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #include "car_server.pb.h" 5 | #if PB_PROTO_HEADER_VERSION != 40 6 | #error Regenerate this file with the current version of nanopb generator. 7 | #endif 8 | 9 | PB_BIND(CarServer_Action, CarServer_Action, AUTO) 10 | 11 | 12 | PB_BIND(CarServer_VehicleAction, CarServer_VehicleAction, 2) 13 | 14 | 15 | PB_BIND(CarServer_EraseUserDataAction, CarServer_EraseUserDataAction, AUTO) 16 | 17 | 18 | PB_BIND(CarServer_Response, CarServer_Response, 2) 19 | 20 | 21 | PB_BIND(CarServer_ActionStatus, CarServer_ActionStatus, AUTO) 22 | 23 | 24 | PB_BIND(CarServer_ResultReason, CarServer_ResultReason, AUTO) 25 | 26 | 27 | PB_BIND(CarServer_EncryptedData, CarServer_EncryptedData, AUTO) 28 | 29 | 30 | PB_BIND(CarServer_ChargingSetLimitAction, CarServer_ChargingSetLimitAction, AUTO) 31 | 32 | 33 | PB_BIND(CarServer_ChargingStartStopAction, CarServer_ChargingStartStopAction, AUTO) 34 | 35 | 36 | PB_BIND(CarServer_DrivingClearSpeedLimitPinAction, CarServer_DrivingClearSpeedLimitPinAction, AUTO) 37 | 38 | 39 | PB_BIND(CarServer_DrivingSetSpeedLimitAction, CarServer_DrivingSetSpeedLimitAction, AUTO) 40 | 41 | 42 | PB_BIND(CarServer_DrivingSpeedLimitAction, CarServer_DrivingSpeedLimitAction, AUTO) 43 | 44 | 45 | PB_BIND(CarServer_HvacAutoAction, CarServer_HvacAutoAction, AUTO) 46 | 47 | 48 | PB_BIND(CarServer_HvacSeatHeaterActions, CarServer_HvacSeatHeaterActions, AUTO) 49 | 50 | 51 | PB_BIND(CarServer_HvacSeatHeaterActions_HvacSeatHeaterAction, CarServer_HvacSeatHeaterActions_HvacSeatHeaterAction, AUTO) 52 | 53 | 54 | PB_BIND(CarServer_HvacSeatCoolerActions, CarServer_HvacSeatCoolerActions, AUTO) 55 | 56 | 57 | PB_BIND(CarServer_HvacSeatCoolerActions_HvacSeatCoolerAction, CarServer_HvacSeatCoolerActions_HvacSeatCoolerAction, AUTO) 58 | 59 | 60 | PB_BIND(CarServer_HvacSetPreconditioningMaxAction, CarServer_HvacSetPreconditioningMaxAction, AUTO) 61 | 62 | 63 | PB_BIND(CarServer_HvacSteeringWheelHeaterAction, CarServer_HvacSteeringWheelHeaterAction, AUTO) 64 | 65 | 66 | PB_BIND(CarServer_HvacTemperatureAdjustmentAction, CarServer_HvacTemperatureAdjustmentAction, AUTO) 67 | 68 | 69 | PB_BIND(CarServer_HvacTemperatureAdjustmentAction_Temperature, CarServer_HvacTemperatureAdjustmentAction_Temperature, AUTO) 70 | 71 | 72 | PB_BIND(CarServer_HvacTemperatureAdjustmentAction_HvacTemperatureZone, CarServer_HvacTemperatureAdjustmentAction_HvacTemperatureZone, AUTO) 73 | 74 | 75 | PB_BIND(CarServer_GetNearbyChargingSites, CarServer_GetNearbyChargingSites, AUTO) 76 | 77 | 78 | PB_BIND(CarServer_NearbyChargingSites, CarServer_NearbyChargingSites, AUTO) 79 | 80 | 81 | PB_BIND(CarServer_Superchargers, CarServer_Superchargers, AUTO) 82 | 83 | 84 | PB_BIND(CarServer_MediaPlayAction, CarServer_MediaPlayAction, AUTO) 85 | 86 | 87 | PB_BIND(CarServer_MediaUpdateVolume, CarServer_MediaUpdateVolume, AUTO) 88 | 89 | 90 | PB_BIND(CarServer_MediaNextFavorite, CarServer_MediaNextFavorite, AUTO) 91 | 92 | 93 | PB_BIND(CarServer_MediaPreviousFavorite, CarServer_MediaPreviousFavorite, AUTO) 94 | 95 | 96 | PB_BIND(CarServer_MediaNextTrack, CarServer_MediaNextTrack, AUTO) 97 | 98 | 99 | PB_BIND(CarServer_MediaPreviousTrack, CarServer_MediaPreviousTrack, AUTO) 100 | 101 | 102 | PB_BIND(CarServer_VehicleControlCancelSoftwareUpdateAction, CarServer_VehicleControlCancelSoftwareUpdateAction, AUTO) 103 | 104 | 105 | PB_BIND(CarServer_VehicleControlFlashLightsAction, CarServer_VehicleControlFlashLightsAction, AUTO) 106 | 107 | 108 | PB_BIND(CarServer_VehicleControlHonkHornAction, CarServer_VehicleControlHonkHornAction, AUTO) 109 | 110 | 111 | PB_BIND(CarServer_VehicleControlResetValetPinAction, CarServer_VehicleControlResetValetPinAction, AUTO) 112 | 113 | 114 | PB_BIND(CarServer_VehicleControlScheduleSoftwareUpdateAction, CarServer_VehicleControlScheduleSoftwareUpdateAction, AUTO) 115 | 116 | 117 | PB_BIND(CarServer_VehicleControlSetSentryModeAction, CarServer_VehicleControlSetSentryModeAction, AUTO) 118 | 119 | 120 | PB_BIND(CarServer_VehicleControlSetValetModeAction, CarServer_VehicleControlSetValetModeAction, AUTO) 121 | 122 | 123 | PB_BIND(CarServer_VehicleControlSunroofOpenCloseAction, CarServer_VehicleControlSunroofOpenCloseAction, AUTO) 124 | 125 | 126 | PB_BIND(CarServer_VehicleControlTriggerHomelinkAction, CarServer_VehicleControlTriggerHomelinkAction, AUTO) 127 | 128 | 129 | PB_BIND(CarServer_VehicleControlWindowAction, CarServer_VehicleControlWindowAction, AUTO) 130 | 131 | 132 | PB_BIND(CarServer_HvacBioweaponModeAction, CarServer_HvacBioweaponModeAction, AUTO) 133 | 134 | 135 | PB_BIND(CarServer_AutoSeatClimateAction, CarServer_AutoSeatClimateAction, AUTO) 136 | 137 | 138 | PB_BIND(CarServer_AutoSeatClimateAction_CarSeat, CarServer_AutoSeatClimateAction_CarSeat, AUTO) 139 | 140 | 141 | PB_BIND(CarServer_Ping, CarServer_Ping, AUTO) 142 | 143 | 144 | PB_BIND(CarServer_ScheduledChargingAction, CarServer_ScheduledChargingAction, AUTO) 145 | 146 | 147 | PB_BIND(CarServer_ScheduledDepartureAction, CarServer_ScheduledDepartureAction, AUTO) 148 | 149 | 150 | PB_BIND(CarServer_HvacClimateKeeperAction, CarServer_HvacClimateKeeperAction, AUTO) 151 | 152 | 153 | PB_BIND(CarServer_SetChargingAmpsAction, CarServer_SetChargingAmpsAction, AUTO) 154 | 155 | 156 | PB_BIND(CarServer_SetCabinOverheatProtectionAction, CarServer_SetCabinOverheatProtectionAction, AUTO) 157 | 158 | 159 | PB_BIND(CarServer_SetVehicleNameAction, CarServer_SetVehicleNameAction, AUTO) 160 | 161 | 162 | PB_BIND(CarServer_ChargePortDoorClose, CarServer_ChargePortDoorClose, AUTO) 163 | 164 | 165 | PB_BIND(CarServer_ChargePortDoorOpen, CarServer_ChargePortDoorOpen, AUTO) 166 | 167 | 168 | PB_BIND(CarServer_SetCopTempAction, CarServer_SetCopTempAction, AUTO) 169 | 170 | 171 | PB_BIND(CarServer_VehicleControlSetPinToDriveAction, CarServer_VehicleControlSetPinToDriveAction, AUTO) 172 | 173 | 174 | PB_BIND(CarServer_VehicleControlResetPinToDriveAction, CarServer_VehicleControlResetPinToDriveAction, AUTO) 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | #ifndef PB_CONVERT_DOUBLE_FLOAT 185 | /* On some platforms (such as AVR), double is really float. 186 | * To be able to encode/decode double on these platforms, you need. 187 | * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. 188 | */ 189 | PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) 190 | #endif 191 | 192 | -------------------------------------------------------------------------------- /src/carserver.cpp: -------------------------------------------------------------------------------- 1 | #include "carserver.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace TeslaBLE { 11 | int CarServer::BuildActionMessage(CarServer_Action *car_server_action, unsigned char *buffer, 12 | size_t *buffer_size) { 13 | pb_ostream_t size_stream = {nullptr}; 14 | if (!pb_encode(&size_stream, CarServer_Action_fields, car_server_action)) { 15 | printf("Failed to encode message: %s", PB_GET_ERROR(&size_stream)); 16 | return ResultCode::NANOPB_ENCODE_ERROR; 17 | } 18 | 19 | pb_ostream_t stream = pb_ostream_from_buffer(buffer, size_stream.bytes_written); 20 | if (!pb_encode(&stream, CarServer_Action_fields, car_server_action)) { 21 | printf("Failed to encode message: %s", PB_GET_ERROR(&stream)); 22 | return ResultCode::NANOPB_ENCODE_ERROR; 23 | } 24 | 25 | *buffer_size = stream.bytes_written; 26 | return ResultCode::SUCCESS; 27 | } 28 | 29 | int CarServer::StartCharging(unsigned char *buffer, size_t *buffer_size) { 30 | CarServer_ChargingStartStopAction start_stop_charging = CarServer_ChargingStartStopAction_init_default; 31 | start_stop_charging.charging_action.start.dummy_field = 1; 32 | start_stop_charging.which_charging_action = CarServer_ChargingStartStopAction_start_tag; 33 | 34 | CarServer_VehicleAction vehicle_action = CarServer_VehicleAction_init_default; 35 | vehicle_action.which_vehicle_action_msg = CarServer_VehicleAction_chargingStartStopAction_tag; 36 | vehicle_action.vehicle_action_msg.chargingStartStopAction = start_stop_charging; 37 | 38 | CarServer_Action car_server_action = CarServer_Action_init_default; 39 | car_server_action.action_msg.vehicleAction = vehicle_action; 40 | car_server_action.which_action_msg = CarServer_Action_vehicleAction_tag; 41 | 42 | return CarServer::BuildActionMessage(&car_server_action, buffer, buffer_size); 43 | } 44 | 45 | int CarServer::StopCharging(unsigned char *buffer, size_t *buffer_size) { 46 | CarServer_ChargingStartStopAction start_stop_charging = CarServer_ChargingStartStopAction_init_default; 47 | start_stop_charging.charging_action.stop.dummy_field = 1; 48 | start_stop_charging.which_charging_action = CarServer_ChargingStartStopAction_start_tag; 49 | 50 | CarServer_VehicleAction vehicle_action = CarServer_VehicleAction_init_default; 51 | vehicle_action.which_vehicle_action_msg = CarServer_VehicleAction_chargingStartStopAction_tag; 52 | vehicle_action.vehicle_action_msg.chargingStartStopAction = start_stop_charging; 53 | 54 | CarServer_Action car_server_action = CarServer_Action_init_default; 55 | car_server_action.action_msg.vehicleAction = vehicle_action; 56 | car_server_action.which_action_msg = CarServer_Action_vehicleAction_tag; 57 | 58 | return CarServer::BuildActionMessage(&car_server_action, buffer, buffer_size); 59 | } 60 | 61 | int CarServer::OpenChargePort(unsigned char *buffer, size_t *buffer_size) { 62 | CarServer_ChargePortDoorOpen open_chargeport = CarServer_ChargePortDoorOpen_init_default; 63 | open_chargeport.dummy_field = 1; 64 | 65 | CarServer_VehicleAction vehicle_action = CarServer_VehicleAction_init_default; 66 | vehicle_action.which_vehicle_action_msg = CarServer_VehicleAction_chargePortDoorOpen_tag; 67 | vehicle_action.vehicle_action_msg.chargePortDoorOpen = open_chargeport; 68 | 69 | CarServer_Action car_server_action = CarServer_Action_init_default; 70 | car_server_action.action_msg.vehicleAction = vehicle_action; 71 | car_server_action.which_action_msg = CarServer_Action_vehicleAction_tag; 72 | 73 | return CarServer::BuildActionMessage(&car_server_action, buffer, buffer_size); 74 | } 75 | 76 | int CarServer::CloseChargePort(unsigned char *buffer, size_t *buffer_size) { 77 | CarServer_ChargePortDoorOpen open_chargeport = CarServer_ChargePortDoorOpen_init_default; 78 | open_chargeport.dummy_field = 1; 79 | 80 | CarServer_VehicleAction vehicle_action = CarServer_VehicleAction_init_default; 81 | vehicle_action.which_vehicle_action_msg = CarServer_VehicleAction_chargePortDoorOpen_tag; 82 | vehicle_action.vehicle_action_msg.chargePortDoorOpen = open_chargeport; 83 | 84 | CarServer_Action car_server_action = CarServer_Action_init_default; 85 | car_server_action.action_msg.vehicleAction = vehicle_action; 86 | car_server_action.which_action_msg = CarServer_Action_vehicleAction_tag; 87 | 88 | return CarServer::BuildActionMessage(&car_server_action, buffer, buffer_size); 89 | } 90 | 91 | int CarServer::ToggleClimate(bool status, unsigned char *buffer, size_t *buffer_size) { 92 | CarServer_HvacAutoAction hvac_auto_action = CarServer_HvacAutoAction_init_default; 93 | hvac_auto_action.power_on = status; 94 | 95 | CarServer_VehicleAction vehicle_action = CarServer_VehicleAction_init_default; 96 | vehicle_action.which_vehicle_action_msg = CarServer_VehicleAction_hvacAutoAction_tag; 97 | vehicle_action.vehicle_action_msg.hvacAutoAction = hvac_auto_action; 98 | 99 | CarServer_Action car_server_action = CarServer_Action_init_default; 100 | car_server_action.action_msg.vehicleAction = vehicle_action; 101 | car_server_action.which_action_msg = CarServer_Action_vehicleAction_tag; 102 | 103 | return CarServer::BuildActionMessage(&car_server_action, buffer, buffer_size); 104 | } 105 | 106 | 107 | int CarServer::TurnOnClimate(unsigned char *buffer, size_t *buffer_size) { 108 | return CarServer::ToggleClimate(true, buffer, buffer_size); 109 | } 110 | 111 | int CarServer::TurnOffClimate(unsigned char *buffer, size_t *buffer_size) { 112 | return CarServer::ToggleClimate(false, buffer, buffer_size); 113 | } 114 | 115 | int CarServer::NextMediaTrack(unsigned char *buffer, size_t *buffer_size) { 116 | CarServer_MediaNextTrack media_next_track = CarServer_MediaNextTrack_init_default; 117 | media_next_track.dummy_field = 1; 118 | 119 | CarServer_VehicleAction vehicle_action = CarServer_VehicleAction_init_default; 120 | vehicle_action.which_vehicle_action_msg = CarServer_VehicleAction_mediaNextTrack_tag; 121 | vehicle_action.vehicle_action_msg.mediaNextTrack = media_next_track; 122 | 123 | CarServer_Action car_server_action = CarServer_Action_init_default; 124 | car_server_action.action_msg.vehicleAction = vehicle_action; 125 | car_server_action.which_action_msg = CarServer_Action_vehicleAction_tag; 126 | 127 | return CarServer::BuildActionMessage(&car_server_action, buffer, buffer_size); 128 | } 129 | 130 | int CarServer::PlayMedia(unsigned char *buffer, size_t *buffer_size) { 131 | CarServer_MediaPlayAction play_action = CarServer_MediaPlayAction_init_default; 132 | play_action.dummy_field = 1; 133 | 134 | CarServer_VehicleAction vehicle_action = CarServer_VehicleAction_init_default; 135 | vehicle_action.which_vehicle_action_msg = CarServer_VehicleAction_mediaPlayAction_tag; 136 | vehicle_action.vehicle_action_msg.mediaPlayAction = play_action; 137 | 138 | CarServer_Action car_server_action = CarServer_Action_init_default; 139 | car_server_action.action_msg.vehicleAction = vehicle_action; 140 | car_server_action.which_action_msg = CarServer_Action_vehicleAction_tag; 141 | 142 | return CarServer::BuildActionMessage(&car_server_action, buffer, buffer_size); 143 | } 144 | 145 | int CarServer::SetVolume(float absolute, unsigned char *buffer, size_t *buffer_size) { 146 | CarServer_MediaUpdateVolume update_volume = CarServer_MediaUpdateVolume_init_default; 147 | update_volume.media_volume.volume_absolute_float = absolute; 148 | update_volume.which_media_volume = CarServer_MediaUpdateVolume_volume_absolute_float_tag; 149 | 150 | CarServer_VehicleAction vehicle_action = CarServer_VehicleAction_init_default; 151 | vehicle_action.which_vehicle_action_msg = CarServer_VehicleAction_mediaUpdateVolume_tag; 152 | vehicle_action.vehicle_action_msg.mediaUpdateVolume = update_volume; 153 | 154 | CarServer_Action car_server_action = CarServer_Action_init_default; 155 | car_server_action.action_msg.vehicleAction = vehicle_action; 156 | car_server_action.which_action_msg = CarServer_Action_vehicleAction_tag; 157 | 158 | return CarServer::BuildActionMessage(&car_server_action, buffer, buffer_size); 159 | } 160 | 161 | int CarServer::SetChargingLimit(int32_t percent, unsigned char *buffer, size_t *buffer_size) { 162 | CarServer_ChargingSetLimitAction set_charging_limit_action = CarServer_ChargingSetLimitAction_init_default; 163 | set_charging_limit_action.percent = percent; 164 | 165 | CarServer_VehicleAction vehicle_action = CarServer_VehicleAction_init_default; 166 | vehicle_action.which_vehicle_action_msg = CarServer_VehicleAction_chargingSetLimitAction_tag; 167 | vehicle_action.vehicle_action_msg.chargingSetLimitAction = set_charging_limit_action; 168 | 169 | CarServer_Action car_server_action = CarServer_Action_init_default; 170 | car_server_action.action_msg.vehicleAction = vehicle_action; 171 | car_server_action.which_action_msg = CarServer_Action_vehicleAction_tag; 172 | 173 | return CarServer::BuildActionMessage(&car_server_action, buffer, buffer_size); 174 | } 175 | 176 | int CarServer::Vent(unsigned char *buffer, size_t *buffer_size) { 177 | CarServer_VehicleControlWindowAction window_action = CarServer_VehicleControlWindowAction_init_default; 178 | window_action.action.vent.dummy_field = 1; 179 | window_action.which_action = CarServer_VehicleControlWindowAction_vent_tag; 180 | 181 | CarServer_VehicleAction vehicle_action = CarServer_VehicleAction_init_default; 182 | vehicle_action.which_vehicle_action_msg = CarServer_VehicleAction_vehicleControlWindowAction_tag; 183 | vehicle_action.vehicle_action_msg.vehicleControlWindowAction = window_action; 184 | 185 | CarServer_Action car_server_action = CarServer_Action_init_default; 186 | car_server_action.action_msg.vehicleAction = vehicle_action; 187 | car_server_action.which_action_msg = CarServer_Action_vehicleAction_tag; 188 | 189 | return CarServer::BuildActionMessage(&car_server_action, buffer, buffer_size); 190 | } 191 | } // TeslaBLE 192 | -------------------------------------------------------------------------------- /src/common.pb.c: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb constant definitions */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #include "common.pb.h" 5 | #if PB_PROTO_HEADER_VERSION != 40 6 | #error Regenerate this file with the current version of nanopb generator. 7 | #endif 8 | 9 | PB_BIND(CarServer_Void, CarServer_Void, AUTO) 10 | 11 | 12 | PB_BIND(CarServer_LatLong, CarServer_LatLong, AUTO) 13 | 14 | 15 | PB_BIND(CarServer_PreconditioningTimes, CarServer_PreconditioningTimes, AUTO) 16 | 17 | 18 | PB_BIND(CarServer_OffPeakChargingTimes, CarServer_OffPeakChargingTimes, AUTO) 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/errors.pb.c: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb constant definitions */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #include "errors.pb.h" 5 | #if PB_PROTO_HEADER_VERSION != 40 6 | #error Regenerate this file with the current version of nanopb generator. 7 | #endif 8 | 9 | PB_BIND(Errors_NominalError, Errors_NominalError, AUTO) 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/keys.pb.c: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb constant definitions */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #include "keys.pb.h" 5 | #if PB_PROTO_HEADER_VERSION != 40 6 | #error Regenerate this file with the current version of nanopb generator. 7 | #endif 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/metadata.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TeslaBLE © 2024 by Pascal Matthiesen 3 | */ 4 | 5 | #include "metadata.h" 6 | 7 | #ifdef ESP_PLATFORM 8 | #define MBEDTLS_CONFIG_FILE "mbedtls/esp_config.h" 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace TeslaBLE { 18 | int MetaData::Start() { 19 | this->last_tag = 0; 20 | mbedtls_sha256_init(&this->sha256_context_); 21 | int return_code = mbedtls_sha256_starts(&this->sha256_context_, 0); 22 | if (return_code != 0) { 23 | Common::PrintErrorFromMbedTlsErrorCode(return_code); 24 | return ResultCode::MBEDTLS_ERROR; 25 | } 26 | 27 | return ResultCode::SUCCESS; 28 | } 29 | 30 | int MetaData::BuildMetadata(UniversalMessage_Domain destination, Signatures_SignatureType method, 31 | unsigned char *vin, uint32_t expiresAt, uint32_t counter, unsigned char *epoch) { 32 | unsigned char method_[1]; 33 | memcpy(method_, &method, 1); 34 | int result_code = this->Add(Signatures_Tag_TAG_SIGNATURE_TYPE, method_, 1); 35 | if (result_code != ResultCode::SUCCESS) { 36 | return result_code; 37 | } 38 | 39 | unsigned char domain[1]; 40 | memcpy(domain, &destination, 1); 41 | result_code = this->Add(Signatures_Tag_TAG_DOMAIN, domain, 1); 42 | if (result_code != ResultCode::SUCCESS) { 43 | return result_code; 44 | } 45 | 46 | result_code = this->Add(Signatures_Tag_TAG_PERSONALIZATION, vin, 17); 47 | if (result_code != ResultCode::SUCCESS) { 48 | return result_code; 49 | } 50 | 51 | result_code = this->Add(Signatures_Tag_TAG_EPOCH, epoch, 16); 52 | if (result_code != ResultCode::SUCCESS) { 53 | return result_code; 54 | } 55 | 56 | result_code = this->AddUint32(Signatures_Tag_TAG_EXPIRES_AT, expiresAt); 57 | if (result_code != ResultCode::SUCCESS) { 58 | return result_code; 59 | } 60 | 61 | return this->AddUint32(Signatures_Tag_TAG_COUNTER, counter); 62 | } 63 | 64 | int MetaData::Add(uint8_t signatures_tag, unsigned char *value, unsigned char value_size) { 65 | if (this->last_tag > signatures_tag) { 66 | return ResultCode::TAG_OUT_OF_ORDER; 67 | } 68 | 69 | if (value_size > 255) { 70 | return ResultCode::TAG_VALUE_TOO_LONG; 71 | } 72 | 73 | if (value_size == 0) { 74 | return ResultCode::TAG_VALUE_EMPTY; 75 | } 76 | 77 | this->last_tag = signatures_tag; 78 | 79 | int return_code = mbedtls_sha256_update(&this->sha256_context_, &signatures_tag, 1); 80 | if (return_code != 0) { 81 | Common::PrintErrorFromMbedTlsErrorCode(return_code); 82 | return ResultCode::MBEDTLS_ERROR; 83 | } 84 | 85 | return_code = mbedtls_sha256_update(&this->sha256_context_, &value_size, 1); 86 | if (return_code != 0) { 87 | Common::PrintErrorFromMbedTlsErrorCode(return_code); 88 | return ResultCode::MBEDTLS_ERROR; 89 | } 90 | 91 | return_code = mbedtls_sha256_update(&this->sha256_context_, value, value_size); 92 | if (return_code != 0) { 93 | Common::PrintErrorFromMbedTlsErrorCode(return_code); 94 | return ResultCode::MBEDTLS_ERROR; 95 | } 96 | 97 | return ResultCode::SUCCESS; 98 | } 99 | 100 | int MetaData::AddUint32(const unsigned char signatures_tag, const uint32_t value) { 101 | unsigned char buffer[4]; 102 | // Convert uint32_t to big-endian byte array 103 | buffer[0] = (value >> 24) & 0xFF; 104 | buffer[1] = (value >> 16) & 0xFF; 105 | buffer[2] = (value >> 8) & 0xFF; 106 | buffer[3] = value & 0xFF; 107 | 108 | return this->Add(signatures_tag, buffer, 4); 109 | } 110 | 111 | void MetaData::Checksum(unsigned char *output, const unsigned char end_tag) { 112 | int return_code = mbedtls_sha256_update(&this->sha256_context_, &end_tag, 1); 113 | if (return_code != 0) { 114 | Common::PrintErrorFromMbedTlsErrorCode(return_code); 115 | } 116 | 117 | return_code = mbedtls_sha256_finish(&this->sha256_context_, output); 118 | if (return_code != 0) { 119 | Common::PrintErrorFromMbedTlsErrorCode(return_code); 120 | } 121 | 122 | mbedtls_sha256_free(&this->sha256_context_); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/security.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TeslaBLE © 2024 by Pascal Matthiesen 3 | */ 4 | 5 | #include "security.h" 6 | 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace TeslaBLE { 16 | int Security::BuildUnsignedMessage(const VCSEC_UnsignedMessage *unsigned_message, unsigned char *buffer, 17 | size_t *buffer_size) { 18 | pb_ostream_t size_stream = {nullptr}; 19 | if (!pb_encode(&size_stream, VCSEC_UnsignedMessage_fields, unsigned_message)) { 20 | printf("Failed to encode message: %s", PB_GET_ERROR(&size_stream)); 21 | return ResultCode::NANOPB_ENCODE_ERROR; 22 | } 23 | 24 | pb_ostream_t stream = pb_ostream_from_buffer(buffer, size_stream.bytes_written); 25 | if (!pb_encode(&stream, VCSEC_UnsignedMessage_fields, unsigned_message)) { 26 | printf("Failed to encode message: %s", PB_GET_ERROR(&stream)); 27 | return ResultCode::NANOPB_ENCODE_ERROR; 28 | } 29 | 30 | *buffer_size = stream.bytes_written; 31 | return ResultCode::SUCCESS; 32 | } 33 | 34 | int Security::Unlock(unsigned char *buffer, size_t *buffer_size) { 35 | VCSEC_UnsignedMessage unsigned_message = VCSEC_UnsignedMessage{}; 36 | unsigned_message.sub_message.RKEAction = VCSEC_RKEAction_E_RKE_ACTION_UNLOCK; 37 | unsigned_message.which_sub_message = VCSEC_UnsignedMessage_RKEAction_tag; 38 | return Security::BuildUnsignedMessage(&unsigned_message, buffer, buffer_size); 39 | } 40 | 41 | int Security::Lock(unsigned char *buffer, size_t *buffer_size) { 42 | VCSEC_UnsignedMessage unsigned_message = VCSEC_UnsignedMessage{}; 43 | unsigned_message.sub_message.RKEAction = VCSEC_RKEAction_E_RKE_ACTION_LOCK; 44 | unsigned_message.which_sub_message = VCSEC_UnsignedMessage_RKEAction_tag; 45 | return Security::BuildUnsignedMessage(&unsigned_message, buffer, buffer_size); 46 | } 47 | 48 | int Security::Wake(unsigned char *buffer, size_t *buffer_size) { 49 | VCSEC_UnsignedMessage unsigned_message = VCSEC_UnsignedMessage{}; 50 | unsigned_message.sub_message.RKEAction = VCSEC_RKEAction_E_RKE_ACTION_WAKE_VEHICLE; 51 | unsigned_message.which_sub_message = VCSEC_UnsignedMessage_RKEAction_tag; 52 | return Security::BuildUnsignedMessage(&unsigned_message, buffer, buffer_size); 53 | } 54 | } // TeslaBLE 55 | -------------------------------------------------------------------------------- /src/session.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TeslaBLE © 2024 by Pascal Matthiesen 3 | */ 4 | 5 | #include "session.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | namespace TeslaBLE { 21 | void Session::LoadAuthenticator(Authenticator *authenticator) { 22 | this->authenticator_ = authenticator; 23 | } 24 | 25 | void Session::GenerateRoutingAddress() { 26 | for (int i = 0; i < sizeof(this->routing_address_); i++) { 27 | this->routing_address_[i] = rand() % 256; 28 | } 29 | } 30 | 31 | void Session::SetRoutingAddress(unsigned char *routing_address) { 32 | memcpy(this->routing_address_, routing_address, 16); 33 | } 34 | 35 | uint32_t Session::ExpiresAt(UniversalMessage_Domain domain, uint8_t expiresInSeconds) { 36 | return std::chrono::system_clock::to_time_t( 37 | std::chrono::system_clock::now() + std::chrono::seconds(expiresInSeconds)) - this-> 38 | time_zeros_[domain]; 39 | } 40 | 41 | // always increases the counter 42 | uint32_t Session::Counter(UniversalMessage_Domain domain) { 43 | this->counters_[domain] = this->counters_[domain] + 1; 44 | return this->counters_[domain]; 45 | } 46 | 47 | void Session::Epoch(UniversalMessage_Domain domain, unsigned char *output_buffer) { 48 | memcpy(output_buffer, this->epochs_[domain], 16); 49 | } 50 | 51 | void Session::SetVIN(unsigned char *vin) { 52 | memcpy(this->vin_, vin, 17); 53 | } 54 | 55 | int Session::ExportSessionInfo(UniversalMessage_Domain domain, unsigned char *output_buffer, 56 | size_t *output_size) { 57 | Signatures_SessionInfo session_info = Signatures_SessionInfo_init_default; 58 | 59 | session_info.clock_time = this->clock_times_[domain]; 60 | session_info.counter = this->counters_[domain]; 61 | session_info.status = Signatures_Session_Info_Status_SESSION_INFO_STATUS_OK; 62 | memcpy(session_info.epoch, this->epochs_[domain], 16); 63 | memcpy(session_info.publicKey.bytes, this->car_keys[domain], this->car_key_sizes[domain]); 64 | session_info.publicKey.size = this->car_key_sizes[domain]; 65 | 66 | pb_ostream_t message_stream = pb_ostream_from_buffer(output_buffer, Signatures_SessionInfo_size); 67 | if (!pb_encode(&message_stream, Signatures_SessionInfo_fields, &session_info)) { 68 | printf("Failed to encode message: %s", PB_GET_ERROR(&message_stream)); 69 | return ResultCode::NANOPB_ENCODE_ERROR; 70 | } 71 | 72 | *output_size = message_stream.bytes_written; 73 | return ResultCode::SUCCESS; 74 | } 75 | 76 | int Session::UpdateSessionInfo(UniversalMessage_Domain domain, unsigned char *session_info_message, 77 | size_t session_info_length) { 78 | Signatures_SessionInfo session_info = Signatures_SessionInfo_init_zero; 79 | 80 | if (session_info.status == Signatures_Session_Info_Status_SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST) { 81 | return ResultCode::SESSION_INFO_KEY_NOT_WHITELISTED; 82 | } 83 | 84 | pb_istream_t stream = pb_istream_from_buffer(session_info_message, session_info_length); 85 | if (!pb_decode(&stream, Signatures_SessionInfo_fields, &session_info)) { 86 | printf("Failed to decode session info: %s\n", PB_GET_ERROR(&stream)); 87 | return ResultCode::NANOPB_DECODE_ERROR; 88 | } 89 | 90 | uint32_t now = std::time(nullptr); 91 | this->clock_times_[domain] = session_info.clock_time; 92 | this->time_zeros_[domain] = now - session_info.clock_time; 93 | this->counters_[domain] = session_info.counter; 94 | memcpy(this->epochs_[domain], session_info.epoch, 16); 95 | 96 | // saved for export and import of the session information 97 | memcpy(this->car_keys[domain], session_info.publicKey.bytes, 65); 98 | this->car_key_sizes[domain] = session_info.publicKey.size; 99 | 100 | this->has_valid_session_info = true; 101 | return this->authenticator_->LoadTeslaPublicKey(domain, session_info.publicKey.bytes, 102 | session_info.publicKey.size); 103 | } 104 | 105 | int Session::BuildRoutableMessage(UniversalMessage_Domain domain, unsigned char *action_message_buffer, 106 | size_t action_message_buffer_size, unsigned char *output_buffer, 107 | size_t *output_buffer_size) { 108 | if (!this->has_valid_session_info) { 109 | return ResultCode::SESSION_INFO_NOT_LOADED; 110 | } 111 | 112 | UniversalMessage_RoutableMessage routable_message = UniversalMessage_RoutableMessage_init_default; 113 | 114 | UniversalMessage_Destination to_destination = UniversalMessage_Destination_init_default; 115 | to_destination.sub_destination.domain = domain; 116 | to_destination.which_sub_destination = UniversalMessage_Destination_domain_tag; 117 | 118 | routable_message.to_destination = to_destination; 119 | routable_message.has_to_destination = true; 120 | 121 | UniversalMessage_Destination from_destination = UniversalMessage_Destination_init_default; 122 | memcpy(from_destination.sub_destination.routing_address.bytes, this->routing_address_, 123 | sizeof(this->routing_address_)); 124 | from_destination.sub_destination.routing_address.size = sizeof(this->routing_address_); 125 | from_destination.which_sub_destination = UniversalMessage_Destination_routing_address_tag; 126 | 127 | routable_message.from_destination = from_destination; 128 | routable_message.has_from_destination = true; 129 | 130 | uint32_t counter = this->Counter(domain); 131 | uint32_t expiresAt = this->ExpiresAt(domain, 10); 132 | 133 | this->meta_data_.Start(); 134 | int result_code = this->meta_data_.BuildMetadata( 135 | domain, Signatures_SignatureType_SIGNATURE_TYPE_AES_GCM_PERSONALIZED, this->vin_, 136 | expiresAt, counter, this->epochs_[domain]); 137 | if (result_code != ResultCode::SUCCESS) { 138 | return result_code; 139 | } 140 | 141 | unsigned char checksum[32]; 142 | this->meta_data_.Checksum(checksum, Signatures_Tag_TAG_END); 143 | 144 | size_t encrypted_output_length = 0; 145 | unsigned char tag[16]; 146 | unsigned char signed_message_buffer[action_message_buffer_size]; 147 | 148 | result_code = this->authenticator_->Encrypt(domain, action_message_buffer, 149 | action_message_buffer_size, 150 | checksum, signed_message_buffer, 151 | sizeof(signed_message_buffer), &encrypted_output_length, tag); 152 | if (result_code != ResultCode::SUCCESS) { 153 | return result_code; 154 | } 155 | 156 | routable_message.which_payload = UniversalMessage_RoutableMessage_protobuf_message_as_bytes_tag; 157 | memcpy(routable_message.payload.protobuf_message_as_bytes.bytes, signed_message_buffer, 158 | encrypted_output_length); 159 | routable_message.payload.protobuf_message_as_bytes.size = encrypted_output_length; 160 | 161 | Signatures_SignatureData signature_data = Signatures_SignatureData_init_default; 162 | Signatures_KeyIdentity signer_identity = Signatures_KeyIdentity_init_default; 163 | signer_identity.which_identity_type = Signatures_KeyIdentity_public_key_tag; 164 | 165 | this->authenticator_->GetPublicKey(signer_identity.identity_type.public_key.bytes, 166 | &signer_identity.identity_type.public_key.size); 167 | 168 | signature_data.has_signer_identity = true; 169 | signature_data.signer_identity = signer_identity; 170 | 171 | signature_data.which_sig_type = Signatures_SignatureData_AES_GCM_Personalized_data_tag; 172 | signature_data.sig_type.AES_GCM_Personalized_data.counter = counter; 173 | signature_data.sig_type.AES_GCM_Personalized_data.expires_at = expiresAt; 174 | 175 | this->authenticator_->GetNonce(signature_data.sig_type.AES_GCM_Personalized_data.nonce.bytes); 176 | signature_data.sig_type.AES_GCM_Personalized_data.nonce.size = 12; 177 | 178 | memcpy(signature_data.sig_type.AES_GCM_Personalized_data.epoch.bytes, this->epochs_[domain], 16); 179 | signature_data.sig_type.AES_GCM_Personalized_data.epoch.size = 16; 180 | 181 | memcpy(signature_data.sig_type.AES_GCM_Personalized_data.tag.bytes, tag, sizeof(tag)); 182 | signature_data.sig_type.AES_GCM_Personalized_data.tag.size = sizeof(tag); 183 | 184 | routable_message.which_sub_sigData = UniversalMessage_RoutableMessage_signature_data_tag; 185 | routable_message.sub_sigData.signature_data = signature_data; 186 | 187 | return Common::EncodeRoutableMessage(routable_message, output_buffer, output_buffer_size); 188 | } 189 | 190 | int Session::BuildRequestSessionInfoMessage(UniversalMessage_Domain domain, 191 | unsigned char *output_buffer, size_t *output_length) { 192 | UniversalMessage_RoutableMessage routable_message = UniversalMessage_RoutableMessage_init_default; 193 | 194 | UniversalMessage_Destination to_destination = UniversalMessage_Destination_init_default; 195 | to_destination.which_sub_destination = UniversalMessage_Destination_domain_tag; 196 | to_destination.sub_destination.domain = domain; 197 | // cannot be set to 0 will break the message otherwise 198 | // to_destination.sub_destination.routing_address.size = 0; 199 | 200 | routable_message.to_destination = to_destination; 201 | routable_message.has_to_destination = true; 202 | 203 | UniversalMessage_Destination from_destination = UniversalMessage_Destination_init_default; 204 | from_destination.which_sub_destination = UniversalMessage_Destination_routing_address_tag; 205 | memcpy(from_destination.sub_destination.routing_address.bytes, this->routing_address_, 16); 206 | from_destination.sub_destination.routing_address.size = 16; 207 | 208 | routable_message.from_destination = from_destination; 209 | routable_message.has_from_destination = true; 210 | 211 | UniversalMessage_SessionInfoRequest session_info_request = UniversalMessage_SessionInfoRequest_init_default; 212 | 213 | this->authenticator_->GetPublicKey(session_info_request.public_key.bytes, 214 | &session_info_request.public_key.size); 215 | 216 | routable_message.payload.session_info_request = session_info_request; 217 | routable_message.which_payload = UniversalMessage_RoutableMessage_session_info_request_tag; 218 | 219 | return Common::EncodeRoutableMessage(routable_message, output_buffer, output_length); 220 | } 221 | } // TeslaBLE 222 | -------------------------------------------------------------------------------- /src/shared.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TeslaBLE © 2024 by Pascal Matthiesen 3 | */ 4 | 5 | #include "shared.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | namespace TeslaBLE { 21 | unsigned char *Common::HexStrToUint8(const char *input) { 22 | if (input == NULL) 23 | return NULL; 24 | 25 | size_t slength = strlen(input); 26 | if ((slength % 2) != 0) // must be even 27 | return NULL; 28 | 29 | size_t dlength = slength / 2; 30 | uint8_t *data = (uint8_t *) malloc(dlength); 31 | memset(data, 0, dlength); 32 | size_t index = 0; 33 | 34 | while (index < slength) { 35 | char c = input[index]; 36 | int value = 0; 37 | if (c >= '0' && c <= '9') 38 | value = (c - '0'); 39 | else if (c >= 'A' && c <= 'F') 40 | value = (10 + (c - 'A')); 41 | else if (c >= 'a' && c <= 'f') 42 | value = (10 + (c - 'a')); 43 | else 44 | return NULL; 45 | 46 | data[(index / 2)] += value << (((index + 1) % 2) * 4); 47 | index++; 48 | } 49 | 50 | return data; 51 | } 52 | 53 | void Common::DumpHexBuffer(const char *title, unsigned char *buffer, size_t size) { 54 | size_t i = 0; 55 | printf("%s", title); 56 | for (i = 0; i < size; i++) { 57 | printf("%c%c", "0123456789ABCDEF"[buffer[i] / 16], 58 | "0123456789ABCDEF"[buffer[i] % 16]); 59 | } 60 | printf("\n"); 61 | } 62 | 63 | void Common::PrependLength(unsigned char *input_buffer, size_t input_buffer_length, unsigned char *output_buffer, 64 | size_t *output_buffer_size) { 65 | uint8_t higher_byte = input_buffer_length >> 8; 66 | uint8_t lower_byte = input_buffer_length & 0xFF; 67 | 68 | uint8_t temp_buffer[2]; 69 | temp_buffer[0] = higher_byte; 70 | temp_buffer[1] = lower_byte; 71 | 72 | memcpy(output_buffer, temp_buffer, sizeof(temp_buffer)); 73 | memcpy(output_buffer + 2, input_buffer, input_buffer_length); 74 | *output_buffer_size = input_buffer_length + 2; 75 | } 76 | 77 | int Common::calculateIdentifier(unsigned char *vin, char *output) { 78 | unsigned char parsed_vin[18]; 79 | strcpy((char *) parsed_vin, (char *) vin); 80 | 81 | unsigned char hashed_vin[20]; 82 | const int return_code = mbedtls_sha1(parsed_vin, 17, hashed_vin); 83 | if (return_code != 0) { 84 | Common::PrintErrorFromMbedTlsErrorCode(return_code); 85 | return 1; 86 | } 87 | 88 | output[0] = 'S'; 89 | for (int i = 0; i < 8; ++i) { 90 | sprintf(&output[1 + i * 2], "%02x", hashed_vin[i]); 91 | } 92 | output[17] = 'C'; 93 | output[18] = '\0'; 94 | 95 | return 0; 96 | } 97 | 98 | size_t Common::ExtractLength(unsigned char *output_buffer) { 99 | uint16_t length = (static_cast(output_buffer[0]) << 8) | static_cast(output_buffer[1]); 100 | return length; 101 | } 102 | 103 | void Common::GenerateUUID(unsigned char *output_buffer, uint16_t *output_size) { 104 | unsigned char uuid[16]; 105 | for (int i = 0; i < sizeof(uuid); i++) { 106 | uuid[i] = rand() % 256; 107 | } 108 | memcpy(output_buffer, uuid, sizeof(uuid)); 109 | *output_size = sizeof(uuid); 110 | } 111 | 112 | int Common::DecodeRoutableMessage(unsigned char *buffer, size_t buffer_size, 113 | UniversalMessage_RoutableMessage *output_message) { 114 | pb_istream_t input_stream = pb_istream_from_buffer(buffer, buffer_size); 115 | if (!pb_decode(&input_stream, UniversalMessage_RoutableMessage_fields, output_message)) { 116 | printf("Decoding failed: %s\n", PB_GET_ERROR(&input_stream)); 117 | return ResultCode::NANOPB_DECODE_ERROR; 118 | } 119 | 120 | return ResultCode::SUCCESS; 121 | } 122 | 123 | int Common::DecodeFromVCSECMessage(unsigned char *buffer, size_t buffer_size, 124 | VCSEC_FromVCSECMessage *output_message) { 125 | pb_istream_t input_stream = pb_istream_from_buffer(buffer, buffer_size); 126 | if (!pb_decode(&input_stream, VCSEC_FromVCSECMessage_fields, output_message)) { 127 | printf("Decoding failed: %s\n", PB_GET_ERROR(&input_stream)); 128 | return ResultCode::NANOPB_DECODE_ERROR; 129 | } 130 | 131 | return ResultCode::SUCCESS; 132 | } 133 | 134 | int Common::EncodeRoutableMessage(UniversalMessage_RoutableMessage routable_message, 135 | unsigned char *output_buffer, 136 | size_t *output_size) { 137 | Common::GenerateUUID(routable_message.uuid.bytes, &routable_message.uuid.size); 138 | 139 | pb_ostream_t size_stream = {nullptr}; 140 | if (!pb_encode(&size_stream, UniversalMessage_RoutableMessage_fields, &routable_message)) { 141 | printf("Failed to encode message: %s", PB_GET_ERROR(&size_stream)); 142 | return ResultCode::NANOPB_ENCODE_ERROR; 143 | } 144 | 145 | uint8_t message_buffer[size_stream.bytes_written]; 146 | pb_ostream_t message_stream = pb_ostream_from_buffer(message_buffer, size_stream.bytes_written); 147 | if (!pb_encode(&message_stream, UniversalMessage_RoutableMessage_fields, &routable_message)) { 148 | printf("Failed to encode message: %s", PB_GET_ERROR(&message_stream)); 149 | return ResultCode::NANOPB_ENCODE_ERROR; 150 | } 151 | 152 | Common::PrependLength(message_buffer, message_stream.bytes_written, output_buffer, output_size); 153 | return ResultCode::SUCCESS; 154 | } 155 | 156 | void Common::ResultCodeToMessage(int result_code) { 157 | if (result_code == ResultCode::ERROR) { 158 | printf("General error.\n"); 159 | } else if (result_code == ResultCode::MBEDTLS_ERROR) { 160 | printf("mbedtls error.\n"); 161 | } else if (result_code == ResultCode::TAG_OUT_OF_ORDER) { 162 | printf("The provided metadata tag is out of order.\n"); 163 | } else if (result_code == ResultCode::TAG_VALUE_EMPTY) { 164 | printf("The provided metadata value is empty.\n"); 165 | } else if (result_code == ResultCode::TAG_VALUE_TOO_LONG) { 166 | printf("The provided metadata value is too long.\n"); 167 | } else if (result_code == ResultCode::NANOPB_DECODE_ERROR) { 168 | printf("Failed to decode protobuf message.\n"); 169 | } else if (result_code == ResultCode::NANOPB_ENCODE_ERROR) { 170 | printf("Failed to encode protobuf message.\n"); 171 | } else if (result_code == ResultCode::PRIVATE_KEY_NOT_LOADED) { 172 | printf("Private key has not been loaded yet.\n"); 173 | } else if (result_code == ResultCode::SESSION_INFO_NOT_LOADED) { 174 | printf("Session info has not been loaded yet.\n"); 175 | } else { 176 | printf("Unkown result code.\n"); 177 | } 178 | } 179 | 180 | void Common::ErrorCodeToMessage(UniversalMessage_MessageFault_E message_fault) { 181 | if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_NONE) { 182 | printf("Request succeeded.\n"); 183 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_BUSY) { 184 | printf("Required vehicle subsystem is busy. Try again.\n"); 185 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_TIMEOUT) { 186 | printf("Vehicle subsystem did not respond. Try again.\n"); 187 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_UNKNOWN_KEY_ID) { 188 | printf( 189 | "Vehicle did not recognize the key used to authorize command. Make sure your key is paired with the vehicle.\n"); 190 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INACTIVE_KEY) { 191 | printf("Key used to authorize command has been disabled.\n"); 192 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INVALID_SIGNATURE) { 193 | printf("Command signature/MAC is incorrect. Use included session info to update session and try again.\n"); 194 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INVALID_TOKEN_OR_COUNTER) { 195 | printf( 196 | "Command anti-replay counter has been used before. Use included session info to update session and try again.\n"); 197 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INSUFFICIENT_PRIVILEGES) { 198 | printf( 199 | "User is not authorized to execute command. This can be because of the role or because of vehicle state.\n"); 200 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INVALID_DOMAINS) { 201 | printf( 202 | "Command was malformed or addressed to an unrecognized vehicle system. May indicate client error or older vehicle firmware.\n"); 203 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INVALID_COMMAND) { 204 | printf("Unrecognized command. May indicate client error or unsupported vehicle firmware.\n"); 205 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_DECODING) { 206 | printf("Could not parse command. Indicates client error.\n"); 207 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INTERNAL) { 208 | printf( 209 | "Internal vehicle error. Try again. Most commonly encountered when the vehicle has not finished booting.\n"); 210 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_WRONG_PERSONALIZATION) { 211 | printf("Command sent to wrong VIN.\n"); 212 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_BAD_PARAMETER) { 213 | printf("Command was malformed or used a deprecated parameter.\n"); 214 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_KEYCHAIN_IS_FULL) { 215 | printf("Vehicle's keychain is full. You must delete a key before you can add another.\n"); 216 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_INCORRECT_EPOCH) { 217 | printf("Session ID mismatch. Use included session info to update session and try again.\n"); 218 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_IV_INCORRECT_LENGTH) { 219 | printf( 220 | "Initialization Value length is incorrect (AES-GCM must use 12-byte IVs). Indicates a client programming error.\n"); 221 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_TIME_EXPIRED) { 222 | printf( 223 | "Command expired. Use included session info to determine if clocks have desynchronized and try again.\n"); 224 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_NOT_PROVISIONED_WITH_IDENTITY) { 225 | printf("Vehicle has not been provisioned with a VIN and may require service.\n"); 226 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_COULD_NOT_HASH_METADATA) { 227 | printf("Internal vehicle error.\n"); 228 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_TIME_TO_LIVE_TOO_LONG) { 229 | printf( 230 | "Vehicle rejected command because its expiration time was too far in the future. This is a security precaution.\n"); 231 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_REMOTE_ACCESS_DISABLED) { 232 | printf("The vehicle owner has disabled Mobile access.\n"); 233 | } else if (message_fault == UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_REMOTE_SERVICE_ACCESS_DISABLED) { 234 | printf( 235 | "The command was authorized with a Service key, but the vehicle has not been configured to permit remote service commands.\n"); 236 | } else if (message_fault == 237 | UniversalMessage_MessageFault_E_MESSAGEFAULT_ERROR_COMMAND_REQUIRES_ACCOUNT_CREDENTIALS) { 238 | printf( 239 | "The command requires proof of Tesla account credentials but was not sent over a channel that provides this proof. Resend the command using Fleet API.\n"); 240 | } else { 241 | printf("Unkown fault message"); 242 | } 243 | } 244 | 245 | void Common::OperationStatusToMessage(UniversalMessage_OperationStatus_E operation_status) { 246 | if (operation_status == UniversalMessage_OperationStatus_E_OPERATIONSTATUS_OK) { 247 | printf("Operation status ok\n"); 248 | } else if (operation_status == UniversalMessage_OperationStatus_E_OPERATIONSTATUS_WAIT) { 249 | printf("Operation status wait (Might be user interaction, like tap NFC card)\n"); 250 | } else if (operation_status == UniversalMessage_OperationStatus_E_OPERATIONSTATUS_ERROR) { 251 | printf("Operation status error\n"); 252 | } else { 253 | printf("Unkown operation status"); 254 | } 255 | } 256 | 257 | void Common::PrintErrorFromMbedTlsErrorCode(int result) { 258 | char error_buf[200]; 259 | mbedtls_strerror(result, error_buf, 200); 260 | printf("mbedtls error: -0x%04x - %s\n", (unsigned int) -result, error_buf); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/signatures.pb.c: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb constant definitions */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #include "signatures.pb.h" 5 | #if PB_PROTO_HEADER_VERSION != 40 6 | #error Regenerate this file with the current version of nanopb generator. 7 | #endif 8 | 9 | PB_BIND(Signatures_KeyIdentity, Signatures_KeyIdentity, AUTO) 10 | 11 | 12 | PB_BIND(Signatures_AES_GCM_Personalized_Signature_Data, Signatures_AES_GCM_Personalized_Signature_Data, AUTO) 13 | 14 | 15 | PB_BIND(Signatures_HMAC_Signature_Data, Signatures_HMAC_Signature_Data, AUTO) 16 | 17 | 18 | PB_BIND(Signatures_HMAC_Personalized_Signature_Data, Signatures_HMAC_Personalized_Signature_Data, AUTO) 19 | 20 | 21 | PB_BIND(Signatures_SignatureData, Signatures_SignatureData, AUTO) 22 | 23 | 24 | PB_BIND(Signatures_GetSessionInfoRequest, Signatures_GetSessionInfoRequest, AUTO) 25 | 26 | 27 | PB_BIND(Signatures_SessionInfo, Signatures_SessionInfo, AUTO) 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/universal_message.pb.c: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb constant definitions */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #include "universal_message.pb.h" 5 | #if PB_PROTO_HEADER_VERSION != 40 6 | #error Regenerate this file with the current version of nanopb generator. 7 | #endif 8 | 9 | PB_BIND(UniversalMessage_Destination, UniversalMessage_Destination, AUTO) 10 | 11 | 12 | PB_BIND(UniversalMessage_MessageStatus, UniversalMessage_MessageStatus, AUTO) 13 | 14 | 15 | PB_BIND(UniversalMessage_SessionInfoRequest, UniversalMessage_SessionInfoRequest, AUTO) 16 | 17 | 18 | PB_BIND(UniversalMessage_RoutableMessage, UniversalMessage_RoutableMessage, 2) 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/vcsec.pb.c: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb constant definitions */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #include "vcsec.pb.h" 5 | #if PB_PROTO_HEADER_VERSION != 40 6 | #error Regenerate this file with the current version of nanopb generator. 7 | #endif 8 | 9 | PB_BIND(VCSEC_SignedMessage, VCSEC_SignedMessage, AUTO) 10 | 11 | 12 | PB_BIND(VCSEC_ToVCSECMessage, VCSEC_ToVCSECMessage, AUTO) 13 | 14 | 15 | PB_BIND(VCSEC_KeyIdentifier, VCSEC_KeyIdentifier, AUTO) 16 | 17 | 18 | PB_BIND(VCSEC_KeyMetadata, VCSEC_KeyMetadata, AUTO) 19 | 20 | 21 | PB_BIND(VCSEC_PublicKey, VCSEC_PublicKey, AUTO) 22 | 23 | 24 | PB_BIND(VCSEC_WhitelistInfo, VCSEC_WhitelistInfo, AUTO) 25 | 26 | 27 | PB_BIND(VCSEC_WhitelistEntryInfo, VCSEC_WhitelistEntryInfo, AUTO) 28 | 29 | 30 | PB_BIND(VCSEC_InformationRequest, VCSEC_InformationRequest, AUTO) 31 | 32 | 33 | PB_BIND(VCSEC_ClosureMoveRequest, VCSEC_ClosureMoveRequest, AUTO) 34 | 35 | 36 | PB_BIND(VCSEC_PermissionChange, VCSEC_PermissionChange, AUTO) 37 | 38 | 39 | PB_BIND(VCSEC_ReplaceKey, VCSEC_ReplaceKey, AUTO) 40 | 41 | 42 | PB_BIND(VCSEC_WhitelistOperation, VCSEC_WhitelistOperation, AUTO) 43 | 44 | 45 | PB_BIND(VCSEC_WhitelistOperation_status, VCSEC_WhitelistOperation_status, AUTO) 46 | 47 | 48 | PB_BIND(VCSEC_SignedMessage_status, VCSEC_SignedMessage_status, AUTO) 49 | 50 | 51 | PB_BIND(VCSEC_CommandStatus, VCSEC_CommandStatus, AUTO) 52 | 53 | 54 | PB_BIND(VCSEC_UnsignedMessage, VCSEC_UnsignedMessage, AUTO) 55 | 56 | 57 | PB_BIND(VCSEC_ClosureStatuses, VCSEC_ClosureStatuses, AUTO) 58 | 59 | 60 | PB_BIND(VCSEC_DetailedClosureStatus, VCSEC_DetailedClosureStatus, AUTO) 61 | 62 | 63 | PB_BIND(VCSEC_VehicleStatus, VCSEC_VehicleStatus, AUTO) 64 | 65 | 66 | PB_BIND(VCSEC_FromVCSECMessage, VCSEC_FromVCSECMessage, AUTO) 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/vehicle.pb.c: -------------------------------------------------------------------------------- 1 | /* Automatically generated nanopb constant definitions */ 2 | /* Generated by nanopb-0.4.8 */ 3 | 4 | #include "vehicle.pb.h" 5 | #if PB_PROTO_HEADER_VERSION != 40 6 | #error Regenerate this file with the current version of nanopb generator. 7 | #endif 8 | 9 | PB_BIND(CarServer_VehicleState, CarServer_VehicleState, 2) 10 | 11 | 12 | PB_BIND(CarServer_VehicleState_GuestMode, CarServer_VehicleState_GuestMode, AUTO) 13 | 14 | 15 | PB_BIND(CarServer_ClimateState, CarServer_ClimateState, AUTO) 16 | 17 | 18 | 19 | 20 | --------------------------------------------------------------------------------