M3DA Server connection and security

Overview

Data transmission from the agent to the server are handled in M3DA. It goes through several modules:

In the other direction, server messages going to the agent, they're received by the session through an LTN12 sink, then pushed, one serialized message at a time, to a callback passed at initialization time. Those messages are expected to be rather short, hence are passed as strings rather than streams.

The session layer is in charge of putting sent messages into M3DA envelopes (and extracting incomping messages from their envelopes). Currently supported session layer are m3da.session.default and m3da.session.security, the latter supporting mandatory authentication, plus optional encryption and password provisioning.

Detailed API

Transport modules

Transport modules must have a function new(url), which returns nil + error message upon failure, or an object with the following properties:

The url parameter of the new() function must be sufficient to fully configure the transport layer, and the scheme part of the url (the initial word before "://") must match the last part of the module name.

Session modules

Session modules must have a function new(cfg), which returns nil + error message upon failure, or an object with the following properties:

The cfg parameter for new() is a table. It must have the following fields, plus optionally some others specific to a given session type:

agent.srvcon

The server connector has the following public APIs:

Key management

Key are stored on embedded devices in a file crypto/crypto.keys. They are obfuscated with an AES key, but this doesn't constitute proper encryption, as the key is in the code and can be retrieved by someone with access to the hardware. The possibility is contemplated, for future versions of the agent, to let users plug alternative obfuscation methods in the system.

Another risk mitigation measure is that keys retrieved from the key store are never passed directly to Lua. Instead, all Lua cryptographic primitives can access keys through their index in the key store. This means that every key used to encrypt or sign data must be put in the store, even if it is derived from a key already present in the store.

The keys are, in order:

PROVIS_KS = MD5(server_id .. MD5(registration_password))
PROVIS_KD = MD5(device_id .. MD5(registration_password))
CRYPTO_K  = MD5(password)
AUTH_KS   = MD5(server_id .. MD5(password))
AUTH_KD   = MD5(device_id .. MD5(password))

Where registration_password is the provisioning secret, generally common to a series of devices, and used to download the actual signing and encryption key CRYPTO_K. password is the final, device-specific secret used to generate the actual security keys.

The keys have indexes 1...5 in Lua. Beware that in the keystore and the C code, they have indexes 0...4.

Registration passwords as well as actual passwords can be provisionned in a device through telnet as follows:

There's also a C API, but at a lower level; you have to compute the keys yourself before writing them down in the store. The writing is preformed by set_plain_bin_keys( first_key, n_keys, keys), in libs/keystore/keystore.{c,h}.

In the keystore, keys are ciphered with an AES-128 key embedded in the code. Moreover, this key is rotated n bytes to the left to encrypt the key at index n (this prevents identical keys from having the same encrypted forms).