Hi Michal, > From: Michał Lowas-Rzechonek [mailto:michal.lowas- > > A couple of comments regarding device key management and local/remote > provisioning flow in general. > > > On 03/22, Brian Gix wrote: > > + uint64 token CreateNetwork(object app_root, array{byte}[16] uuid) > > + > > + This is the first method that an application calls to become > > + a Provisioner node, and a Configuration Client on a newly > > + created Mesh Network. > > What if I would like my node to act as a Config Client without being a > provisioner? In our application, nodes can obtain network details from a > remote storage, so you don't need to be the one provisioning the network in > order to have access to device keys. > > I think we should distinguish creating a node within meshd and provisioning > it, as there are a couple of ways to provision a new node: > > - the node can advertise itself as unprovisioned device, waiting for > PB-ADV > - the node can advertise itself as a connectable, exposing PB-GATT > service > - the node might not want to advertise at all, and provision itself > "out of band", getting provisioning information from a remote > database > > The 3rd option is of particular concern for new networks, and also > automatically adding devices to existing ones. > > Because of that, I think we should rather split the > 'org.bluez.mesh.Network1.Join' method into something like: > > > Mesh Network Hierarchy > ====================== > Service org.bluez.mesh > Interface org.bluez.mesh.Network1 > Object path /org/bluez/mesh > > Methods: > uint64 token > Create(array{byte}[16] uuid) > > This is the first method that an application has to call to > become a provisioned node on a mesh network. The call will > allocate resources for the new node within the mesh daemon. > > The token parameter serves as a unique identifier of the > particular node. The token must be preserved by the application > in order to authenticate itself to the mesh daemon and attach to > the network as a mesh node by calling Attach() method or > permanently remove the identity of the mesh node by calling > Remove() method. > > The created node is initially idle, particularly it doesn't > initiate broadcasting of Unprovisioned Device Beacon. > > (...) We are committed to using the org.freedesktop.DBus.ObjectManager in order to create the first Node of a new Mesh, which will require an object_path in all places where we have currently put them. This initial node must have at a minimum have Configuration Client available before it may Provision other nodes and start creating it's database of external nodes, by at a minimum requesting new node Composition data. > > void Remove(uint64 token) > > This removes the configuration information about the mesh node > identified by the 64-bit token parameter. The token parameter > has been obtained as a result of successful Create() method call. > > > The application (after calling Attach) can then proceed to either provision the > node itself, or let it broadcast Unprovisioned Device > Beacons: > > > Mesh Node Hierarchy > =================== > Service org.bluez.mesh > Interface org.bluez.mesh.Node1 > > void Provision(array{byte}[16] network_key, uint16_t address) > > This call will add an unprovisioned local node to a given > network, using provided primary unicast address. Internally, concern has been expressed for the security of all encryption keys used within Mesh. The current design avoids all handling of keys (Network, Application and Device keys) outside of the Daemon, particularly over the D-Bus interface. It is true that we will need to eventually be able to Export Key Databases such that Provisioning (and Configuration Client) responsibilities can be either Transferred or Shared. We currently envision this as a privileged operation where an Export tool of some sort can retrieve the keys it needs over a mechanism the is *not* D-Bus.... Perhaps either a file descriptor or a privileged pipe. Initially of course, anyone with Root privileges is able to retrieve the keys from within the Daemon's storage structure. > void Join(void) > > The call will initiate broadcasting of Unprovisioned Device > Beacon. > > If regular bluetoothd is available on the system bus, mesh > daemon will also register itself as a GATT application, > providing PB-GATT service. This will again need to support the org.freedesktop.DBus.ObjectManager interface. While it may be an open question as to whether to include the UUID as a parameter here, or as a Property of the Application creating the node, we chose parameter, since it is information that is typically needed only at Provisioning, and such will not need to be re-quired over the life of the node via a Property. Also note, that the ObjectManager interface is required so that the number of Elements are available to the Provisioning Procedure, and so that upon successful completion of the Provisioning procedure, all Configuration Server requests can be immediately serviced by the daemon. It is the goal of the daemon to provide all Foundational Server functionality (server functionality that requires the Device Key of the node) with or without a foreground Application "Attached" to the node. We also chose to put Join/Cancel/Leave/Create in the Network1 interface because no "Node" technically exists until provisioning is successful. The methods under the Node1 interface are only available to applications which have "Attached" using a token. > > void Cancel() > > Cancels an outstanding provisioning request initiated by Join() > method. > > > After provisioning, the application will then receive freshly generate device > key, so that it can be stored externally. See earlier comment about keeping keys internally secured. > > > Mesh Application Hierarchy > ========================== > Service unique name > Interface org.bluez.mesh.Application1 > Object path <app_defined_root> > > void JoinComplete(array{byte}[16] device_key) > > > Having the application aware of the device key of a local node would also > mean that we could get rid of magic 0x7fff index for device key in > Send() call. Instead, we could provide two separate calls for application-level > and device-level security: > > void SendWithApplicationKey(object element_path, uint16 destination, > uint16 key_index, array{byte} data) > > void SendWithDeviceKey(object element_path, uint16 destination, > array{byte}[16] device_key, > array{byte} data) > > > Mesh Node Hierarchy > > =================== > > @@ -146,6 +178,88 @@ Methods: > > org.bluez.mesh.Error.InvalidArguments > > org.bluez.mesh.Error.NotFound > > > > + void SendWithDeviceKey(object element_path, uint16 destination, > > + uint16 net_index, array{byte} data) > > + > > + This method is used to send a message originated by a local > > + model encoded with the device key of the remote node. > > + > > + The element_path parameter is the object path of an > element from > > + a collection of the application elements (see Mesh > Application > > + Hierarchy section). > > + > > + The destination parameter contains the destination address. > This > > + destination must be a uint16 to a unicast address, or a well > > + known group address. > > I don't think it makes sense to send messages using device keys to non- > unicast addresses. I don't think this is 100% certain... An "All Provisioners" or "All Config Clients" is a distinct possibility, especially given that Provisioning and Config Client duties may at some point be shared. It is not 100% certain that a Node will *always* know who the active Provisioner is, and whether a Config Server will ever need to send unsolicited reports to a Provisioner (if for instance a node is going to permanently leave). This is forward looking, and in any case, uint16 is the smallest reasonable address space for unicast addresses, and so we are choosing to use the more generic "destination" for now. > Also, since mesh daemon might not be the one who originally provisioned > device we would like to communicate with, I think this method should accept > the device key as an argument, moving key management duties to the > application. > > Also, because it's not possible to configure publications using a device key, I > think device keys make sense only in case of unicast *requests*. > As far as I understand the mesh architecture, such responses are the only > case where we are receiving messages encrypted with remote node's device > key. > > Therefore, I think the mesh daemon might want to retain the device key > passed to SendWithDeviceKey call for a certain time period, in order to > process these response. This has been part of the conversation. Marcel was the main advocate/instigator for *not* passing the device key here, and he made a compelling argument for the proper securing of *all* keys (not just the Device Key). > This would also mean that this call: > > > + void AddNode(array{byte}[16] uuid) > > + > > + This method is used by the application that supports > > + org.bluez.mesh.Provisioner1 interface to add the > > + unprovisioned device specified by uuid, to the Network. > > + > > + The uuid parameter is a 16-byte array that contains Device > UUID > > + of the unprovisioned device to be added to the network. > > should return generated device key back to the caller, so that it can store the > device key for later use, and/or send it to the remote storage, so that other > config clients can retrieve/use them. > > All of that relieves the mesh daemon from keeping the network database, > and gives us certain symmetry in the API wrt local vs. remote nodes. > > What do you think? Most of your recommendation directly affect the security of the Keys, and whether they get passed via D-Bus... And this is something that I would *not* do without the OK from Marcel Holtmann, and I am inclined to believe this assent will probably not happen, for reasons stated, and that I agree with. However, a secure methodology for Exporting keys should definitely be high on our TODO list. > > regards > -- > Michał Lowas-Rzechonek <michal.lowas-rzechonek@xxxxxxxxxxx> > Silvair http://silvair.com > Jasnogórska 44, 31-358 Krakow, POLAND BR, Brian Gix