Pure Python request signing for Luna Music Car. Generate X-Argus, X-Gorgon, X-Khronos and X-Ladon through a Python API or a JSON command-line interface.
Requires Python 3.10 or later. All runtime code uses the Python standard library. Signing runs entirely in Python and does not send HTTP requests.
The supported profile is Luna Music Car 1.1.35 / MetaSec v04.04. Application configuration and runtime state are explicit inputs; other SDK versions may require different formats or configuration.
From the project directory:
python -m pip install .You can also run the module directly from the project directory without installation:
python -m car_metasec examples/request.jsonimport json
from car_metasec import ArgusConfig, sign_headers
url = "https://example.invalid/api/media?aid=765213&device_id=example-device&vehicle_id="
body = b'{"media_list":[{"id":"example-track","type":"track"}]}'
headers = sign_headers(
url,
body,
config=ArgusConfig(device_id="example-device"),
)
print(json.dumps(headers, indent=2))sign_headers returns a dictionary of header names and values. It accepts:
| Parameter | Description |
|---|---|
url |
Final request URL, including its original query string. |
body |
Raw request bytes; defaults to b"". |
timestamp |
Unix seconds; defaults to the current time. |
config |
An ArgusConfig containing application and device configuration. |
state |
A SigningState containing entropy and environment flags; defaults to fresh entropy. |
signing_url |
Optional original signing URL when it differs from url. |
Pass the exact body bytes that will be sent. JSON formatting, whitespace and trailing newlines affect the signature. Use seconds for timestamps. If a timestamp appears in your query string, update that query value yourself before signing.
With a nonempty aid=... query parameter, the result contains all four headers. A missing or empty aid produces X-Gorgon and X-Khronos. For repeated aid=... parameters, the last assigned value is used.
The individual functions are also available:
from car_metasec import sign_argus, sign_gorgon, sign_khronos, sign_ladonCreate a JSON request description:
{
"url": "https://example.invalid/api/media?aid=765213",
"body_base64": "eyJwcm9iZSI6MX0=",
"config": {
"device_id": "example-device"
}
}Run it with the installed command or the module:
car-metasec request.json
python -m car_metasec request.json
python -m car_metasec request.json --timestamp 1700000000 --output headers.jsonThe result contains headers, the timestamp used, the request URL and the complete entropy state. An explicitly supplied signing_url is included in the result as well. Standard output is JSON; input errors are written to standard error with exit code 2.
| JSON field | Description |
|---|---|
url |
Required request URL. |
body_base64 |
Base64-encoded body; omit for an empty body. |
body_file |
Alternative body file, read as raw bytes. Relative paths resolve against the input JSON directory. |
timestamp |
Optional Unix seconds. --timestamp takes precedence. |
config |
Optional application/device configuration. |
state |
Optional entropy and environment state. Values may be integers or strings such as "0x1000". |
signing_url |
Optional original signing URL. |
Use either body_base64 or body_file. Example requests are available in examples/: request.json uses fresh entropy; request_with_state.json supplies a complete fixed state.
ArgusConfig provides the following fields. The CLI uses the same names except for the two byte fields shown below.
| Python field | CLI field | Default / purpose |
|---|---|---|
sdk_aid |
sdk_aid |
"753999"; SDK application ID. |
license_id |
license_id |
"291705791"; SDK license identifier. |
app_version |
app_version |
"1.1.35". |
sdk_version |
sdk_version |
"v04.04.00-ml-android". |
sdk_version_code |
sdk_version_code |
0x04040000, or decimal 67371008 in JSON. |
device_id |
device_id |
Empty string; supply your device identifier. |
environment_bytes |
environment_hex |
Exactly eight bytes; defaults to zero bytes. |
statistics |
statistics |
Four integers; defaults to [694367,694367,694367,0]. |
field16 |
field16 |
Opaque runtime provider string; defaults to an empty string. |
security_token |
security_token |
"none". |
context_value |
context_value |
377. |
sign_key |
sign_key_hex |
32-byte signing constant for the supported profile. |
The URL aid and configuration sdk_aid have separate roles. The default host application ID is 765213; the SDK application ID is 753999.
Supply the configuration associated with your application/device session. Environment bytes, statistics and field16 are runtime inputs and can change during a session. The library accepts these values from the caller.
Fresh entropy is generated when state is omitted. For deterministic output, set every SigningState field and keep the URL, body, timestamp and configuration unchanged:
from car_metasec import SigningState
state = SigningState(
gorgon_address_seed=0x1000,
gorgon_status_flags=0x0140,
protobuf_nonce=1,
crypto_nonce=2,
envelope_nonce=3,
argus_address=0x02000000,
ladon_nonce=4,
)
headers = sign_headers(
url,
body,
timestamp=1700000000,
state=state,
config=ArgusConfig(device_id="example-device"),
)The gorgon_* fields accept unsigned 16-bit integers. The remaining fields accept unsigned 32-bit integers. Address fields carry entropy used by the format; the Python implementation does not dereference them. The default flags are zero. Supply environment flags appropriate to your session when needed.
Query bytes are preserved. Parameter order, duplicates, percent escapes, + and empty-value spelling participate in the digest. For example, vehicle_id and vehicle_id= are different strings. The signer does not sort or normalize query parameters.
When the original signing URL differs from the request URL, pass it explicitly:
headers = sign_headers(
"https://example.invalid/api/media?aid=765213&vehicle_id",
body,
signing_url="https://example.invalid/api/media?aid=765213&vehicle_id=",
)signing_url supplies the query used for signing; it does not rewrite the request URL.
Licensed under the GNU Affero General Public License v3.0 only (AGPL-3.0-only). See LICENSE for the full terms.