An alternate approach for launcher. The idea is to expose secure RESTFull APIs instead of the present IPC design. Which will allow developers to easily get started with the development on SafeNetwork. Moreover this would reduce the configuration required to bare minimum making the life of the end users easy too. Since RESTFull APIs are built on top of the standard HTTP Application layer protocol, the common tools used in practice every day can be used for debugging and development.
Launcher at present is responsible for starting the SafeNetwork compatible applications. Launcher acts as a server gateway between the application and the SAFENetwork. The applications authorise and exchange data with the SafeNetwork using the IPC calls exposed by the launcher. This approach, certainly is more secure as it opens one secure channel for each application to communicate.
At the same time the configuration required to kick start is more and not easy as one would expect. Also the developers have to understand the IPC calls, which again is not a common approach that the application developers are familiar with.
If we have a local service which would expose RESTFull API interfaces for authorising and also for data exchange with the SafeNetwork, it would make the developer's life easy. The applications would have to authorise with the local service/server through a token based mechanism. Token based mechanism is a standard approach for authorisation, eg (OAuth, JWT). In our case the best fit would be JWT for token based authorisation. Permissions requested by the application would be approved manually by the user, only after which the JWT for the application would be generated and exchanged with the application.
An end user would have to download the SafeNetwork GateWay/Service and login to the application with user's network credentials. When the user starts an SafeNetwork compatible application, the third party application would request for authorisation to connect with the SafeNetwork Gateway service. When the service receives authorisation request from an application, the same is prompted to the user for manual approval. Based on users decision the application is permitted to connect to the service.
The present launcher implementation creates an random id for the application and stores the application configurations in
Launcher Configuration directory
. The permissions granted by the user along with the application directory key is saved
as a part of the configuration. But in this new approach we would need a deterministic approach for associating the
application directory.
Every application will have to provide its own unique identifier string which will be concatenated with the developer's id or with the vendor name and hashed. The result of the hash (SHA512(Vendor + AppKey)) will represent id of the application. The metadata related to the application can be stored mapping to this deterministic id.
So when the application directory has to be fetched,
- The application id is generated in the deterministic approach.
- The metadata related to the application is retrieved from the
Launcher Configuration Directory
- If the metadata file is not present, then a new file is created. Metadata file format would be in cbor,
{
"app_directory_key": DirectoryKey
}
- If the data is already present, then the metadata file is returned
- The Application directory can be fetched using the DirectoryKey from the metadata file.
There is no need to persist the permissions of the application because it would be approved on demand by the user for the session.
-
User logs in to the SafeNetwork Gateway application with user's credentials. On successful login, the SafeNetwork Gateway application would start a local RESTFull server at a fixed port (59999). If this port is not available then the Gateway application kills itself intimating to the user that the port required is not free.
-
The SafeNetwork Gateway application must be kept running for the third party applications to connect to the SafeNetwork
-
When the user starts an SafeNetwork compatible application, the application would send an authorisation request to the Gateway Service. The application will generate an Asymmetric encryption key pair for securely exchanging the Symmetric encryption key with the Gateway(ECDH-Key-Exchange). This uses Curve25519 (from libsodium) for symmetric key exchange. Every application will have its own unique identifier string.
POST http://localhost:59999/safe-api/v1/auth/registered-access
{ application: { name: 'DNS Example', // Required vendor: 'MaidSafe', // Required id: string, // Required - unique string to identify the application - this can not be changed later - if changed // it will lead to lose of data version: 0.0.1 // Required }, permissions: [ // Optional 'SAFE_DRIVE_ACCESS' ], publicKey: base64String // Public Key for Asymmetric encryption nonce: base64String // Asymmetric Nonce }
-
When the Gateway service receives the authorisation request from the third party application, the application details along with the list of permissions requested are presented to the user for approval.
--------------------------------------------------- | | | DNS Example is requesting access. | | | | Permissions Requested: | | SAFE DRIVE ACCESS | | | | Vendor: MaidSafe | | Version: 0.0.1 | | | | Yes No | | | ---------------------------------------------------
-
If the user rejects the request, then the authorisation failure response is sent back to the application
Http Error code : 401 (Unauthorised)
-
If the user approves the request, then the application would generate a JWT token and send it back to the application as a part of the response. Generation of the JWT is not a single step process. The JWT is generated as follows,
- An unique ID is generated by the Gateway Service. This ID would map to the application information which was approved by the user. This ID is temporary and will only be used for this session of the Gateway application.
- Once the ID is generated, the information related to the approved request including the permission list is stored in memory with reference to the application ID. Application related information could be fetched whenever needed using the ID.
- Asymmetric Keys for encryption are generated.
- Symmetric Key & Nonce for encryption are generated. Generated Symmetric key and nonce are used for encrypting the messages between application and the Gateway service.
- Signing Keys are generated. Signing Keys are used for signing the JWT token.
- The generated signing keys, symmetric keys and nonce (from 6.4) are also stored in memory against the application Id.
- Constructing the JWT token,
- The header of the JWT token
{"typ": "JWT", "alg": "ED512"}
is converted as base64 String - The payload section will contain the application ID converted as base64 String
- Finally the third part is the signed data. The data
header_base64string.payload_base64string
is signed using the generated secret signing key. The signed data is again converted to a base64 String - Finally the token is formed by concatenating
header_base64.payload_base64.signed_data_base64
. Each section in the token is separated with a.
- The header of the JWT token
-
The in-memory store can be a HashMap, with sessionId as Key and SessionData struct.
pub struct SessionData {
permissions: Vec<PERMISSION_ENUM>,
public_signing_key: sodiumoxide::crypto::sign::ed25519::PublicKey,
secret_signing_key: sodiumoxide::crypto::sign::ed25519::SecretKey,
encryption_key: sodiumoxide::crypto::secretbox::xsalsa20poly1305::Key
}
- The symmetric key + Nonce (from step 6.4) is encrypted using the Generated Private Asymmetric key (from step 6.3), the public key and nonce received as a part of the authorisation request.
- Now the authorisation request is responded back with the list of permissions, JWT token, Encrypted symmetric Key, public key of the asymmetric key pair (from step 6.3)
Status: 200 Content-Type: application/json
{
token: generated_jwt_token,
encryptedSymmetricKey: base64String, // [SYMMETRIC_KEY + NONCE]
public_key: base64String,
permissions: [
'SAFE_DRIVE_ACCESS'
]
}
- The application will be able to decrypt the symmetric key using the public key from response, its own private key and nonce which was sent as part of the authorisation request.
- The JWT token should be sent for every API request from the application.
Once the authorisation is successful, the application receives the token and the Symmetric encryption key for data exchange.
The JWT token must be sent as a part of Authorization
header field and the data should be encrypted with the Symmetric Key.
- Application will send the JWT token in the HTTP
Authorization
header field.
GET http://localhost:59999/api/v1/nfs/directory
Request Headers:
Authorization: Bearer {JWT]
- When the gateway receives the request, the session id is fetched from the payload section of the JWT.
- The session id fetched is used to look up the application's authorisation details. The authorisation details are saved in the memory when the application was authorised (As explained in step 6).
- Using the session id, the signing keys are fetched from the memory store (Probably HashMap).
- Finally, the last section (signed data) of the JWT is verified using the fetched signature keys.
- If the signature verification is not successful, then the unauthorised response is sent.
- Else, the request is processed and the response is sent back.
- The approved permissions list can be fetched using the session id lookup.