@
xoft
Bildschirmfoto von »2017-10-10 21-39-42«.png (Size: 36 KB / Downloads: 362)
so the things i have planned so far (and i tick the boxes i have already)
oh, just a note: server = app. client = desktop
[-] Autodiscovery. (1)
[x] Security. If my thought process is flawless here, the communication channel is encrypted, the server's fingerprint confirmed by the user and even man in the middle attacks prevented (2)
[x] Sleek UI (hello dinosaur
still work in progress)
[ ] Features. Planned: "Contacts app" on desktop, optionally maybe: SMS messages? notifications? still gotta try out the battery performance with those (3)
(1) Autodiscovery. I actually had that feature implemented before reading your post. My implementation is as follows:
1. The client sends a WHO udp package to the local network's broadcast address (255.255.255.255) (WHO package is [0x49,0x4c,0x7b,0xae,0x30,0x30,0x69,0x9e] plus the client's public key fingerprint as ASCII encoded (see (2))
2. The server responds with a HERE udp package directly to the client (HERE is [0x22,0xd6,0xb1,0x4b,0x35,0x28,0x10,0x51] plus one byte known information (0x00 = no info, 0x01 = unknown public key, 0x02 = known public key) and after that the android device name (Build.MANUFACTURER + " " + Build.MODEL). i used this as it identifies the phone fairly well and is usable across all devices
However, this has a few caveats.
1. UDP holepunching only works when sending messages to specific devices. That means, any firewall running on the pc would probably filter the response from the phone unless configured otherwise (allow udp incoming on port 8877).
2. Android devices only receive UDP broadcasts when the screen is turned on. Yes, its not "is the app in foreground" or "is the cpu active", its "is the screen turned on". Kinda sucks, so note that for device discovery the screen must be turned on (however, the app doestn have to be shown)
3. the 0x00 no info code was originally intended that upon server startup, the client sends an empty HERE to the network to immediately appear on computers. i have kinda replaced this with relatively frequent broadcast messages.
(2) Security. On the first server start (or if the certificate expires after a year) the server generates a new keypair and self signed certificate. the actual values aren't important, it is only important for establishing the connection. On the first app start, the client generates a keypair (but without certificate. i deemed it not necessary to have another self signed certificate. where normally the certificate would be used i am using the public key instead). BTW: yes, i am kinda re-implementing something similar to TLS client certificates, but with protection against man in the middle attacks (hence i didnt choose tls client certificates)
Workflow:
1. Client connects with a normal connect, discards the server's certificate information and makes a secure handshake. (I'm using bouncy castle in its standard setup)
2. The client sends its public key to the server.
3. The server answers either with PUBLIC KEY UNKNOWN or PUBLIC KEY KNOWN, in the first case both the client and the server show a popup showing the public keys's sha1 fingerprint. the user then acknowledges the fingerprint in the app and the server sends PUBLIC KEY KNOWN (or closes the connection if the key was rejected)
4. Next, the server computes a challenge and sends it to the client. Thats actually the part where it differs most from the tls specification. The challenge isnt completely random, it consists of two parts: The server's id (=kinda like the fingerprint but using sha256 and encoded as base64), a colon, and the actual randomness. The server stores this value.
5. The client checks whether the server's id in the challenge is the same as the server id it computes from the certificate. if so, it signs this data and returns the challenge, if not, it closes the connection
6. The server checks if the response got from the client actually matches the challenge it sent and is a valid signature, if so, sends an OK
7. The client sending a similarely formed challenge to the server is planned, so the client doesnt have to rely on the server for man in the middle detection
Now, how does this prevent a man in the middle? Lets assume i am man in the middle and have two connections running with both the server and the client and pass data between both two. Now the client sees my certificate instead of the servers, but cant do anything as its just like the server's self signed. Now the server sends its challenge. I have two options: Either i pass the challenge without changing it and the client will close the connection (as the challenge's serverid isnt the same as the client sees) or i modify the challenge to match my man in the middle certificate before sending it to the client. in this case, the client signs the data, but now it signed the challenge with the fake server id. i cannot change this signature as it would invalidate the signature, so the server sees that the signature signs a different server id that for itself and will close the connection.
there is still two attack scenarios: 1. the "man in the middle" emulates a phone and can without problem accept a client connections. now it didnt earn a thing because the relevant data is on the phone, not the computer. (there is not yet a feature to directly transfer data from one phone to another) 2. the "man in the middle" emulates a computer, but then it either has to steal the computer's private key or get a user confirmation in the app once.
oh btw: confirmed public keys are stored in a database, alongside with their fingerprints (so detecting whether a client should be known or not isnt hard. i say shoudl be as anyone could fake that at this time. however, at connection level they have to send a full public key and afterwards have to confirm they have the private key for that public key too). A notification informs you how many authenticated devices are currently connected similarely to wifi (the app doesnt show connections that have not authenticated (yet) as they dont have access to anything)
(3) Battery drain. I dont know how much this will drain. i'm running a tcp server, am listening for udp packets (but only if the screen is turned on), i'm holding a WIFI lock on android plus a WIFI multicast lock (for the broadcasts), but no wake lock. The service however runs as foreground service, so i dont know whether a wake lock would be redundant or not. If it drains battery a lot, then some longer connection for like notifications wouldnt be good.
EDIT: oh yeah, i missed the most important part
source code on github:
https://github.com/monstertoss
android apk isnt available yet, the desktop app can be run using "npm install" inside the project's root (this might take a while), and then "npm start" (first start may take a while)