| title | Message Encryption |
|---|---|
| category | messages |
| type | reference |
| source | https://github.com/uport-project/specs/blob/develop/messages/encryption.md |
Some message transports are not directly secure and require encryption of the message. We currently use the box public key auhtenticated encryption algorithm and thus both parties need a Curve25519 public key to be able to create a secure session.
A public key can either be published as part of the DID document. In this case it looks for a publicKey of type Curve25519EncryptionPublicKey in the DID document. eg.
{
'@context': 'https://w3id.org/did/v1',
'id': 'did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX',
'publicKey': [
...,
{
'id': 'did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX#keys-2',
'type': 'Curve25519EncryptionPublicKey',
'owner': 'did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX',
'publicKeyBase64': 'QCFPBLm5pwmuTOu+haxv0+Vpmr6Rrz/DEEvbcjktQnQ='
}],
...
}Currently the public key must be base64 encoded in the publicKeyBase64 attribute.
If your DID method does not include an encryption key it can also be included as part of the initiation of a session setup between requesting and disclosing parties as part of the Selective Disclosure Flow.
If the requesting party does not include the encryption public key in the DID document they can include a boxPub attribute as part of the Selective Disclosure Request.
If the disclosing party does not include the encryption public key in the DID document they can also include a boxPub attribute as part of the Selective Disclosure Response.
We currently do not support the JOSE JWT standards including the JOSE CFRG ECDH RFC. This is primarily for reasons of compactness. Our responses have to be limited in size to be able to comfortably fit in a QR code.
We plan to support these in the future.
We use the ERC 1098 encryption method which uses an ephemeral sending key and the box method from tweet-nacl. This allows the recipient to decrypt a message without having to resolve the public key of the sender.
The following method is used:
- Create the signed JWT payload like normal
- JWT is padded with
\0s to the nearest multiple of 64 bytes (see "padding" below) - Create an ephemeral keypair using
nacl.box.keyPair() - Create a random 24 bytes
nonceusingnacl.randomBytes(nacl.box.nonceLength) - encrypt the resulting JWT using the
nacl.box(message, nonce, recipient publicKey, ephemeralKeyPair.secretKey) - Combine the base64 encoded versions of the above
nonce,ephemPublicKeyandciphertextvalues together with theversionofx25519-xsalsa20-poly1305in a JSON payload.
{ version: 'x25519-xsalsa20-poly1305',
nonce: '1dvWO7uOnBnO7iNDJ9kO9pTasLuKNlej',
ephemPublicKey: 'FBH1/pAEHOOW14Lu3FWkgV3qOEcuL78Zy+qW1RwzMXQ=',
ciphertext: 'f8kBcl/NCyf3sybfbwAKk/np2Bzt9lRVkZejr6uh5FgnNlH/ic62DZzy' }You need to know the recipients secretKey.
- Check that the
versionfield isx25519-xsalsa20-poly1305 - Decode the base64 encoded
nonce,ephemPublicKeyandciphertextattributes - Decrypt it message using
nacl.box.open(ciphertext, nonce, ephemPublicKey, recieverEncryptionPrivateKey) - Strip any trailing
\0from the payload - Decode JWT as normal
To avoid leaking information about the specific size of the payload, we need to pad the payload to the nearest multiple of 64 bytes. The padding should be done with \0 bytes.
After decrypting any trailing \0s are removed before passing the result to the JWT verifier.
This method is used in early versions of uPort and is only documented here for historical reasons.
To encrypt the request NACL Box Public Key Encryption is used. An ephemeral key is generated in order to encrypt the data to the public key of the user.
The encryptionPublicKey is encoded as a Base64 string. The decoded public key upk should be 32 bytes.
The ephemeral key pair is generated using the NACL library. Both secret key eSK and the public key epk has to be 32 bytes.
The nonce n should be randomly generated and of length 24.
Using the data above a NACL Box can be used to encrypt the message: c = crypto_box(m, n, upk, eSK)
In order for the mobile app to be able to decrypt the ciphertext it also needs epk and n. This needs to be formatted in a specific way. Most importantly the parameters need to be encoded as Base64 strings.
Simply create a JSON object and encode it as a string: {"from":"<epk encoded as Base64>","nonce":"<n encoded as Base64>","ciphertext":"<c encoded as Base64>"}. This string is now our encrypted message.