API.Priv.IM

What is it?

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.

How this works

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.

What you'd need to implement this in your clients

All of the above should be available (or easily reproduced) in pretty much any development environment.

@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.

Suggested API (Draft revision 2.1)

Key object data structure (version 0)

  {
    "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
  }
      
Message object data structure (version 0)

  {
    "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/USERNAME(S)/X[/KEYTYPE] (No Auth for public, Auth via Identity Delegation for private)
Get KEYTYPE (public/private) key revision number X for USERNAME(s). If requesting a public key, multiple usernames can be specified by using a comma separated list. If X is 0, this means the latest key. If X is -1, this means all key versions

Possible failures

Examples

Let's get @ryantharp's current public key
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
    }
  ]
          
Let's get @ryantharp's and @ravisorg's current 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
    }
  ]
          
Let's get @ryantharp's current key pair
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/USERNAME (Auth via Identity Delegation)
Store the key pair for USERNAME. Will return the new key version number. Private key is optional and not required (As it may be directly provided to a client to be more secure). Public key should also be broadcast on an ADN channel. See channels below. Considering:

Possible failures

Example

Let's create a new keypair for @ryantharp
POST https://api.priv.im.draft/0/json/ryantharp

  {
    "public":"VGhpcyBpcyBteSBrZXk=",
    "private":"KghpcyBpcyBteSBrZxv=" // optional
  }
          

  {
    "rev": "1357249648" // UTC Timestamp of creation value
  }
          

GET http://api.priv.im.draft/0/json/USERNAME/X/revoke (Auth via Identity Delegation)
requests that key pair revision number X for USERNAME is disabled. If X is 0, this means the latest key pair. If X is -1, this means all keypair versions

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

Possible failures

Example

Let's revoke @ryantharp's current keypair
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
    }
  ]
          

Client use cases processes

Decrypt message:
  1. Ask user for password
  2. Request the message recipient's private key specifing the message key version from the API (in case it was changed or revoked)
  3. Decrypt using AES (CBC Mode) the encrypted key with an MD5 of the provided password
  4. A correctly decrypted blob will render a valid JSON string with the following keys: n,e,d,p,q,dp,dq,c
  5. Request message sender's public key specifing the message key version from the API (in case it was changed or revoked)
  6. These keys can be loaded into an RSAKey used to decrypt the message
Encrypt message:
  1. Ask user for password
  2. Request user latest private key from the server (in case it was changed)
  3. Decrypt using AES (CBC Mode) the encrypted key with an MD5 of the provided password
  4. A correctly decrypted blob will render a valid JSON string with the following keys: n,e,d,p,q,dp,dq,c
  5. Request each message recipient's public key from the API
  6. Public and Private keys can be loaded into RSA for encryption
  7. Final encrypted message can be writen to the network
KeyPair Generation:
  1. Ask user for password (this can be different than previous keys, however make sure the user knows he still may need the old password too unless he has revoked the old password keypairs)
  2. Generate new key pair
  3. store public key as ??
  4. store private key as a valid JSON string with the following keys: n,e,d,p,q,dp,dq,c
  5. Encrypt the JSON string using AES (CBC Mode) with an MD5 of the provided password
  6. Send the resulting public and encrypted private key to the API using Base64 encoding
KeyPair Revocation:
  1. Ask user for password
  2. Verify password decryptions the current private key
  3. Blow the whistle, bang the drum, red alert, warning. Make the user think about their actions
  4. Explain the proper situations for using this feature (i.e. they feel their encryption has been compromised)
  5. 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.
  6. After user confirmation, send a request to API
  7. API will PM the user for final confirmation of deletion
  8. If user confirms the PM confirmation, then the keypair is revoked on our server

Priv.IM Notification User Annotations

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 Channel Verification

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"
        }
      }
    ]
  }

Thread Model

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:

Scenario 1 - 3rd party client or ADN account compromise

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.

Scenario 2 - Priv.IM API compromise

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

Scenario 3 - ADN compromise

?

Project Values

Use defined standards wherever possible

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

Universal support

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).

Questions & Outstanding Issues

Ordering of API and ADN channel message
It would be nice to either include the revision number in the ADN channel/message. And it would be nice to include the channelid and messageid in the API call. But we have the chicken and egg problem.
Should we only revoke public keys?
Most certification only revoke public keys.
Do revision numbers need to be protected?
If so, we can move them out of the query string and into post body where SSL can encrypt them.
Numeric version number and creation datetime or unified version number being the creation timestamp?
unified version number being the creation timestamp
short and simple json.
Numeric version number and creation datetime
non-contiguous version numbers, do they have any value?
Can we chain certs
if so we can do a per-app key pair. So that we can verify the source of the messages too. Though ADN will provide a validation of what client sent what.
Base16k or Base64 encoding
We will be using Base64 encoding
Base16k
gives us a smaller key and backwards compatibility with existing priv.im
Base64
Easier for clients to integrate and adopt
Funding operations?
Donation model. Hosting is super cheap (Amazon). Will public post Amazon bill each month for transparency.
Backup and recovery
We can back up the public encrypted private keys BUT if we user losses their password, they lose everything encrypted with that password. There is no forgot password, there is no recovery.
Delegation
We will be using delegation. @ryantharp still setting up sample.
Additional information
Slightly older information: https://priv.im/about.php
Other project ADN client developers may be interested in:
Project Llama - http://llama.adn.customwebapps.com/ - User interest searching and tagging

Discussion

Contact

contact @ryantharp on ADN, if you have any comments, questions, feedback.

Contact Form: Back to top