This is an attempt to create a cross-client encryption method for the ADN platform.
Can be used for encrypting:Encrypted messages exchanged acrossed the App.net network will not be able to read by anyone but the people with the proper private key and password. Neither of these things are ever shared across the network.
Clients will generate an assymetric key pair (a private key and public key per user). A private key must be guarded at all costs. A public key can be freely distributed. To further protect the private key, it will by encrypted with symmetric encryption (AES) and a user provided password.
Clients that support this encryption, can collect the (encrypted) private key from the priv.im API (using Identity Delegation to prevent just anyone from retrieving it), decrypt it using the password from the user, and then use that to decrypt incoming messages. To send/encrypt a message, we simply use the recipient's public key which will generate a message that can be decrypted by the recipient after obtaining/decrypting their private key.
The password and unencrypted private key must not be never sent over the network. This means the symmetric private key generation/encryption/decryption and the assymetric message encryption/decryption must be done in the client. The public key will be stored on the priv.im api in plain text, available for anyone so they can send you messages. The private key will also be stored on the priv.im API, however it will be encrypted with your password and access will be restricted to apps you've autorized.
I would suggest password have a minimum length of 8 characters, include at least one uppercase, lowercase, and a numeric character. The longer the better. I would suggest you suggest to the user to short phrases. The password should never match their ADN password.
@jazzychad brings our attention to the iOS app store, which asks "does it contain any encryption?" whenever you submit an application. Apparently there are still encryption export rules in the USA, but submitting an ERN to Apple (and perhaps Android?) can get you around this. It takes a few minutes of form filling and some waiting time, but once you're approved you can "export encryption from the USA". There's a blog post describing what you need to do.
{ "ver": 0, // key object data structure version "type": "KEYTYPE", // public or private, may be omitted if specified in request or referencing a keypair "user": "USERNAME", // ADN username, may be omitted if specified in request "rev": "REVISIONNUMBER", // UTC Timestamp of creation value // will return one of the following keys depending if key have been revoked or not "revoked": "REVOCATIONTIMESTAMP", // UTC Timestamp of revocation date and time "key": "BASE64ENCODEDKEY" // private keys will have AES encoding after the Base64 that public keys won't }
{ "ver": 0, // message object data structure version "msg": "BASE64ENCODEDMESSAGE", // Encrypted message value "rev": "REVISIONNUMBER" // Which senders keypair version it's using "number": "NUMBER" // a message number that unique to the sender that increases. This is an nonce to stop replay attacks. It could be a timestamp if you can ensure it's unique (i.e. only allow one message per second) "to": [ // a list of recipient key versions { "username":"USERNAME", // which recipient "rev":"REVISIONNUMBER", // which recipient version number keypair to use } ] }We assume the transport already includes the sender's identification (such as username) and message creation date and time.
GET http://api.priv.im.draft/0/json/ryantharp/0/public
[ // this endpoint always returns an array of keys even if there's one { "rev": "1357249648", // UTC Timestamp of creation value "key": "VGhpcyBpcyBteSBrZXk=" // Base64 encoded public key } ]
GET http://api.priv.im.draft/0/json/ryantharp,ravisorg/0/public
[ // this endpoint always returns an array of keys even if there's one { "user": "ryantharp", "rev": "1357249648", // UTC Timestamp of creation value "key": "VGhpcyBpcyBteSBrZXk=" // Base64 encoded public key }, { "user": "ravisorg", "rev": "1357249000", // UTC Timestamp of creation value "key": "BteSBrZxvKghpcyBpcy=" // Base64 encoded public key } ]
GET http://api.priv.im.draft/0/json/ryantharp/0
[ // this endpoint always returns an array of keys even if there's one { "type": "public", "rev": "1357249648", // UTC Timestamp of creation value "key": "VGhpcyBpcyBteSBrZXk=" // Base64 encoded public key }, { "type": "private", "rev": "1357249648", // UTC Timestamp of creation value "key": "KghpcyBpcyBteSBrZxv=" // Base64 encoded public key }, ]
POST https://api.priv.im.draft/0/json/ryantharp
{ "public":"VGhpcyBpcyBteSBrZXk=", "private":"KghpcyBpcyBteSBrZxv=" // optional }
{ "rev": "1357249648" // UTC Timestamp of creation value }
This puts a lot of responsibility on 3rd party app developer's user interface. You must be explicit that revoking a key will remove the ability to read all messages (sent and received) that use this keypair in all 3rd party apps.
NOTE: This may not be an instant process, we may add an intermidiary step where the system will PM the user to confirm revocation. Will include some sort of CAPTCHA system to prevent an automated response
GET http://api.priv.im.draft/0/json/ryantharp/0/revoke
[ // this endpoint always returns an array of keypairs even if there's one { "rev": "1357249648", // UTC Timestamp of creation value "revoked": "1357249999" // Estimated UTC Timestamp of revocation date and time } ]
While ADN does not have message notifications in the interactions API AND you intend to mark encrypted messages as machine_only:
If we mark encrypted messages as machine_only, the user may not be notified there is a new message available for the user. Some clients will be dumb clients (not support priv.im) and others will be smart clients (will support priv.im).
Smart clients can write this user annotation to inform other clients that this user is infact using a smart client. If notifications in a smart client are turned off, then it can report itself as a dumb client. Dumb clients will not write to this field.
When a smart client drafts an encryption message, it can check each destination user's object to see if they're using a smart client or not. If not, then a 2nd notification message can be sent for notification.
NOTE: A user with a single smart client will prevent all other clients from getting a notification. So must confirm with user that wish to receive all encryption message notifications here. Additional smart clients may be added later. Otherwise if a user still wants to be notified in other clients, then keep your client marked as a dumb client.
data: { annotations: [ { type: "im.priv.smartclients" value: { "smartclients0": ["Smarty","SmartAss","S-A-R-T, I mean S-M-A-R-T"] } } ] }
Additional we can create a channel for the user and list all public key creations . This would prevent some attacks where ADN or the API is compromised but not both. Priv.Im would not be allowed to write to this personal user channel. a REVISIONNUMBER implies the API has to be called first.
data: { annotations: [ { type: "im.priv.publickey" value: { "REVISIONNUMBER": "BASE64ENCODEDPUBLICKEY" } } ] }
This API places a lot of the trust on the 3rd party developers. As soon as a 3rd party client has access to an ADN account, it can:
A compromised 3rd party developer could build a fake app, get a user to auth with it, and brute force their private key looking for a valid json response. Or a compormised ADN account could be plugged into a malious app to perform unwant changes.
If a user thinks they're underattack by an app, they can revoke their token.
This will only help a little since the malicious app may already have downloaded a copy of the encrypted key and encrypted messages.
We have to enable users to make their own trust decisions, while doing what we can to inform them of the consequences of the decisions they make. We simply cannot stop them from making decisions. And I think it's in all of our best interest to try educate them and provide UI that hints users in the right directions.
Also users can change their private key to prevent newer messages from being read.a compromised priv.im could facilitate any man in the middle attacks it wants by handing out forged public keys for new message
This means Priv.IM API can not write public keys to a channel
?
Encryption is hard. Don't do it yourself, use established ciphers and libraries that have been tested. When at all possible use concepts that are already widely in use. Easier to implement, easier to understand. This system uses AES, RSA and Unicode to work, all of which are very standard and supported on any modern system. It should be reasonably easy to implement on any device, including web based clients A 1024 bit RSA key , which provides reasonable security and allows a full message plus a message key to be encrypted
For this to work well, this needs to be a standard of sorts. We can't have each client supporting their own version of Private Messaging that works slightly different than the rest. For that reason I've used pretty common technologies that are widely available on most platforms. The exception being base16k, which is reasonably easy to implement (and links to JS and C++ code is below).