switch RST to MD, thanks to @aaronraimist. fixes spec docs in gitlab
This commit is contained in:
parent
214908ace5
commit
7682be97d7
7 changed files with 770 additions and 871 deletions
|
@ -1,5 +1,4 @@
|
||||||
Contributing code to libolm
|
# Contributing code to libolm
|
||||||
===========================
|
|
||||||
|
|
||||||
To contribute code to this library, the preferred way is to clone the git
|
To contribute code to this library, the preferred way is to clone the git
|
||||||
repository, create a git patch series (for example via ``git
|
repository, create a git patch series (for example via ``git
|
||||||
|
@ -8,18 +7,16 @@ format-patch --stdout origin/master``), and send this by email to
|
||||||
|
|
||||||
Naturally, you must be willing to license your contributions under the same
|
Naturally, you must be willing to license your contributions under the same
|
||||||
license as the project itself - in this case, Apache Software License v2 (see
|
license as the project itself - in this case, Apache Software License v2 (see
|
||||||
`<LICENSE>`_).
|
[LICENSE](LICENSE)).
|
||||||
|
|
||||||
Sign off
|
## Sign off
|
||||||
--------
|
|
||||||
|
|
||||||
In order to have a concrete record that your contribution is intentional and
|
In order to have a concrete record that your contribution is intentional and
|
||||||
you agree to license it under the same terms as the project's license, we've
|
you agree to license it under the same terms as the project's license, we've
|
||||||
adopted the same lightweight approach that the
|
adopted the same lightweight approach that the
|
||||||
`Linux Kernel <https://www.kernel.org/doc/Documentation/SubmittingPatches>`_,
|
[Linux Kernel](https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin),
|
||||||
`Docker <https://github.com/docker/docker/blob/master/CONTRIBUTING.md>`_,
|
[Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md),
|
||||||
and many other projects use: the DCO
|
and many other projects use: the DCO ([Developer Certificate of Origin](http://developercertificate.org/)).
|
||||||
(`Developer Certificate of Origin <http://developercertificate.org/>`_).
|
|
||||||
This is a simple declaration that you wrote the contribution or otherwise have
|
This is a simple declaration that you wrote the contribution or otherwise have
|
||||||
the right to contribute it to Matrix::
|
the right to contribute it to Matrix::
|
||||||
|
|
|
@ -1,78 +1,75 @@
|
||||||
Olm
|
# Olm
|
||||||
===
|
|
||||||
|
|
||||||
An implementation of the Double Ratchet cryptographic ratchet described by
|
An implementation of the Double Ratchet cryptographic ratchet described by
|
||||||
https://whispersystems.org/docs/specifications/doubleratchet/, written in C and
|
https://whispersystems.org/docs/specifications/doubleratchet/, written in C and
|
||||||
C++11 and exposed as a C API.
|
C++11 and exposed as a C API.
|
||||||
|
|
||||||
The specification of the Olm ratchet can be found in `<docs/olm.rst>`_.
|
The specification of the Olm ratchet can be found in [docs/olm.md](docs/olm.md).
|
||||||
|
|
||||||
This library also includes an implementation of the Megolm cryptographic
|
This library also includes an implementation of the Megolm cryptographic
|
||||||
ratchet, as specified in `<docs/megolm.rst>`_.
|
ratchet, as specified in [docs/megolm.md](docs/megolm.md).
|
||||||
|
|
||||||
Building
|
## Building
|
||||||
--------
|
|
||||||
|
|
||||||
To build olm as a shared library run either:
|
To build olm as a shared library run either:
|
||||||
|
|
||||||
.. code:: bash
|
```bash
|
||||||
|
|
||||||
cmake . -Bbuild
|
cmake . -Bbuild
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
```
|
||||||
|
|
||||||
or:
|
or:
|
||||||
|
|
||||||
.. code:: bash
|
```bash
|
||||||
|
|
||||||
make
|
make
|
||||||
|
```
|
||||||
|
|
||||||
Using cmake is the preferred method for building the shared library; the
|
Using cmake is the preferred method for building the shared library; the
|
||||||
Makefile may be removed in the future.
|
Makefile may be removed in the future.
|
||||||
|
|
||||||
To run the tests when using cmake, run:
|
To run the tests when using cmake, run:
|
||||||
|
|
||||||
.. code:: bash
|
```bash
|
||||||
|
|
||||||
cd build/tests
|
cd build/tests
|
||||||
ctest .
|
ctest .
|
||||||
|
```
|
||||||
To run the tests when using make, run:
|
To run the tests when using make, run:
|
||||||
|
|
||||||
.. code:: bash
|
```bash
|
||||||
|
|
||||||
make test
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
To build the JavaScript bindings, install emscripten from http://kripken.github.io/emscripten-site/ and then run:
|
To build the JavaScript bindings, install emscripten from http://kripken.github.io/emscripten-site/ and then run:
|
||||||
|
|
||||||
.. code:: bash
|
```bash
|
||||||
|
|
||||||
make js
|
make js
|
||||||
|
```
|
||||||
|
|
||||||
Note that if you run emscripten in a docker container, you need to pass through
|
Note that if you run emscripten in a docker container, you need to pass through
|
||||||
the EMCC_CLOSURE_ARGS environment variable.
|
the EMCC_CLOSURE_ARGS environment variable.
|
||||||
|
|
||||||
To build the android project for Android bindings, run:
|
To build the android project for Android bindings, run:
|
||||||
|
|
||||||
.. code:: bash
|
```bash
|
||||||
|
|
||||||
cd android
|
cd android
|
||||||
./gradlew clean assembleRelease
|
./gradlew clean assembleRelease
|
||||||
|
```
|
||||||
|
|
||||||
To build the Xcode workspace for Objective-C bindings, run:
|
To build the Xcode workspace for Objective-C bindings, run:
|
||||||
|
|
||||||
.. code:: bash
|
```bash
|
||||||
|
|
||||||
cd xcode
|
cd xcode
|
||||||
pod install
|
pod install
|
||||||
open OLMKit.xcworkspace
|
open OLMKit.xcworkspace
|
||||||
|
```
|
||||||
|
|
||||||
To build the Python bindings, first build olm as a shared library as above, and
|
To build the Python bindings, first build olm as a shared library as above, and
|
||||||
then run:
|
then run:
|
||||||
|
|
||||||
.. code:: bash
|
```bash
|
||||||
|
|
||||||
cd python
|
cd python
|
||||||
make
|
make
|
||||||
|
```
|
||||||
|
|
||||||
to make both the Python 2 and Python 3 bindings. To make only one version, use
|
to make both the Python 2 and Python 3 bindings. To make only one version, use
|
||||||
``make olm-python2`` or ``make olm-python3`` instead of just ``make``.
|
``make olm-python2`` or ``make olm-python3`` instead of just ``make``.
|
||||||
|
@ -80,27 +77,25 @@ to make both the Python 2 and Python 3 bindings. To make only one version, use
|
||||||
To build olm as a static library (which still needs libstdc++ dynamically) run
|
To build olm as a static library (which still needs libstdc++ dynamically) run
|
||||||
either:
|
either:
|
||||||
|
|
||||||
.. code:: bash
|
```bash
|
||||||
|
|
||||||
cmake . -Bbuild -DBUILD_SHARED_LIBS=NO
|
cmake . -Bbuild -DBUILD_SHARED_LIBS=NO
|
||||||
cmake --build build
|
cmake --build build
|
||||||
|
```
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
.. code:: bash
|
```bash
|
||||||
|
|
||||||
make static
|
make static
|
||||||
|
```
|
||||||
|
|
||||||
The library can also be used as a dependency with CMake using:
|
The library can also be used as a dependency with CMake using:
|
||||||
|
|
||||||
.. code:: cmake
|
```cmake
|
||||||
|
|
||||||
find_package(Olm::Olm REQUIRED)
|
find_package(Olm::Olm REQUIRED)
|
||||||
target_link_libraries(my_exe Olm::Olm)
|
target_link_libraries(my_exe Olm::Olm)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Release process
|
||||||
Release process
|
|
||||||
---------------
|
|
||||||
|
|
||||||
First: bump version numbers in ``common.mk``, ``CMakeLists.txt``,
|
First: bump version numbers in ``common.mk``, ``CMakeLists.txt``,
|
||||||
``javascript/package.json``, ``python/olm/__version__.py``, ``OLMKit.podspec``,
|
``javascript/package.json``, ``python/olm/__version__.py``, ``OLMKit.podspec``,
|
||||||
|
@ -113,8 +108,7 @@ git.
|
||||||
It's probably sensible to do the above on a release branch (``release-vx.y.z``
|
It's probably sensible to do the above on a release branch (``release-vx.y.z``
|
||||||
by convention), and merge back to master once the release is complete.
|
by convention), and merge back to master once the release is complete.
|
||||||
|
|
||||||
.. code:: bash
|
```bash
|
||||||
|
|
||||||
make clean
|
make clean
|
||||||
|
|
||||||
# build and test C library
|
# build and test C library
|
||||||
|
@ -137,10 +131,9 @@ by convention), and merge back to master once the release is complete.
|
||||||
pod trunk push OLMKit.podspec --use-libraries --allow-warnings
|
pod trunk push OLMKit.podspec --use-libraries --allow-warnings
|
||||||
# Check the pod has been successully published with:
|
# Check the pod has been successully published with:
|
||||||
pod search OLMKit
|
pod search OLMKit
|
||||||
|
```
|
||||||
|
|
||||||
|
## Design
|
||||||
Design
|
|
||||||
------
|
|
||||||
|
|
||||||
Olm is designed to be easy port to different platforms and to be easy
|
Olm is designed to be easy port to different platforms and to be easy
|
||||||
to write bindings for.
|
to write bindings for.
|
||||||
|
@ -150,46 +143,40 @@ API. As development has progressed, it has become clear that C++ gives little
|
||||||
advantage, and new functionality is being added in C, with C++ parts being
|
advantage, and new functionality is being added in C, with C++ parts being
|
||||||
rewritten as the need ariases.
|
rewritten as the need ariases.
|
||||||
|
|
||||||
Error Handling
|
### Error Handling
|
||||||
~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
All C functions in the API for olm return ``olm_error()`` on error.
|
All C functions in the API for olm return ``olm_error()`` on error.
|
||||||
This makes it easy to check for error conditions within the language bindings.
|
This makes it easy to check for error conditions within the language bindings.
|
||||||
|
|
||||||
Random Numbers
|
### Random Numbers
|
||||||
~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Olm doesn't generate random numbers itself. Instead the caller must
|
Olm doesn't generate random numbers itself. Instead the caller must
|
||||||
provide the random data. This makes it easier to port the library to different
|
provide the random data. This makes it easier to port the library to different
|
||||||
platforms since the caller can use whatever cryptographic random number
|
platforms since the caller can use whatever cryptographic random number
|
||||||
generator their platform provides.
|
generator their platform provides.
|
||||||
|
|
||||||
Memory
|
### Memory
|
||||||
~~~~~~
|
|
||||||
|
|
||||||
Olm avoids calling malloc or allocating memory on the heap itself.
|
Olm avoids calling malloc or allocating memory on the heap itself.
|
||||||
Instead the library calculates how much memory will be needed to hold the
|
Instead the library calculates how much memory will be needed to hold the
|
||||||
output and the caller supplies a buffer of the appropriate size.
|
output and the caller supplies a buffer of the appropriate size.
|
||||||
|
|
||||||
Output Encoding
|
### Output Encoding
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Binary output is encoded as base64 so that languages that prefer unicode
|
Binary output is encoded as base64 so that languages that prefer unicode
|
||||||
strings will find it easier to handle the output.
|
strings will find it easier to handle the output.
|
||||||
|
|
||||||
Dependencies
|
### Dependencies
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Olm uses pure C implementations of the cryptographic primitives used by
|
Olm uses pure C implementations of the cryptographic primitives used by
|
||||||
the ratchet. While this decreases the performance it makes it much easier
|
the ratchet. While this decreases the performance it makes it much easier
|
||||||
to compile the library for different architectures.
|
to compile the library for different architectures.
|
||||||
|
|
||||||
Contributing
|
## Contributing
|
||||||
------------
|
|
||||||
Please see `<CONTRIBUTING.rst>`_ when making contributions to the library.
|
|
||||||
|
|
||||||
Security assessment
|
Please see [CONTRIBUTING.md](CONTRIBUTING.md) when making contributions to the library.
|
||||||
-------------------
|
|
||||||
|
## Security assessment
|
||||||
|
|
||||||
Olm 1.3.0 was independently assessed by NCC Group's Cryptography Services
|
Olm 1.3.0 was independently assessed by NCC Group's Cryptography Services
|
||||||
Practive in September 2016 to check for security issues: you can read all
|
Practive in September 2016 to check for security issues: you can read all
|
||||||
|
@ -197,18 +184,16 @@ about it at
|
||||||
https://www.nccgroup.trust/us/our-research/matrix-olm-cryptographic-review/
|
https://www.nccgroup.trust/us/our-research/matrix-olm-cryptographic-review/
|
||||||
and https://matrix.org/blog/2016/11/21/matrixs-olm-end-to-end-encryption-security-assessment-released-and-implemented-cross-platform-on-riot-at-last/
|
and https://matrix.org/blog/2016/11/21/matrixs-olm-end-to-end-encryption-security-assessment-released-and-implemented-cross-platform-on-riot-at-last/
|
||||||
|
|
||||||
Bug reports
|
## Bug reports
|
||||||
-----------
|
|
||||||
Please file bug reports at https://github.com/matrix-org/olm/issues
|
Please file bug reports at https://github.com/matrix-org/olm/issues
|
||||||
|
|
||||||
What's an olm?
|
## What's an olm?
|
||||||
--------------
|
|
||||||
|
|
||||||
It's a really cool species of European troglodytic salamander.
|
It's a really cool species of European troglodytic salamander.
|
||||||
http://www.postojnska-jama.eu/en/come-and-visit-us/vivarium-proteus/
|
http://www.postojnska-jama.eu/en/come-and-visit-us/vivarium-proteus/
|
||||||
|
|
||||||
Legal Notice
|
## Legal Notice
|
||||||
------------
|
|
||||||
|
|
||||||
The software may be subject to the U.S. export control laws and regulations
|
The software may be subject to the U.S. export control laws and regulations
|
||||||
and by downloading the software the user certifies that he/she/it is
|
and by downloading the software the user certifies that he/she/it is
|
325
docs/megolm.md
Normal file
325
docs/megolm.md
Normal file
|
@ -0,0 +1,325 @@
|
||||||
|
# Megolm group ratchet
|
||||||
|
|
||||||
|
An AES-based cryptographic ratchet intended for group communications.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
The Megolm ratchet is intended for encrypted messaging applications where there
|
||||||
|
may be a large number of recipients of each message, thus precluding the use of
|
||||||
|
peer-to-peer encryption systems such as [Olm][].
|
||||||
|
|
||||||
|
It also allows a recipient to decrypt received messages multiple times. For
|
||||||
|
instance, in client/server applications, a copy of the ciphertext can be stored
|
||||||
|
on the (untrusted) server, while the client need only store the session keys.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Each participant in a conversation uses their own outbound session for
|
||||||
|
encrypting messages. A session consists of a ratchet and an [Ed25519][] keypair.
|
||||||
|
|
||||||
|
Secrecy is provided by the ratchet, which can be wound forwards but not
|
||||||
|
backwards, and is used to derive a distinct message key for each message.
|
||||||
|
|
||||||
|
Authenticity is provided via Ed25519 signatures.
|
||||||
|
|
||||||
|
The value of the ratchet, and the public part of the Ed25519 key, are shared
|
||||||
|
with other participants in the conversation via secure peer-to-peer
|
||||||
|
channels. Provided that peer-to-peer channel provides authenticity of the
|
||||||
|
messages to the participants and deniability of the messages to third parties,
|
||||||
|
the Megolm session will inherit those properties.
|
||||||
|
|
||||||
|
## The Megolm ratchet algorithm
|
||||||
|
|
||||||
|
The Megolm ratchet $`R_i`$ consists of four parts, $`R_{i,j}`$ for
|
||||||
|
$`j \in {0,1,2,3}`$. The length of each part depends on the hash function
|
||||||
|
in use (256 bits for this version of Megolm).
|
||||||
|
|
||||||
|
The ratchet is initialised with cryptographically-secure random data, and
|
||||||
|
advanced as follows:
|
||||||
|
|
||||||
|
```math
|
||||||
|
\begin{aligned}
|
||||||
|
R_{i,0} &=
|
||||||
|
\begin{cases}
|
||||||
|
H_0\left(R_{2^24(n-1),0}\right) &\text{if }\exists n | i = 2^24n\\
|
||||||
|
R_{i-1,0} &\text{otherwise}
|
||||||
|
\end{cases}\\
|
||||||
|
R_{i,1} &=
|
||||||
|
\begin{cases}
|
||||||
|
H_1\left(R_{2^24(n-1),0}\right) &\text{if }\exists n | i = 2^24n\\
|
||||||
|
H_1\left(R_{2^16(m-1),1}\right) &\text{if }\exists m | i = 2^16m\\
|
||||||
|
R_{i-1,1} &\text{otherwise}
|
||||||
|
\end{cases}\\
|
||||||
|
R_{i,2} &=
|
||||||
|
\begin{cases}
|
||||||
|
H_2\left(R_{2^24(n-1),0}\right) &\text{if }\exists n | i = 2^24n\\
|
||||||
|
H_2\left(R_{2^16(m-1),1}\right) &\text{if }\exists m | i = 2^16m\\
|
||||||
|
H_2\left(R_{2^8(p-1),2}\right) &\text{if }\exists p | i = 2^8p\\
|
||||||
|
R_{i-1,2} &\text{otherwise}
|
||||||
|
\end{cases}\\
|
||||||
|
R_{i,3} &=
|
||||||
|
\begin{cases}
|
||||||
|
H_3\left(R_{2^24(n-1),0}\right) &\text{if }\exists n | i = 2^24n\\
|
||||||
|
H_3\left(R_{2^16(m-1),1}\right) &\text{if }\exists m | i = 2^16m\\
|
||||||
|
H_3\left(R_{2^8(p-1),2}\right) &\text{if }\exists p | i = 2^8p\\
|
||||||
|
H_3\left(R_{i-1,3}\right) &\text{otherwise}
|
||||||
|
\end{cases}
|
||||||
|
\end{aligned}
|
||||||
|
```
|
||||||
|
|
||||||
|
where $`H_0`$, $`H_1`$, $`H_2`$, and $`H_3`$ are different hash
|
||||||
|
functions. In summary: every $`2^8`$ iterations, $`R_{i,3}`$ is
|
||||||
|
reseeded from $`R_{i,2}`$. Every $`2^16`$ iterations, $`R_{i,2}`$
|
||||||
|
and $`R_{i,3}`$ are reseeded from $`R_{i,1}`$. Every $`2^24`$
|
||||||
|
iterations, $`R_{i,1}`$, $`R_{i,2}`$ and $`R_{i,3}`$ are reseeded
|
||||||
|
from $`R_{i,0}`$.
|
||||||
|
|
||||||
|
The complete ratchet value, $`R_{i}`$, is hashed to generate the keys used
|
||||||
|
to encrypt each message. This scheme allows the ratchet to be advanced an
|
||||||
|
arbitrary amount forwards while needing at most 1023 hash computations. A
|
||||||
|
client can decrypt chat history onwards from the earliest value of the ratchet
|
||||||
|
it is aware of, but cannot decrypt history from before that point without
|
||||||
|
reversing the hash function.
|
||||||
|
|
||||||
|
This allows a participant to share its ability to decrypt chat history with
|
||||||
|
another from a point in the conversation onwards by giving a copy of the
|
||||||
|
ratchet at that point in the conversation.
|
||||||
|
|
||||||
|
|
||||||
|
## The Megolm protocol
|
||||||
|
|
||||||
|
### Session setup
|
||||||
|
|
||||||
|
Each participant in a conversation generates their own Megolm session. A
|
||||||
|
session consists of three parts:
|
||||||
|
|
||||||
|
* a 32 bit counter, $`i`$.
|
||||||
|
* an [Ed25519][] keypair, $`K`$.
|
||||||
|
* a ratchet, $`R_i`$, which consists of four 256-bit values,
|
||||||
|
$`R_{i,j}`$ for $`j \in {0,1,2,3}`$.
|
||||||
|
|
||||||
|
The counter $`i`$ is initialised to $`0`$. A new Ed25519 keypair is
|
||||||
|
generated for $`K`$. The ratchet is simply initialised with 1024 bits of
|
||||||
|
cryptographically-secure random data.
|
||||||
|
|
||||||
|
A single participant may use multiple sessions over the lifetime of a
|
||||||
|
conversation. The public part of $`K`$ is used as an identifier to
|
||||||
|
discriminate between sessions.
|
||||||
|
|
||||||
|
### Sharing session data
|
||||||
|
|
||||||
|
To allow other participants in the conversation to decrypt messages, the
|
||||||
|
session data is formatted as described in [Session-sharing format](#Session-sharing-format). It is then
|
||||||
|
shared with other participants in the conversation via a secure peer-to-peer
|
||||||
|
channel (such as that provided by [Olm][]).
|
||||||
|
|
||||||
|
When the session data is received from other participants, the recipient first
|
||||||
|
checks that the signature matches the public key. They then store their own
|
||||||
|
copy of the counter, ratchet, and public key.
|
||||||
|
|
||||||
|
### Message encryption
|
||||||
|
|
||||||
|
This version of Megolm uses AES-256_ in CBC_ mode with [PKCS#7][] padding and
|
||||||
|
HMAC-SHA-256_ (truncated to 64 bits). The 256 bit AES key, 256 bit HMAC key,
|
||||||
|
and 128 bit AES IV are derived from the megolm ratchet $`R_i`$:
|
||||||
|
|
||||||
|
```math
|
||||||
|
\begin{aligned}
|
||||||
|
AES\_KEY_{i}\;\parallel\;HMAC\_KEY_{i}\;\parallel\;AES\_IV_{i}
|
||||||
|
&= HKDF\left(0,\,R_{i},\text{"MEGOLM\_KEYS"},\,80\right) \\
|
||||||
|
\end{aligned}
|
||||||
|
```
|
||||||
|
|
||||||
|
where $`\parallel`$ represents string splitting, and
|
||||||
|
$`HKDF\left(salt,\,IKM,\,info,\,L\right)`$ refers to the [HMAC-based key
|
||||||
|
derivation function][] using using [SHA-256][] as the hash function
|
||||||
|
([HKDF-SHA-256][]) with a salt value of $`salt`$, input key material of
|
||||||
|
$`IKM`$, context string $`info`$, and output keying material length of
|
||||||
|
$`L`$ bytes.
|
||||||
|
|
||||||
|
The plain-text is encrypted with AES-256, using the key $`AES\_KEY_{i}`$
|
||||||
|
and the IV $`AES\_IV_{i}`$ to give the cipher-text, $`X_{i}`$.
|
||||||
|
|
||||||
|
The ratchet index $`i`$, and the cipher-text $`X_{i}`$, are then packed
|
||||||
|
into a message as described in [Message format](#message-format). Then the entire message
|
||||||
|
(including the version bytes and all payload bytes) are passed through
|
||||||
|
HMAC-SHA-256. The first 8 bytes of the MAC are appended to the message.
|
||||||
|
|
||||||
|
Finally, the authenticated message is signed using the Ed25519 keypair; the 64
|
||||||
|
byte signature is appended to the message.
|
||||||
|
|
||||||
|
The complete signed message, together with the public part of $`K`$ (acting
|
||||||
|
as a session identifier), can then be sent over an insecure channel. The
|
||||||
|
message can then be authenticated and decrypted only by recipients who have
|
||||||
|
received the session data.
|
||||||
|
|
||||||
|
### Advancing the ratchet
|
||||||
|
|
||||||
|
After each message is encrypted, the ratchet is advanced. This is done as
|
||||||
|
described in [The Megolm ratchet algorithm](#the-megolm-ratchet-algorithm), using the following definitions:
|
||||||
|
|
||||||
|
```math
|
||||||
|
\begin{aligned}
|
||||||
|
H_0(A) &\equiv HMAC(A,\text{"\x00"}) \\
|
||||||
|
H_1(A) &\equiv HMAC(A,\text{"\x01"}) \\
|
||||||
|
H_2(A) &\equiv HMAC(A,\text{"\x02"}) \\
|
||||||
|
H_3(A) &\equiv HMAC(A,\text{"\x03"}) \\
|
||||||
|
\end{aligned}
|
||||||
|
```
|
||||||
|
|
||||||
|
where $`HMAC(A, T)`$ is the HMAC-SHA-256 of ``T``, using ``A`` as the
|
||||||
|
key.
|
||||||
|
|
||||||
|
For outbound sessions, the updated ratchet and counter are stored in the
|
||||||
|
session.
|
||||||
|
|
||||||
|
In order to maintain the ability to decrypt conversation history, inbound
|
||||||
|
sessions should store a copy of their earliest known ratchet value (unless they
|
||||||
|
explicitly want to drop the ability to decrypt that history - see [Partial
|
||||||
|
Forward Secrecy](#partial-forward-secrecy)). They may also choose to cache calculated ratchet values,
|
||||||
|
but the decision of which ratchet states to cache is left to the application.
|
||||||
|
|
||||||
|
## Data exchange formats
|
||||||
|
|
||||||
|
### Session-sharing format
|
||||||
|
|
||||||
|
The Megolm key-sharing format is as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
+---+----+--------+--------+--------+--------+------+-----------+
|
||||||
|
| V | i | R(i,0) | R(i,1) | R(i,2) | R(i,3) | Kpub | Signature |
|
||||||
|
+---+----+--------+--------+--------+--------+------+-----------+
|
||||||
|
0 1 5 37 69 101 133 165 229 bytes
|
||||||
|
```
|
||||||
|
|
||||||
|
The version byte, ``V``, is ``"\x02"``.
|
||||||
|
|
||||||
|
This is followed by the ratchet index, $`i`$, which is encoded as a
|
||||||
|
big-endian 32-bit integer; the ratchet values $`R_{i,j}`$; and the public
|
||||||
|
part of the Ed25519 keypair $`K`$.
|
||||||
|
|
||||||
|
The data is then signed using the Ed25519 keypair, and the 64-byte signature is
|
||||||
|
appended.
|
||||||
|
|
||||||
|
### Message format
|
||||||
|
|
||||||
|
Megolm messages consist of a one byte version, followed by a variable length
|
||||||
|
payload, a fixed length message authentication code, and a fixed length
|
||||||
|
signature.
|
||||||
|
|
||||||
|
```
|
||||||
|
+---+------------------------------------+-----------+------------------+
|
||||||
|
| V | Payload Bytes | MAC Bytes | Signature Bytes |
|
||||||
|
+---+------------------------------------+-----------+------------------+
|
||||||
|
0 1 N N+8 N+72 bytes
|
||||||
|
```
|
||||||
|
|
||||||
|
The version byte, ``V``, is ``"\x03"``.
|
||||||
|
|
||||||
|
The payload uses a format based on the [Protocol Buffers encoding][]. It
|
||||||
|
consists of the following key-value pairs:
|
||||||
|
|
||||||
|
**Name**|**Tag**|**Type**|**Meaning**
|
||||||
|
:-----:|:-----:|:-----:|:-----:
|
||||||
|
Message-Index|0x08|Integer|The index of the ratchet, i
|
||||||
|
Cipher-Text|0x12|String|The cipher-text, Xi, of the message
|
||||||
|
|
||||||
|
Within the payload, integers are encoded using a variable length encoding. Each
|
||||||
|
integer is encoded as a sequence of bytes with the high bit set followed by a
|
||||||
|
byte with the high bit clear. The seven low bits of each byte store the bits of
|
||||||
|
the integer. The least significant bits are stored in the first byte.
|
||||||
|
|
||||||
|
Strings are encoded as a variable-length integer followed by the string itself.
|
||||||
|
|
||||||
|
Each key-value pair is encoded as a variable-length integer giving the tag,
|
||||||
|
followed by a string or variable-length integer giving the value.
|
||||||
|
|
||||||
|
The payload is followed by the MAC. The length of the MAC is determined by the
|
||||||
|
authenticated encryption algorithm being used (8 bytes in this version of the
|
||||||
|
protocol). The MAC protects all of the bytes preceding the MAC.
|
||||||
|
|
||||||
|
The length of the signature is determined by the signing algorithm being used
|
||||||
|
(64 bytes in this version of the protocol). The signature covers all of the
|
||||||
|
bytes preceding the signature.
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
### Message Replays
|
||||||
|
|
||||||
|
A message can be decrypted successfully multiple times. This means that an
|
||||||
|
attacker can re-send a copy of an old message, and the recipient will treat it
|
||||||
|
as a new message.
|
||||||
|
|
||||||
|
To mitigate this it is recommended that applications track the ratchet indices
|
||||||
|
they have received and that they reject messages with a ratchet index that
|
||||||
|
they have already decrypted.
|
||||||
|
|
||||||
|
### Lack of Transcript Consistency
|
||||||
|
|
||||||
|
In a group conversation, there is no guarantee that all recipients have
|
||||||
|
received the same messages. For example, if Alice is in a conversation with Bob
|
||||||
|
and Charlie, she could send different messages to Bob and Charlie, or could
|
||||||
|
send some messages to Bob but not Charlie, or vice versa.
|
||||||
|
|
||||||
|
Solving this is, in general, a hard problem, particularly in a protocol which
|
||||||
|
does not guarantee in-order message delivery. For now it remains the subject of
|
||||||
|
future research.
|
||||||
|
|
||||||
|
### Lack of Backward Secrecy
|
||||||
|
|
||||||
|
Once the key to a Megolm session is compromised, the attacker can decrypt any
|
||||||
|
future messages sent via that session.
|
||||||
|
|
||||||
|
In order to mitigate this, the application should ensure that Megolm sessions
|
||||||
|
are not used indefinitely. Instead it should periodically start a new session,
|
||||||
|
with new keys shared over a secure channel.
|
||||||
|
|
||||||
|
<!-- TODO: Can we recommend sensible lifetimes for Megolm sessions? Probably
|
||||||
|
depends how paranoid we're feeling, but some guidelines might be useful. -->
|
||||||
|
|
||||||
|
### Partial Forward Secrecy
|
||||||
|
|
||||||
|
Each recipient maintains a record of the ratchet value which allows them to
|
||||||
|
decrypt any messages sent in the session after the corresponding point in the
|
||||||
|
conversation. If this value is compromised, an attacker can similarly decrypt
|
||||||
|
those past messages.
|
||||||
|
|
||||||
|
To mitigate this issue, the application should offer the user the option to
|
||||||
|
discard historical conversations, by winding forward any stored ratchet values,
|
||||||
|
or discarding sessions altogether.
|
||||||
|
|
||||||
|
### Dependency on secure channel for key exchange
|
||||||
|
|
||||||
|
The design of the Megolm ratchet relies on the availability of a secure
|
||||||
|
peer-to-peer channel for the exchange of session keys. Any vulnerabilities in
|
||||||
|
the underlying channel are likely to be amplified when applied to Megolm
|
||||||
|
session setup.
|
||||||
|
|
||||||
|
For example, if the peer-to-peer channel is vulnerable to an unknown key-share
|
||||||
|
attack, the entire Megolm session become similarly vulnerable. For example:
|
||||||
|
Alice starts a group chat with Eve, and shares the session keys with Eve. Eve
|
||||||
|
uses the unknown key-share attack to forward the session keys to Bob, who
|
||||||
|
believes Alice is starting the session with him. Eve then forwards messages
|
||||||
|
from the Megolm session to Bob, who again believes they are coming from
|
||||||
|
Alice. Provided the peer-to-peer channel is not vulnerable to this attack, Bob
|
||||||
|
will realise that the key-sharing message was forwarded by Eve, and can treat
|
||||||
|
the Megolm session as a forgery.
|
||||||
|
|
||||||
|
A second example: if the peer-to-peer channel is vulnerable to a replay
|
||||||
|
attack, this can be extended to entire Megolm sessions.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The Megolm specification (this document) is licensed under the Apache License,
|
||||||
|
Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
|
||||||
|
[Ed25519]: http://ed25519.cr.yp.to/
|
||||||
|
[HMAC-based key derivation function]: https://tools.ietf.org/html/rfc5869
|
||||||
|
[HKDF-SHA-256]: https://tools.ietf.org/html/rfc5869
|
||||||
|
[HMAC-SHA-256]: https://tools.ietf.org/html/rfc2104
|
||||||
|
[SHA-256]: https://tools.ietf.org/html/rfc6234
|
||||||
|
[AES-256]: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
||||||
|
[CBC]: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
|
||||||
|
[PKCS#7]: https://tools.ietf.org/html/rfc2315
|
||||||
|
[Olm]: https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/olm.md
|
||||||
|
[Protocol Buffers encoding]: https://developers.google.com/protocol-buffers/docs/encoding
|
362
docs/megolm.rst
362
docs/megolm.rst
|
@ -1,362 +0,0 @@
|
||||||
.. Copyright 2016 OpenMarket Ltd
|
|
||||||
..
|
|
||||||
.. Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
.. you may not use this file except in compliance with the License.
|
|
||||||
.. You may obtain a copy of the License at
|
|
||||||
..
|
|
||||||
.. http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
..
|
|
||||||
.. Unless required by applicable law or agreed to in writing, software
|
|
||||||
.. distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
.. See the License for the specific language governing permissions and
|
|
||||||
.. limitations under the License.
|
|
||||||
|
|
||||||
|
|
||||||
Megolm group ratchet
|
|
||||||
====================
|
|
||||||
|
|
||||||
An AES-based cryptographic ratchet intended for group communications.
|
|
||||||
|
|
||||||
.. contents::
|
|
||||||
|
|
||||||
Background
|
|
||||||
----------
|
|
||||||
|
|
||||||
The Megolm ratchet is intended for encrypted messaging applications where there
|
|
||||||
may be a large number of recipients of each message, thus precluding the use of
|
|
||||||
peer-to-peer encryption systems such as `Olm`_.
|
|
||||||
|
|
||||||
It also allows a recipient to decrypt received messages multiple times. For
|
|
||||||
instance, in client/server applications, a copy of the ciphertext can be stored
|
|
||||||
on the (untrusted) server, while the client need only store the session keys.
|
|
||||||
|
|
||||||
Overview
|
|
||||||
--------
|
|
||||||
|
|
||||||
Each participant in a conversation uses their own outbound session for
|
|
||||||
encrypting messages. A session consists of a ratchet and an `Ed25519`_ keypair.
|
|
||||||
|
|
||||||
Secrecy is provided by the ratchet, which can be wound forwards but not
|
|
||||||
backwards, and is used to derive a distinct message key for each message.
|
|
||||||
|
|
||||||
Authenticity is provided via Ed25519 signatures.
|
|
||||||
|
|
||||||
The value of the ratchet, and the public part of the Ed25519 key, are shared
|
|
||||||
with other participants in the conversation via secure peer-to-peer
|
|
||||||
channels. Provided that peer-to-peer channel provides authenticity of the
|
|
||||||
messages to the participants and deniability of the messages to third parties,
|
|
||||||
the Megolm session will inherit those properties.
|
|
||||||
|
|
||||||
The Megolm ratchet algorithm
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
The Megolm ratchet :math:`R_i` consists of four parts, :math:`R_{i,j}` for
|
|
||||||
:math:`j \in {0,1,2,3}`. The length of each part depends on the hash function
|
|
||||||
in use (256 bits for this version of Megolm).
|
|
||||||
|
|
||||||
The ratchet is initialised with cryptographically-secure random data, and
|
|
||||||
advanced as follows:
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
\begin{align}
|
|
||||||
R_{i,0} &=
|
|
||||||
\begin{cases}
|
|
||||||
H_0\left(R_{2^24(n-1),0}\right) &\text{if }\exists n | i = 2^24n\\
|
|
||||||
R_{i-1,0} &\text{otherwise}
|
|
||||||
\end{cases}\\
|
|
||||||
R_{i,1} &=
|
|
||||||
\begin{cases}
|
|
||||||
H_1\left(R_{2^24(n-1),0}\right) &\text{if }\exists n | i = 2^24n\\
|
|
||||||
H_1\left(R_{2^16(m-1),1}\right) &\text{if }\exists m | i = 2^16m\\
|
|
||||||
R_{i-1,1} &\text{otherwise}
|
|
||||||
\end{cases}\\
|
|
||||||
R_{i,2} &=
|
|
||||||
\begin{cases}
|
|
||||||
H_2\left(R_{2^24(n-1),0}\right) &\text{if }\exists n | i = 2^24n\\
|
|
||||||
H_2\left(R_{2^16(m-1),1}\right) &\text{if }\exists m | i = 2^16m\\
|
|
||||||
H_2\left(R_{2^8(p-1),2}\right) &\text{if }\exists p | i = 2^8p\\
|
|
||||||
R_{i-1,2} &\text{otherwise}
|
|
||||||
\end{cases}\\
|
|
||||||
R_{i,3} &=
|
|
||||||
\begin{cases}
|
|
||||||
H_3\left(R_{2^24(n-1),0}\right) &\text{if }\exists n | i = 2^24n\\
|
|
||||||
H_3\left(R_{2^16(m-1),1}\right) &\text{if }\exists m | i = 2^16m\\
|
|
||||||
H_3\left(R_{2^8(p-1),2}\right) &\text{if }\exists p | i = 2^8p\\
|
|
||||||
H_3\left(R_{i-1,3}\right) &\text{otherwise}
|
|
||||||
\end{cases}
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
where :math:`H_0`, :math:`H_1`, :math:`H_2`, and :math:`H_3` are different hash
|
|
||||||
functions. In summary: every :math:`2^8` iterations, :math:`R_{i,3}` is
|
|
||||||
reseeded from :math:`R_{i,2}`. Every :math:`2^16` iterations, :math:`R_{i,2}`
|
|
||||||
and :math:`R_{i,3}` are reseeded from :math:`R_{i,1}`. Every :math:`2^24`
|
|
||||||
iterations, :math:`R_{i,1}`, :math:`R_{i,2}` and :math:`R_{i,3}` are reseeded
|
|
||||||
from :math:`R_{i,0}`.
|
|
||||||
|
|
||||||
The complete ratchet value, :math:`R_{i}`, is hashed to generate the keys used
|
|
||||||
to encrypt each message. This scheme allows the ratchet to be advanced an
|
|
||||||
arbitrary amount forwards while needing at most 1020 hash computations. A
|
|
||||||
client can decrypt chat history onwards from the earliest value of the ratchet
|
|
||||||
it is aware of, but cannot decrypt history from before that point without
|
|
||||||
reversing the hash function.
|
|
||||||
|
|
||||||
This allows a participant to share its ability to decrypt chat history with
|
|
||||||
another from a point in the conversation onwards by giving a copy of the
|
|
||||||
ratchet at that point in the conversation.
|
|
||||||
|
|
||||||
|
|
||||||
The Megolm protocol
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Session setup
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Each participant in a conversation generates their own Megolm session. A
|
|
||||||
session consists of three parts:
|
|
||||||
|
|
||||||
* a 32 bit counter, :math:`i`.
|
|
||||||
* an `Ed25519`_ keypair, :math:`K`.
|
|
||||||
* a ratchet, :math:`R_i`, which consists of four 256-bit values,
|
|
||||||
:math:`R_{i,j}` for :math:`j \in {0,1,2,3}`.
|
|
||||||
|
|
||||||
The counter :math:`i` is initialised to :math:`0`. A new Ed25519 keypair is
|
|
||||||
generated for :math:`K`. The ratchet is simply initialised with 1024 bits of
|
|
||||||
cryptographically-secure random data.
|
|
||||||
|
|
||||||
A single participant may use multiple sessions over the lifetime of a
|
|
||||||
conversation. The public part of :math:`K` is used as an identifier to
|
|
||||||
discriminate between sessions.
|
|
||||||
|
|
||||||
Sharing session data
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
To allow other participants in the conversation to decrypt messages, the
|
|
||||||
session data is formatted as described in `Session-sharing format`_. It is then
|
|
||||||
shared with other participants in the conversation via a secure peer-to-peer
|
|
||||||
channel (such as that provided by `Olm`_).
|
|
||||||
|
|
||||||
When the session data is received from other participants, the recipient first
|
|
||||||
checks that the signature matches the public key. They then store their own
|
|
||||||
copy of the counter, ratchet, and public key.
|
|
||||||
|
|
||||||
Message encryption
|
|
||||||
~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This version of Megolm uses AES-256_ in CBC_ mode with `PKCS#7`_ padding and
|
|
||||||
HMAC-SHA-256_ (truncated to 64 bits). The 256 bit AES key, 256 bit HMAC key,
|
|
||||||
and 128 bit AES IV are derived from the megolm ratchet :math:`R_i`:
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
AES\_KEY_{i}\;\parallel\;HMAC\_KEY_{i}\;\parallel\;AES\_IV_{i}
|
|
||||||
&= HKDF\left(0,\,R_{i},\text{"MEGOLM\_KEYS"},\,80\right) \\
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
where :math:`\parallel` represents string splitting, and
|
|
||||||
:math:`HKDF\left(salt,\,IKM,\,info,\,L\right)` refers to the `HMAC-based key
|
|
||||||
derivation function`_ using using `SHA-256`_ as the hash function
|
|
||||||
(`HKDF-SHA-256`_) with a salt value of :math:`salt`, input key material of
|
|
||||||
:math:`IKM`, context string :math:`info`, and output keying material length of
|
|
||||||
:math:`L` bytes.
|
|
||||||
|
|
||||||
The plain-text is encrypted with AES-256, using the key :math:`AES\_KEY_{i}`
|
|
||||||
and the IV :math:`AES\_IV_{i}` to give the cipher-text, :math:`X_{i}`.
|
|
||||||
|
|
||||||
The ratchet index :math:`i`, and the cipher-text :math:`X_{i}`, are then packed
|
|
||||||
into a message as described in `Message format`_. Then the entire message
|
|
||||||
(including the version bytes and all payload bytes) are passed through
|
|
||||||
HMAC-SHA-256. The first 8 bytes of the MAC are appended to the message.
|
|
||||||
|
|
||||||
Finally, the authenticated message is signed using the Ed25519 keypair; the 64
|
|
||||||
byte signature is appended to the message.
|
|
||||||
|
|
||||||
The complete signed message, together with the public part of :math:`K` (acting
|
|
||||||
as a session identifier), can then be sent over an insecure channel. The
|
|
||||||
message can then be authenticated and decrypted only by recipients who have
|
|
||||||
received the session data.
|
|
||||||
|
|
||||||
Advancing the ratchet
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
After each message is encrypted, the ratchet is advanced. This is done as
|
|
||||||
described in `The Megolm ratchet algorithm`_, using the following definitions:
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
\begin{align}
|
|
||||||
H_0(A) &\equiv HMAC(A,\text{"\textbackslash x00"}) \\
|
|
||||||
H_1(A) &\equiv HMAC(A,\text{"\textbackslash x01"}) \\
|
|
||||||
H_2(A) &\equiv HMAC(A,\text{"\textbackslash x02"}) \\
|
|
||||||
H_3(A) &\equiv HMAC(A,\text{"\textbackslash x03"}) \\
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
where :math:`HMAC(A, T)` is the HMAC-SHA-256_ of ``T``, using ``A`` as the
|
|
||||||
key.
|
|
||||||
|
|
||||||
For outbound sessions, the updated ratchet and counter are stored in the
|
|
||||||
session.
|
|
||||||
|
|
||||||
In order to maintain the ability to decrypt conversation history, inbound
|
|
||||||
sessions should store a copy of their earliest known ratchet value (unless they
|
|
||||||
explicitly want to drop the ability to decrypt that history - see `Partial
|
|
||||||
Forward Secrecy`_\ ). They may also choose to cache calculated ratchet values,
|
|
||||||
but the decision of which ratchet states to cache is left to the application.
|
|
||||||
|
|
||||||
Data exchange formats
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
Session-sharing format
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The Megolm key-sharing format is as follows:
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
+---+----+--------+--------+--------+--------+------+-----------+
|
|
||||||
| V | i | R(i,0) | R(i,1) | R(i,2) | R(i,3) | Kpub | Signature |
|
|
||||||
+---+----+--------+--------+--------+--------+------+-----------+
|
|
||||||
0 1 5 37 69 101 133 165 229 bytes
|
|
||||||
|
|
||||||
The version byte, ``V``, is ``"\x02"``.
|
|
||||||
|
|
||||||
This is followed by the ratchet index, :math:`i`, which is encoded as a
|
|
||||||
big-endian 32-bit integer; the ratchet values :math:`R_{i,j}`; and the public
|
|
||||||
part of the Ed25519 keypair :math:`K`.
|
|
||||||
|
|
||||||
The data is then signed using the Ed25519 keypair, and the 64-byte signature is
|
|
||||||
appended.
|
|
||||||
|
|
||||||
Message format
|
|
||||||
~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Megolm messages consist of a one byte version, followed by a variable length
|
|
||||||
payload, a fixed length message authentication code, and a fixed length
|
|
||||||
signature.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
+---+------------------------------------+-----------+------------------+
|
|
||||||
| V | Payload Bytes | MAC Bytes | Signature Bytes |
|
|
||||||
+---+------------------------------------+-----------+------------------+
|
|
||||||
0 1 N N+8 N+72 bytes
|
|
||||||
|
|
||||||
The version byte, ``V``, is ``"\x03"``.
|
|
||||||
|
|
||||||
The payload uses a format based on the `Protocol Buffers encoding`_. It
|
|
||||||
consists of the following key-value pairs:
|
|
||||||
|
|
||||||
============= ===== ======== ================================================
|
|
||||||
Name Tag Type Meaning
|
|
||||||
============= ===== ======== ================================================
|
|
||||||
Message-Index 0x08 Integer The index of the ratchet, :math:`i`
|
|
||||||
Cipher-Text 0x12 String The cipher-text, :math:`X_{i}`, of the message
|
|
||||||
============= ===== ======== ================================================
|
|
||||||
|
|
||||||
Within the payload, integers are encoded using a variable length encoding. Each
|
|
||||||
integer is encoded as a sequence of bytes with the high bit set followed by a
|
|
||||||
byte with the high bit clear. The seven low bits of each byte store the bits of
|
|
||||||
the integer. The least significant bits are stored in the first byte.
|
|
||||||
|
|
||||||
Strings are encoded as a variable-length integer followed by the string itself.
|
|
||||||
|
|
||||||
Each key-value pair is encoded as a variable-length integer giving the tag,
|
|
||||||
followed by a string or variable-length integer giving the value.
|
|
||||||
|
|
||||||
The payload is followed by the MAC. The length of the MAC is determined by the
|
|
||||||
authenticated encryption algorithm being used (8 bytes in this version of the
|
|
||||||
protocol). The MAC protects all of the bytes preceding the MAC.
|
|
||||||
|
|
||||||
The length of the signature is determined by the signing algorithm being used
|
|
||||||
(64 bytes in this version of the protocol). The signature covers all of the
|
|
||||||
bytes preceding the signature.
|
|
||||||
|
|
||||||
Limitations
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Message Replays
|
|
||||||
---------------
|
|
||||||
|
|
||||||
A message can be decrypted successfully multiple times. This means that an
|
|
||||||
attacker can re-send a copy of an old message, and the recipient will treat it
|
|
||||||
as a new message.
|
|
||||||
|
|
||||||
To mitigate this it is recommended that applications track the ratchet indices
|
|
||||||
they have received and that they reject messages with a ratchet index that
|
|
||||||
they have already decrypted.
|
|
||||||
|
|
||||||
Lack of Transcript Consistency
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
In a group conversation, there is no guarantee that all recipients have
|
|
||||||
received the same messages. For example, if Alice is in a conversation with Bob
|
|
||||||
and Charlie, she could send different messages to Bob and Charlie, or could
|
|
||||||
send some messages to Bob but not Charlie, or vice versa.
|
|
||||||
|
|
||||||
Solving this is, in general, a hard problem, particularly in a protocol which
|
|
||||||
does not guarantee in-order message delivery. For now it remains the subject of
|
|
||||||
future research.
|
|
||||||
|
|
||||||
Lack of Backward Secrecy
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Once the key to a Megolm session is compromised, the attacker can decrypt any
|
|
||||||
future messages sent via that session.
|
|
||||||
|
|
||||||
In order to mitigate this, the application should ensure that Megolm sessions
|
|
||||||
are not used indefinitely. Instead it should periodically start a new session,
|
|
||||||
with new keys shared over a secure channel.
|
|
||||||
|
|
||||||
.. TODO: Can we recommend sensible lifetimes for Megolm sessions? Probably
|
|
||||||
depends how paranoid we're feeling, but some guidelines might be useful.
|
|
||||||
|
|
||||||
Partial Forward Secrecy
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Each recipient maintains a record of the ratchet value which allows them to
|
|
||||||
decrypt any messages sent in the session after the corresponding point in the
|
|
||||||
conversation. If this value is compromised, an attacker can similarly decrypt
|
|
||||||
those past messages.
|
|
||||||
|
|
||||||
To mitigate this issue, the application should offer the user the option to
|
|
||||||
discard historical conversations, by winding forward any stored ratchet values,
|
|
||||||
or discarding sessions altogether.
|
|
||||||
|
|
||||||
Dependency on secure channel for key exchange
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The design of the Megolm ratchet relies on the availability of a secure
|
|
||||||
peer-to-peer channel for the exchange of session keys. Any vulnerabilities in
|
|
||||||
the underlying channel are likely to be amplified when applied to Megolm
|
|
||||||
session setup.
|
|
||||||
|
|
||||||
For example, if the peer-to-peer channel is vulnerable to an unknown key-share
|
|
||||||
attack, the entire Megolm session become similarly vulnerable. For example:
|
|
||||||
Alice starts a group chat with Eve, and shares the session keys with Eve. Eve
|
|
||||||
uses the unknown key-share attack to forward the session keys to Bob, who
|
|
||||||
believes Alice is starting the session with him. Eve then forwards messages
|
|
||||||
from the Megolm session to Bob, who again believes they are coming from
|
|
||||||
Alice. Provided the peer-to-peer channel is not vulnerable to this attack, Bob
|
|
||||||
will realise that the key-sharing message was forwarded by Eve, and can treat
|
|
||||||
the Megolm session as a forgery.
|
|
||||||
|
|
||||||
A second example: if the peer-to-peer channel is vulnerable to a replay
|
|
||||||
attack, this can be extended to entire Megolm sessions.
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
The Megolm specification (this document) is licensed under the `Apache License,
|
|
||||||
Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_.
|
|
||||||
|
|
||||||
|
|
||||||
.. _`Ed25519`: http://ed25519.cr.yp.to/
|
|
||||||
.. _`HMAC-based key derivation function`: https://tools.ietf.org/html/rfc5869
|
|
||||||
.. _`HKDF-SHA-256`: https://tools.ietf.org/html/rfc5869
|
|
||||||
.. _`HMAC-SHA-256`: https://tools.ietf.org/html/rfc2104
|
|
||||||
.. _`SHA-256`: https://tools.ietf.org/html/rfc6234
|
|
||||||
.. _`AES-256`: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
|
||||||
.. _`CBC`: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
|
|
||||||
.. _`PKCS#7`: https://tools.ietf.org/html/rfc2315
|
|
||||||
.. _`Olm`: ./olm.html
|
|
||||||
.. _`Protocol Buffers encoding`: https://developers.google.com/protocol-buffers/docs/encoding
|
|
328
docs/olm.md
Normal file
328
docs/olm.md
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
# Olm: A Cryptographic Ratchet
|
||||||
|
|
||||||
|
An implementation of the double cryptographic ratchet described by
|
||||||
|
https://whispersystems.org/docs/specifications/doubleratchet/.
|
||||||
|
|
||||||
|
## Notation
|
||||||
|
|
||||||
|
This document uses $`\parallel`$ to represent string concatenation. When
|
||||||
|
$`\parallel`$ appears on the right hand side of an $`=`$ it means that
|
||||||
|
the inputs are concatenated. When $`\parallel`$ appears on the left hand
|
||||||
|
side of an $`=`$ it means that the output is split.
|
||||||
|
|
||||||
|
When this document uses $`ECDH\left(K_A,\,K_B\right)`$ it means that each
|
||||||
|
party computes a Diffie-Hellman agreement using their private key and the
|
||||||
|
remote party's public key.
|
||||||
|
So party $`A`$ computes $`ECDH\left(K_B^{public},\,K_A^{private}\right)`$
|
||||||
|
and party $`B`$ computes $`ECDH\left(K_A^{public},\,K_B^{private}\right)`$.
|
||||||
|
|
||||||
|
Where this document uses $`HKDF\left(salt,\,IKM,\,info,\,L\right)`$ it
|
||||||
|
refers to the [HMAC-based key derivation function][] with a salt value of
|
||||||
|
$`salt`$, input key material of $`IKM`$, context string $`info`$,
|
||||||
|
and output keying material length of $`L`$ bytes.
|
||||||
|
|
||||||
|
## The Olm Algorithm
|
||||||
|
|
||||||
|
### Initial setup
|
||||||
|
|
||||||
|
The setup takes four [Curve25519][] inputs: Identity keys for Alice and Bob,
|
||||||
|
$`I_A`$ and $`I_B`$, and one-time keys for Alice and Bob,
|
||||||
|
$`E_A`$ and $`E_B`$. A shared secret, $`S`$, is generated using
|
||||||
|
[Triple Diffie-Hellman][]. The initial 256 bit root key, $`R_0`$, and 256
|
||||||
|
bit chain key, $`C_{0,0}`$, are derived from the shared secret using an
|
||||||
|
HMAC-based Key Derivation Function using [SHA-256][] as the hash function
|
||||||
|
([HKDF-SHA-256][]) with default salt and ``"OLM_ROOT"`` as the info.
|
||||||
|
|
||||||
|
```math
|
||||||
|
\begin{aligned}
|
||||||
|
S&=ECDH\left(I_A,\,E_B\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\;
|
||||||
|
\parallel\;ECDH\left(E_A,\,E_B\right)\\
|
||||||
|
R_0\;\parallel\;C_{0,0}&=
|
||||||
|
HKDF\left(0,\,S,\,\text{"OLM\_ROOT"},\,64\right)
|
||||||
|
\end{aligned}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advancing the root key
|
||||||
|
|
||||||
|
Advancing a root key takes the previous root key, $`R_{i-1}`$, and two
|
||||||
|
Curve25519 inputs: the previous ratchet key, $`T_{i-1}`$, and the current
|
||||||
|
ratchet key $`T_i`$. The even ratchet keys are generated by Alice.
|
||||||
|
The odd ratchet keys are generated by Bob. A shared secret is generated
|
||||||
|
using Diffie-Hellman on the ratchet keys. The next root key, $`R_i`$, and
|
||||||
|
chain key, $`C_{i,0}`$, are derived from the shared secret using
|
||||||
|
[HKDF-SHA-256][] using $`R_{i-1}`$ as the salt and ``"OLM_RATCHET"`` as the
|
||||||
|
info.
|
||||||
|
|
||||||
|
```math
|
||||||
|
\begin{aligned}
|
||||||
|
R_i\;\parallel\;C_{i,0}&=HKDF\left(
|
||||||
|
R_{i-1},\,
|
||||||
|
ECDH\left(T_{i-1},\,T_i\right),\,
|
||||||
|
\text{"OLM\_RATCHET"},\,
|
||||||
|
64
|
||||||
|
\right)
|
||||||
|
\end{aligned}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advancing the chain key
|
||||||
|
|
||||||
|
Advancing a chain key takes the previous chain key, $`C_{i,j-1}`$. The next
|
||||||
|
chain key, $`C_{i,j}`$, is the [HMAC-SHA-256][] of ``"\x02"`` using the
|
||||||
|
previous chain key as the key.
|
||||||
|
|
||||||
|
```math
|
||||||
|
\begin{aligned}
|
||||||
|
C_{i,j}&=HMAC\left(C_{i,j-1},\,\text{"\x02"}\right)
|
||||||
|
\end{aligned}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Creating a message key
|
||||||
|
|
||||||
|
Creating a message key takes the current chain key, $`C_{i,j}`$. The
|
||||||
|
message key, $`M_{i,j}`$, is the [HMAC-SHA-256][] of ``"\x01"`` using the
|
||||||
|
current chain key as the key. The message keys where $`i`$ is even are used
|
||||||
|
by Alice to encrypt messages. The message keys where $`i`$ is odd are used
|
||||||
|
by Bob to encrypt messages.
|
||||||
|
|
||||||
|
```math
|
||||||
|
\begin{aligned}
|
||||||
|
M_{i,j}&=HMAC\left(C_{i,j},\,\text{"\x01"}\right)
|
||||||
|
\end{aligned}
|
||||||
|
```
|
||||||
|
|
||||||
|
## The Olm Protocol
|
||||||
|
|
||||||
|
### Creating an outbound session
|
||||||
|
|
||||||
|
Bob publishes the public parts of his identity key, $`I_B`$, and some
|
||||||
|
single-use one-time keys $`E_B`$.
|
||||||
|
|
||||||
|
Alice downloads Bob's identity key, $`I_B`$, and a one-time key,
|
||||||
|
$`E_B`$. She generates a new single-use key, $`E_A`$, and computes a
|
||||||
|
root key, $`R_0`$, and a chain key $`C_{0,0}`$. She also generates a
|
||||||
|
new ratchet key $`T_0`$.
|
||||||
|
|
||||||
|
### Sending the first pre-key messages
|
||||||
|
|
||||||
|
Alice computes a message key, $`M_{0,j}`$, and a new chain key,
|
||||||
|
$`C_{0,j+1}`$, using the current chain key. She replaces the current chain
|
||||||
|
key with the new one.
|
||||||
|
|
||||||
|
Alice encrypts her plain-text with the message key, $`M_{0,j}`$, using an
|
||||||
|
authenticated encryption scheme (see below) to get a cipher-text,
|
||||||
|
$`X_{0,j}`$.
|
||||||
|
|
||||||
|
She then sends the following to Bob:
|
||||||
|
* The public part of her identity key, $`I_A`$
|
||||||
|
* The public part of her single-use key, $`E_A`$
|
||||||
|
* The public part of Bob's single-use key, $`E_B`$
|
||||||
|
* The current chain index, $`j`$
|
||||||
|
* The public part of her ratchet key, $`T_0`$
|
||||||
|
* The cipher-text, $`X_{0,j}`$
|
||||||
|
|
||||||
|
Alice will continue to send pre-key messages until she receives a message from
|
||||||
|
Bob.
|
||||||
|
|
||||||
|
### Creating an inbound session from a pre-key message
|
||||||
|
|
||||||
|
Bob receives a pre-key message as above.
|
||||||
|
|
||||||
|
Bob looks up the private part of his single-use key, $`E_B`$. He can now
|
||||||
|
compute the root key, $`R_0`$, and the chain key, $`C_{0,0}`$, from
|
||||||
|
$`I_A`$, $`E_A`$, $`I_B`$, and $`E_B`$.
|
||||||
|
|
||||||
|
Bob then advances the chain key $`j`$ times, to compute the chain key used
|
||||||
|
by the message, $`C_{0,j}`$. He now creates the
|
||||||
|
message key, $`M_{0,j}`$, and attempts to decrypt the cipher-text,
|
||||||
|
$`X_{0,j}`$. If the cipher-text's authentication is correct then Bob can
|
||||||
|
discard the private part of his single-use one-time key, $`E_B`$.
|
||||||
|
|
||||||
|
Bob stores Alice's initial ratchet key, $`T_0`$, until he wants to
|
||||||
|
send a message.
|
||||||
|
|
||||||
|
### Sending normal messages
|
||||||
|
|
||||||
|
Once a message has been received from the other side, a session is considered
|
||||||
|
established, and a more compact form is used.
|
||||||
|
|
||||||
|
To send a message, the user checks if they have a sender chain key,
|
||||||
|
$`C_{i,j}`$. Alice uses chain keys where $`i`$ is even. Bob uses chain
|
||||||
|
keys where $`i`$ is odd. If the chain key doesn't exist then a new ratchet
|
||||||
|
key $`T_i`$ is generated and a new root key $`R_i`$ and chain key
|
||||||
|
$`C_{i,0}`$ are computed using $`R_{i-1}`$, $`T_{i-1}`$ and
|
||||||
|
$`T_i`$.
|
||||||
|
|
||||||
|
A message key,
|
||||||
|
$`M_{i,j}`$ is computed from the current chain key, $`C_{i,j}`$, and
|
||||||
|
the chain key is replaced with the next chain key, $`C_{i,j+1}`$. The
|
||||||
|
plain-text is encrypted with $`M_{i,j}`$, using an authenticated encryption
|
||||||
|
scheme (see below) to get a cipher-text, $`X_{i,j}`$.
|
||||||
|
|
||||||
|
The user then sends the following to the recipient:
|
||||||
|
* The current chain index, $`j`$
|
||||||
|
* The public part of the current ratchet key, $`T_i`$
|
||||||
|
* The cipher-text, $`X_{i,j}`$
|
||||||
|
|
||||||
|
### Receiving messages
|
||||||
|
|
||||||
|
The user receives a message as above with the sender's current chain index, $`j`$,
|
||||||
|
the sender's ratchet key, $`T_i`$, and the cipher-text, $`X_{i,j}`$.
|
||||||
|
|
||||||
|
The user checks if they have a receiver chain with the correct
|
||||||
|
$`i`$ by comparing the ratchet key, $`T_i`$. If the chain doesn't exist
|
||||||
|
then they compute a new root key, $`R_i`$, and a new receiver chain, with
|
||||||
|
chain key $`C_{i,0}`$, using $`R_{i-1}`$, $`T_{i-1}`$ and
|
||||||
|
$`T_i`$.
|
||||||
|
|
||||||
|
If the $`j`$ of the message is less than
|
||||||
|
the current chain index on the receiver then the message may only be decrypted
|
||||||
|
if the receiver has stored a copy of the message key $`M_{i,j}`$. Otherwise
|
||||||
|
the receiver computes the chain key, $`C_{i,j}`$. The receiver computes the
|
||||||
|
message key, $`M_{i,j}`$, from the chain key and attempts to decrypt the
|
||||||
|
cipher-text, $`X_{i,j}`$.
|
||||||
|
|
||||||
|
If the decryption succeeds the receiver updates the chain key for $`T_i`$
|
||||||
|
with $`C_{i,j+1}`$ and stores the message keys that were skipped in the
|
||||||
|
process so that they can decode out of order messages. If the receiver created
|
||||||
|
a new receiver chain then they discard their current sender chain so that
|
||||||
|
they will create a new chain when they next send a message.
|
||||||
|
|
||||||
|
## The Olm Message Format
|
||||||
|
|
||||||
|
Olm uses two types of messages. The underlying transport protocol must provide
|
||||||
|
a means for recipients to distinguish between them.
|
||||||
|
|
||||||
|
### Normal Messages
|
||||||
|
|
||||||
|
Olm messages start with a one byte version followed by a variable length
|
||||||
|
payload followed by a fixed length message authentication code.
|
||||||
|
|
||||||
|
```
|
||||||
|
+--------------+------------------------------------+-----------+
|
||||||
|
| Version Byte | Payload Bytes | MAC Bytes |
|
||||||
|
+--------------+------------------------------------+-----------+
|
||||||
|
```
|
||||||
|
|
||||||
|
The version byte is ``"\x03"``.
|
||||||
|
|
||||||
|
The payload consists of key-value pairs where the keys are integers and the
|
||||||
|
values are integers and strings. The keys are encoded as a variable length
|
||||||
|
integer tag where the 3 lowest bits indicates the type of the value:
|
||||||
|
0 for integers, 2 for strings. If the value is an integer then the tag is
|
||||||
|
followed by the value encoded as a variable length integer. If the value is
|
||||||
|
a string then the tag is followed by the length of the string encoded as
|
||||||
|
a variable length integer followed by the string itself.
|
||||||
|
|
||||||
|
Olm uses a variable length encoding for integers. Each integer is encoded as a
|
||||||
|
sequence of bytes with the high bit set followed by a byte with the high bit
|
||||||
|
clear. The seven low bits of each byte store the bits of the integer. The least
|
||||||
|
significant bits are stored in the first byte.
|
||||||
|
|
||||||
|
**Name**|**Tag**|**Type**|**Meaning**
|
||||||
|
:-----:|:-----:|:-----:|:-----:
|
||||||
|
Ratchet-Key|0x0A|String|The public part of the ratchet key, Ti, of the message
|
||||||
|
Chain-Index|0x10|Integer|The chain index, j, of the message
|
||||||
|
Cipher-Text|0x22|String|The cipher-text, Xi, j, of the message
|
||||||
|
|
||||||
|
The length of the MAC is determined by the authenticated encryption algorithm
|
||||||
|
being used. (Olm version 1 uses [HMAC-SHA-256][], truncated to 8 bytes). The
|
||||||
|
MAC protects all of the bytes preceding the MAC.
|
||||||
|
|
||||||
|
### Pre-Key Messages
|
||||||
|
|
||||||
|
Olm pre-key messages start with a one byte version followed by a variable
|
||||||
|
length payload.
|
||||||
|
|
||||||
|
```
|
||||||
|
+--------------+------------------------------------+
|
||||||
|
| Version Byte | Payload Bytes |
|
||||||
|
+--------------+------------------------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
The version byte is ``"\x03"``.
|
||||||
|
|
||||||
|
The payload uses the same key-value format as for normal messages.
|
||||||
|
|
||||||
|
**Name**|**Tag**|**Type**|**Meaning**
|
||||||
|
:-----:|:-----:|:-----:|:-----:
|
||||||
|
One-Time-Key|0x0A|String|The public part of Bob's single-use key, Eb.
|
||||||
|
Base-Key|0x12|String|The public part of Alice's single-use key, Ea.
|
||||||
|
Identity-Key|0x1A|String|The public part of Alice's identity key, Ia.
|
||||||
|
Message|0x22|String|An embedded Olm message with its own version and MAC.
|
||||||
|
|
||||||
|
## Olm Authenticated Encryption
|
||||||
|
|
||||||
|
### Version 1
|
||||||
|
|
||||||
|
Version 1 of Olm uses [AES-256][] in [CBC][] mode with [PKCS#7][] padding for
|
||||||
|
encryption and [HMAC-SHA-256][] (truncated to 64 bits) for authentication. The
|
||||||
|
256 bit AES key, 256 bit HMAC key, and 128 bit AES IV are derived from the
|
||||||
|
message key using [HKDF-SHA-256][] using the default salt and an info of
|
||||||
|
``"OLM_KEYS"``.
|
||||||
|
|
||||||
|
```math
|
||||||
|
\begin{aligned}
|
||||||
|
AES\_KEY_{i,j}\;\parallel\;HMAC\_KEY_{i,j}\;\parallel\;AES\_IV_{i,j}
|
||||||
|
&= HKDF\left(0,\,M_{i,j},\text{"OLM\_KEYS"},\,80\right) \\
|
||||||
|
\end{aligned}
|
||||||
|
```
|
||||||
|
|
||||||
|
The plain-text is encrypted with AES-256, using the key $`AES\_KEY_{i,j}`$
|
||||||
|
and the IV $`AES\_IV_{i,j}`$ to give the cipher-text, $`X_{i,j}`$.
|
||||||
|
|
||||||
|
Then the entire message (including the Version Byte and all Payload Bytes) are
|
||||||
|
passed through [HMAC-SHA-256][]. The first 8 bytes of the MAC are appended to the message.
|
||||||
|
|
||||||
|
## Message authentication concerns
|
||||||
|
|
||||||
|
To avoid unknown key-share attacks, the application must include identifying
|
||||||
|
data for the sending and receiving user in the plain-text of (at least) the
|
||||||
|
pre-key messages. Such data could be a user ID, a telephone number;
|
||||||
|
alternatively it could be the public part of a keypair which the relevant user
|
||||||
|
has proven ownership of.
|
||||||
|
|
||||||
|
### Example attacks
|
||||||
|
|
||||||
|
1. Alice publishes her public [Curve25519][] identity key, $`I_A`$. Eve
|
||||||
|
publishes the same identity key, claiming it as her own. Bob downloads
|
||||||
|
Eve's keys, and associates $`I_A`$ with Eve. Alice sends a message to
|
||||||
|
Bob; Eve intercepts it before forwarding it to Bob. Bob believes the
|
||||||
|
message came from Eve rather than Alice.
|
||||||
|
|
||||||
|
This is prevented if Alice includes her user ID in the plain-text of the
|
||||||
|
pre-key message, so that Bob can see that the message was sent by Alice
|
||||||
|
originally.
|
||||||
|
|
||||||
|
2. Bob publishes his public [Curve25519][] identity key, $`I_B`$. Eve
|
||||||
|
publishes the same identity key, claiming it as her own. Alice downloads
|
||||||
|
Eve's keys, and associates $`I_B`$ with Eve. Alice sends a message to
|
||||||
|
Eve; Eve cannot decrypt it, but forwards it to Bob. Bob believes the
|
||||||
|
Alice sent the message to him, wheras Alice intended it to go to Eve.
|
||||||
|
|
||||||
|
This is prevented by Alice including the user ID of the intended recpient
|
||||||
|
(Eve) in the plain-text of the pre-key message. Bob can now tell that the
|
||||||
|
message was meant for Eve rather than him.
|
||||||
|
|
||||||
|
## IPR
|
||||||
|
|
||||||
|
The Olm specification (this document) is hereby placed in the public domain.
|
||||||
|
|
||||||
|
## Feedback
|
||||||
|
|
||||||
|
Can be sent to olm at matrix.org.
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
The ratchet that Olm implements was designed by Trevor Perrin and Moxie
|
||||||
|
Marlinspike - details at https://whispersystems.org/docs/specifications/doubleratchet/. Olm is
|
||||||
|
an entirely new implementation written by the Matrix.org team.
|
||||||
|
|
||||||
|
[Curve25519]: http://cr.yp.to/ecdh.html
|
||||||
|
[Triple Diffie-Hellman]: https://whispersystems.org/blog/simplifying-otr-deniability/
|
||||||
|
[HMAC-based key derivation function]: https://tools.ietf.org/html/rfc5869
|
||||||
|
[HKDF-SHA-256]: https://tools.ietf.org/html/rfc5869
|
||||||
|
[HMAC-SHA-256]: https://tools.ietf.org/html/rfc2104
|
||||||
|
[SHA-256]: https://tools.ietf.org/html/rfc6234
|
||||||
|
[AES-256]: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
||||||
|
[CBC]: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
|
||||||
|
[PKCS#7]: https://tools.ietf.org/html/rfc2315
|
358
docs/olm.rst
358
docs/olm.rst
|
@ -1,358 +0,0 @@
|
||||||
Olm: A Cryptographic Ratchet
|
|
||||||
============================
|
|
||||||
|
|
||||||
An implementation of the double cryptographic ratchet described by
|
|
||||||
https://whispersystems.org/docs/specifications/doubleratchet/.
|
|
||||||
|
|
||||||
|
|
||||||
Notation
|
|
||||||
--------
|
|
||||||
|
|
||||||
This document uses :math:`\parallel` to represent string concatenation. When
|
|
||||||
:math:`\parallel` appears on the right hand side of an :math:`=` it means that
|
|
||||||
the inputs are concatenated. When :math:`\parallel` appears on the left hand
|
|
||||||
side of an :math:`=` it means that the output is split.
|
|
||||||
|
|
||||||
When this document uses :math:`ECDH\left(K_A,\,K_B\right)` it means that each
|
|
||||||
party computes a Diffie-Hellman agreement using their private key and the
|
|
||||||
remote party's public key.
|
|
||||||
So party :math:`A` computes :math:`ECDH\left(K_B_public,\,K_A_private\right)`
|
|
||||||
and party :math:`B` computes :math:`ECDH\left(K_A_public,\,K_B_private\right)`.
|
|
||||||
|
|
||||||
Where this document uses :math:`HKDF\left(salt,\,IKM,\,info,\,L\right)` it
|
|
||||||
refers to the `HMAC-based key derivation function`_ with a salt value of
|
|
||||||
:math:`salt`, input key material of :math:`IKM`, context string :math:`info`,
|
|
||||||
and output keying material length of :math:`L` bytes.
|
|
||||||
|
|
||||||
The Olm Algorithm
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Initial setup
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The setup takes four Curve25519_ inputs: Identity keys for Alice and Bob,
|
|
||||||
:math:`I_A` and :math:`I_B`, and one-time keys for Alice and Bob,
|
|
||||||
:math:`E_A` and :math:`E_B`. A shared secret, :math:`S`, is generated using
|
|
||||||
`Triple Diffie-Hellman`_. The initial 256 bit root key, :math:`R_0`, and 256
|
|
||||||
bit chain key, :math:`C_{0,0}`, are derived from the shared secret using an
|
|
||||||
HMAC-based Key Derivation Function using SHA-256_ as the hash function
|
|
||||||
(HKDF-SHA-256_) with default salt and ``"OLM_ROOT"`` as the info.
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
\begin{align}
|
|
||||||
S&=ECDH\left(I_A,\,E_B\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\;
|
|
||||||
\parallel\;ECDH\left(E_A,\,E_B\right)\\
|
|
||||||
R_0\;\parallel\;C_{0,0}&=
|
|
||||||
HKDF\left(0,\,S,\,\text{"OLM\_ROOT"},\,64\right)
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
Advancing the root key
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Advancing a root key takes the previous root key, :math:`R_{i-1}`, and two
|
|
||||||
Curve25519 inputs: the previous ratchet key, :math:`T_{i-1}`, and the current
|
|
||||||
ratchet key :math:`T_i`. The even ratchet keys are generated by Alice.
|
|
||||||
The odd ratchet keys are generated by Bob. A shared secret is generated
|
|
||||||
using Diffie-Hellman on the ratchet keys. The next root key, :math:`R_i`, and
|
|
||||||
chain key, :math:`C_{i,0}`, are derived from the shared secret using
|
|
||||||
HKDF-SHA-256_ using :math:`R_{i-1}` as the salt and ``"OLM_RATCHET"`` as the
|
|
||||||
info.
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
\begin{align}
|
|
||||||
R_i\;\parallel\;C_{i,0}&=HKDF\left(
|
|
||||||
R_{i-1},\,
|
|
||||||
ECDH\left(T_{i-1},\,T_i\right),\,
|
|
||||||
\text{"OLM\_RATCHET"},\,
|
|
||||||
64
|
|
||||||
\right)
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
|
|
||||||
Advancing the chain key
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Advancing a chain key takes the previous chain key, :math:`C_{i,j-1}`. The next
|
|
||||||
chain key, :math:`C_{i,j}`, is the HMAC-SHA-256_ of ``"\x02"`` using the
|
|
||||||
previous chain key as the key.
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
\begin{align}
|
|
||||||
C_{i,j}&=HMAC\left(C_{i,j-1},\,\text{"\textbackslash x02"}\right)
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
Creating a message key
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Creating a message key takes the current chain key, :math:`C_{i,j}`. The
|
|
||||||
message key, :math:`M_{i,j}`, is the HMAC-SHA-256_ of ``"\x01"`` using the
|
|
||||||
current chain key as the key. The message keys where :math:`i` is even are used
|
|
||||||
by Alice to encrypt messages. The message keys where :math:`i` is odd are used
|
|
||||||
by Bob to encrypt messages.
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
\begin{align}
|
|
||||||
M_{i,j}&=HMAC\left(C_{i,j},\,\text{"\textbackslash x01"}\right)
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
|
|
||||||
The Olm Protocol
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Creating an outbound session
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Bob publishes the public parts of his identity key, :math:`I_B`, and some
|
|
||||||
single-use one-time keys :math:`E_B`.
|
|
||||||
|
|
||||||
Alice downloads Bob's identity key, :math:`I_B`, and a one-time key,
|
|
||||||
:math:`E_B`. She generates a new single-use key, :math:`E_A`, and computes a
|
|
||||||
root key, :math:`R_0`, and a chain key :math:`C_{0,0}`. She also generates a
|
|
||||||
new ratchet key :math:`T_0`.
|
|
||||||
|
|
||||||
Sending the first pre-key messages
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Alice computes a message key, :math:`M_{0,j}`, and a new chain key,
|
|
||||||
:math:`C_{0,j+1}`, using the current chain key. She replaces the current chain
|
|
||||||
key with the new one.
|
|
||||||
|
|
||||||
Alice encrypts her plain-text with the message key, :math:`M_{0,j}`, using an
|
|
||||||
authenticated encryption scheme (see below) to get a cipher-text,
|
|
||||||
:math:`X_{0,j}`.
|
|
||||||
|
|
||||||
She then sends the following to Bob:
|
|
||||||
* The public part of her identity key, :math:`I_A`
|
|
||||||
* The public part of her single-use key, :math:`E_A`
|
|
||||||
* The public part of Bob's single-use key, :math:`E_B`
|
|
||||||
* The current chain index, :math:`j`
|
|
||||||
* The public part of her ratchet key, :math:`T_0`
|
|
||||||
* The cipher-text, :math:`X_{0,j}`
|
|
||||||
|
|
||||||
Alice will continue to send pre-key messages until she receives a message from
|
|
||||||
Bob.
|
|
||||||
|
|
||||||
Creating an inbound session from a pre-key message
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Bob receives a pre-key message as above.
|
|
||||||
|
|
||||||
Bob looks up the private part of his single-use key, :math:`E_B`. He can now
|
|
||||||
compute the root key, :math:`R_0`, and the chain key, :math:`C_{0,0}`, from
|
|
||||||
:math:`I_A`, :math:`E_A`, :math:`I_B`, and :math:`E_B`.
|
|
||||||
|
|
||||||
Bob then advances the chain key :math:`j` times, to compute the chain key used
|
|
||||||
by the message, :math:`C_{0,j}`. He now creates the
|
|
||||||
message key, :math:`M_{0,j}`, and attempts to decrypt the cipher-text,
|
|
||||||
:math:`X_{0,j}`. If the cipher-text's authentication is correct then Bob can
|
|
||||||
discard the private part of his single-use one-time key, :math:`E_B`.
|
|
||||||
|
|
||||||
Bob stores Alice's initial ratchet key, :math:`T_0`, until he wants to
|
|
||||||
send a message.
|
|
||||||
|
|
||||||
Sending normal messages
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Once a message has been received from the other side, a session is considered
|
|
||||||
established, and a more compact form is used.
|
|
||||||
|
|
||||||
To send a message, the user checks if they have a sender chain key,
|
|
||||||
:math:`C_{i,j}`. Alice uses chain keys where :math:`i` is even. Bob uses chain
|
|
||||||
keys where :math:`i` is odd. If the chain key doesn't exist then a new ratchet
|
|
||||||
key :math:`T_i` is generated and a new root key :math:`R_i` and chain key
|
|
||||||
:math:`C_{i,0}` are computed using :math:`R_{i-1}`, :math:`T_{i-1}` and
|
|
||||||
:math:`T_i`.
|
|
||||||
|
|
||||||
A message key,
|
|
||||||
:math:`M_{i,j}` is computed from the current chain key, :math:`C_{i,j}`, and
|
|
||||||
the chain key is replaced with the next chain key, :math:`C_{i,j+1}`. The
|
|
||||||
plain-text is encrypted with :math:`M_{i,j}`, using an authenticated encryption
|
|
||||||
scheme (see below) to get a cipher-text, :math:`X_{i,j}`.
|
|
||||||
|
|
||||||
The user then sends the following to the recipient:
|
|
||||||
* The current chain index, :math:`j`
|
|
||||||
* The public part of the current ratchet key, :math:`T_i`
|
|
||||||
* The cipher-text, :math:`X_{i,j}`
|
|
||||||
|
|
||||||
Receiving messages
|
|
||||||
~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The user receives a message as above with the sender's current chain index, :math:`j`,
|
|
||||||
the sender's ratchet key, :math:`T_i`, and the cipher-text, :math:`X_{i,j}`.
|
|
||||||
|
|
||||||
The user checks if they have a receiver chain with the correct
|
|
||||||
:math:`i` by comparing the ratchet key, :math:`T_i`. If the chain doesn't exist
|
|
||||||
then they compute a new root key, :math:`R_i`, and a new receiver chain, with
|
|
||||||
chain key :math:`C_{i,0}`, using :math:`R_{i-1}`, :math:`T_{i-1}` and
|
|
||||||
:math:`T_i`.
|
|
||||||
|
|
||||||
If the :math:`j` of the message is less than
|
|
||||||
the current chain index on the receiver then the message may only be decrypted
|
|
||||||
if the receiver has stored a copy of the message key :math:`M_{i,j}`. Otherwise
|
|
||||||
the receiver computes the chain key, :math:`C_{i,j}`. The receiver computes the
|
|
||||||
message key, :math:`M_{i,j}`, from the chain key and attempts to decrypt the
|
|
||||||
cipher-text, :math:`X_{i,j}`.
|
|
||||||
|
|
||||||
If the decryption succeeds the receiver updates the chain key for :math:`T_i`
|
|
||||||
with :math:`C_{i,j+1}` and stores the message keys that were skipped in the
|
|
||||||
process so that they can decode out of order messages. If the receiver created
|
|
||||||
a new receiver chain then they discard their current sender chain so that
|
|
||||||
they will create a new chain when they next send a message.
|
|
||||||
|
|
||||||
The Olm Message Format
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Olm uses two types of messages. The underlying transport protocol must provide
|
|
||||||
a means for recipients to distinguish between them.
|
|
||||||
|
|
||||||
Normal Messages
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Olm messages start with a one byte version followed by a variable length
|
|
||||||
payload followed by a fixed length message authentication code.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
+--------------+------------------------------------+-----------+
|
|
||||||
| Version Byte | Payload Bytes | MAC Bytes |
|
|
||||||
+--------------+------------------------------------+-----------+
|
|
||||||
|
|
||||||
The version byte is ``"\x03"``.
|
|
||||||
|
|
||||||
The payload consists of key-value pairs where the keys are integers and the
|
|
||||||
values are integers and strings. The keys are encoded as a variable length
|
|
||||||
integer tag where the 3 lowest bits indicates the type of the value:
|
|
||||||
0 for integers, 2 for strings. If the value is an integer then the tag is
|
|
||||||
followed by the value encoded as a variable length integer. If the value is
|
|
||||||
a string then the tag is followed by the length of the string encoded as
|
|
||||||
a variable length integer followed by the string itself.
|
|
||||||
|
|
||||||
Olm uses a variable length encoding for integers. Each integer is encoded as a
|
|
||||||
sequence of bytes with the high bit set followed by a byte with the high bit
|
|
||||||
clear. The seven low bits of each byte store the bits of the integer. The least
|
|
||||||
significant bits are stored in the first byte.
|
|
||||||
|
|
||||||
=========== ===== ======== ================================================
|
|
||||||
Name Tag Type Meaning
|
|
||||||
=========== ===== ======== ================================================
|
|
||||||
Ratchet-Key 0x0A String The public part of the ratchet key, :math:`T_{i}`,
|
|
||||||
of the message
|
|
||||||
Chain-Index 0x10 Integer The chain index, :math:`j`, of the message
|
|
||||||
Cipher-Text 0x22 String The cipher-text, :math:`X_{i,j}`, of the message
|
|
||||||
=========== ===== ======== ================================================
|
|
||||||
|
|
||||||
The length of the MAC is determined by the authenticated encryption algorithm
|
|
||||||
being used. (Olm version 1 uses HMAC-SHA-256, truncated to 8 bytes). The
|
|
||||||
MAC protects all of the bytes preceding the MAC.
|
|
||||||
|
|
||||||
Pre-Key Messages
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Olm pre-key messages start with a one byte version followed by a variable
|
|
||||||
length payload.
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
+--------------+------------------------------------+
|
|
||||||
| Version Byte | Payload Bytes |
|
|
||||||
+--------------+------------------------------------+
|
|
||||||
|
|
||||||
The version byte is ``"\x03"``.
|
|
||||||
|
|
||||||
The payload uses the same key-value format as for normal messages.
|
|
||||||
|
|
||||||
============ ===== ======== ================================================
|
|
||||||
Name Tag Type Meaning
|
|
||||||
============ ===== ======== ================================================
|
|
||||||
One-Time-Key 0x0A String The public part of Bob's single-use key,
|
|
||||||
:math:`E_b`.
|
|
||||||
Base-Key 0x12 String The public part of Alice's single-use key,
|
|
||||||
:math:`E_a`.
|
|
||||||
Identity-Key 0x1A String The public part of Alice's identity key,
|
|
||||||
:math:`I_a`.
|
|
||||||
Message 0x22 String An embedded Olm message with its own version and
|
|
||||||
MAC.
|
|
||||||
============ ===== ======== ================================================
|
|
||||||
|
|
||||||
Olm Authenticated Encryption
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Version 1
|
|
||||||
~~~~~~~~~
|
|
||||||
|
|
||||||
Version 1 of Olm uses AES-256_ in CBC_ mode with `PKCS#7`_ padding for
|
|
||||||
encryption and HMAC-SHA-256_ (truncated to 64 bits) for authentication. The
|
|
||||||
256 bit AES key, 256 bit HMAC key, and 128 bit AES IV are derived from the
|
|
||||||
message key using HKDF-SHA-256_ using the default salt and an info of
|
|
||||||
``"OLM_KEYS"``.
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
\begin{align}
|
|
||||||
AES\_KEY_{i,j}\;\parallel\;HMAC\_KEY_{i,j}\;\parallel\;AES\_IV_{i,j}
|
|
||||||
&= HKDF\left(0,\,M_{i,j},\text{"OLM\_KEYS"},\,80\right) \\
|
|
||||||
\end{align}
|
|
||||||
|
|
||||||
The plain-text is encrypted with AES-256, using the key :math:`AES\_KEY_{i,j}`
|
|
||||||
and the IV :math:`AES\_IV_{i,j}` to give the cipher-text, :math:`X_{i,j}`.
|
|
||||||
|
|
||||||
Then the entire message (including the Version Byte and all Payload Bytes) are
|
|
||||||
passed through HMAC-SHA-256. The first 8 bytes of the MAC are appended to the message.
|
|
||||||
|
|
||||||
Message authentication concerns
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
To avoid unknown key-share attacks, the application must include identifying
|
|
||||||
data for the sending and receiving user in the plain-text of (at least) the
|
|
||||||
pre-key messages. Such data could be a user ID, a telephone number;
|
|
||||||
alternatively it could be the public part of a keypair which the relevant user
|
|
||||||
has proven ownership of.
|
|
||||||
|
|
||||||
.. admonition:: Example attacks
|
|
||||||
|
|
||||||
1. Alice publishes her public Curve25519 identity key, :math:`I_A`. Eve
|
|
||||||
publishes the same identity key, claiming it as her own. Bob downloads
|
|
||||||
Eve's keys, and associates :math:`I_A` with Eve. Alice sends a message to
|
|
||||||
Bob; Eve intercepts it before forwarding it to Bob. Bob believes the
|
|
||||||
message came from Eve rather than Alice.
|
|
||||||
|
|
||||||
This is prevented if Alice includes her user ID in the plain-text of the
|
|
||||||
pre-key message, so that Bob can see that the message was sent by Alice
|
|
||||||
originally.
|
|
||||||
|
|
||||||
2. Bob publishes his public Curve25519 identity key, :math:`I_B`. Eve
|
|
||||||
publishes the same identity key, claiming it as her own. Alice downloads
|
|
||||||
Eve's keys, and associates :math:`I_B` with Eve. Alice sends a message to
|
|
||||||
Eve; Eve cannot decrypt it, but forwards it to Bob. Bob believes the
|
|
||||||
Alice sent the message to him, wheras Alice intended it to go to Eve.
|
|
||||||
|
|
||||||
This is prevented by Alice including the user ID of the intended recpient
|
|
||||||
(Eve) in the plain-text of the pre-key message. Bob can now tell that the
|
|
||||||
message was meant for Eve rather than him.
|
|
||||||
|
|
||||||
IPR
|
|
||||||
---
|
|
||||||
|
|
||||||
The Olm specification (this document) is hereby placed in the public domain.
|
|
||||||
|
|
||||||
Feedback
|
|
||||||
--------
|
|
||||||
|
|
||||||
Can be sent to olm at matrix.org.
|
|
||||||
|
|
||||||
Acknowledgements
|
|
||||||
----------------
|
|
||||||
|
|
||||||
The ratchet that Olm implements was designed by Trevor Perrin and Moxie
|
|
||||||
Marlinspike - details at https://whispersystems.org/docs/specifications/doubleratchet/. Olm is
|
|
||||||
an entirely new implementation written by the Matrix.org team.
|
|
||||||
|
|
||||||
.. _`Curve25519`: http://cr.yp.to/ecdh.html
|
|
||||||
.. _`Triple Diffie-Hellman`: https://whispersystems.org/blog/simplifying-otr-deniability/
|
|
||||||
.. _`HMAC-based key derivation function`: https://tools.ietf.org/html/rfc5869
|
|
||||||
.. _`HKDF-SHA-256`: https://tools.ietf.org/html/rfc5869
|
|
||||||
.. _`HMAC-SHA-256`: https://tools.ietf.org/html/rfc2104
|
|
||||||
.. _`SHA-256`: https://tools.ietf.org/html/rfc6234
|
|
||||||
.. _`AES-256`: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
|
||||||
.. _`CBC`: http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
|
|
||||||
.. _`PKCS#7`: https://tools.ietf.org/html/rfc2315
|
|
|
@ -1,20 +1,4 @@
|
||||||
.. Copyright 2016 OpenMarket Ltd
|
# Signature keys and user identity in libolm
|
||||||
..
|
|
||||||
.. Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
.. you may not use this file except in compliance with the License.
|
|
||||||
.. You may obtain a copy of the License at
|
|
||||||
..
|
|
||||||
.. http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
..
|
|
||||||
.. Unless required by applicable law or agreed to in writing, software
|
|
||||||
.. distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
.. See the License for the specific language governing permissions and
|
|
||||||
.. limitations under the License.
|
|
||||||
|
|
||||||
|
|
||||||
Signature keys and user identity in libolm
|
|
||||||
==========================================
|
|
||||||
|
|
||||||
The use of any public-key based cryptography system such as Olm presents the
|
The use of any public-key based cryptography system such as Olm presents the
|
||||||
need for our users Alice and Bob to verify that they are in fact communicating
|
need for our users Alice and Bob to verify that they are in fact communicating
|
||||||
|
@ -23,13 +7,13 @@ out-of-band process in which Alice and Bob verify that they have the correct
|
||||||
public keys for each other. For example, this might be done via physical
|
public keys for each other. For example, this might be done via physical
|
||||||
presence or via a voice call.
|
presence or via a voice call.
|
||||||
|
|
||||||
In the basic `Olm <olm.html>`_ protocol, it is sufficient to compare the public
|
In the basic [Olm][] protocol, it is sufficient to compare the public
|
||||||
Curve25519 identity keys. As a naive example, Alice would meet Bob and ensure
|
Curve25519 identity keys. As a naive example, Alice would meet Bob and ensure
|
||||||
that the identity key she downloaded from the key server matched that shown by
|
that the identity key she downloaded from the key server matched that shown by
|
||||||
his device. This prevents the eavesdropper Eve from decrypting any messages
|
his device. This prevents the eavesdropper Eve from decrypting any messages
|
||||||
sent from Alice to Bob, or from masquerading as Bob to send messages to Alice:
|
sent from Alice to Bob, or from masquerading as Bob to send messages to Alice:
|
||||||
she has neither Alice's nor Bob's private identity key, so cannot successfully
|
she has neither Alice's nor Bob's private identity key, so cannot successfully
|
||||||
complete the triple-DH calculation to compute the shared secret, :math:`S`,
|
complete the triple-DH calculation to compute the shared secret, $`S`$,
|
||||||
which in turn prevents her decrypting intercepted messages, or from creating
|
which in turn prevents her decrypting intercepted messages, or from creating
|
||||||
new messages with valid MACs. Obviously, for protection to be complete, Bob
|
new messages with valid MACs. Obviously, for protection to be complete, Bob
|
||||||
must similarly verify Alice's key.
|
must similarly verify Alice's key.
|
||||||
|
@ -41,7 +25,7 @@ one-time keys. Curve25519 keys are intended for use in DH calculations, and
|
||||||
their use to calculate signatures is non-trivial.
|
their use to calculate signatures is non-trivial.
|
||||||
|
|
||||||
The solution adopted in this library is to generate a signing key for each
|
The solution adopted in this library is to generate a signing key for each
|
||||||
user. This is an `Ed25519`_ keypair, which is used to calculate a signature on
|
user. This is an [Ed25519][] keypair, which is used to calculate a signature on
|
||||||
an object including both the public Ed25519 signing key and the public
|
an object including both the public Ed25519 signing key and the public
|
||||||
Curve25519 identity key. It is then the **public Ed25519 signing key** which is
|
Curve25519 identity key. It is then the **public Ed25519 signing key** which is
|
||||||
used as the device fingerprint which Alice and Bob verify with each other.
|
used as the device fingerprint which Alice and Bob verify with each other.
|
||||||
|
@ -50,8 +34,7 @@ By verifying the signatures on the key object, Alice and Bob then get the same
|
||||||
level of assurance about the ownership of the Curve25519 identity keys as if
|
level of assurance about the ownership of the Curve25519 identity keys as if
|
||||||
they had compared those directly.
|
they had compared those directly.
|
||||||
|
|
||||||
Signing one-time keys
|
## Signing one-time keys
|
||||||
---------------------
|
|
||||||
|
|
||||||
The Olm protocol requires users to publish a set of one-time keys to a key
|
The Olm protocol requires users to publish a set of one-time keys to a key
|
||||||
server. To establish an Olm session, the originator downloads a key for the
|
server. To establish an Olm session, the originator downloads a key for the
|
||||||
|
@ -60,19 +43,20 @@ is left to the application. There are both advantages and disadvantages to
|
||||||
doing so.
|
doing so.
|
||||||
|
|
||||||
Consider the scenario where one-time keys are unsigned. Alice wants to initiate
|
Consider the scenario where one-time keys are unsigned. Alice wants to initiate
|
||||||
an Olm session with Bob. Bob uploads his one-time keys, :math:`E_B`, but Eve
|
an Olm session with Bob. Bob uploads his one-time keys, $`E_B`$, but Eve
|
||||||
replaces them with ones she controls, :math:`E_E`. Alice downloads one of the
|
replaces them with ones she controls, $`E_E`$. Alice downloads one of the
|
||||||
compromised keys, and sends a pre-key message using a shared secret :math:`S`,
|
compromised keys, and sends a pre-key message using a shared secret $`S`$,
|
||||||
where:
|
where:
|
||||||
|
|
||||||
.. math::
|
```math
|
||||||
S = ECDH\left(I_A,\,E_E\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\;
|
S = ECDH\left(I_A,\,E_E\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\;
|
||||||
\parallel\;ECDH\left(E_A,\,E_E\right)
|
\parallel\;ECDH\left(E_A,\,E_E\right)
|
||||||
|
```
|
||||||
|
|
||||||
Eve cannot decrypt the message because she does not have the private parts of
|
Eve cannot decrypt the message because she does not have the private parts of
|
||||||
either :math:`E_A` nor :math:`I_B`, so cannot calculate
|
either $`E_A`$ nor $`I_B`$, so cannot calculate
|
||||||
:math:`ECDH\left(E_A,\,I_B\right)`. However, suppose she later compromises
|
$`ECDH\left(E_A,\,I_B\right)`$. However, suppose she later compromises
|
||||||
Bob's identity key :math:`I_B`. This would give her the ability to decrypt any
|
Bob's identity key $`I_B`$. This would give her the ability to decrypt any
|
||||||
pre-key messages sent to Bob using the compromised one-time keys, and is thus a
|
pre-key messages sent to Bob using the compromised one-time keys, and is thus a
|
||||||
problematic loss of forward secrecy. If Bob signs his keys with his Ed25519
|
problematic loss of forward secrecy. If Bob signs his keys with his Ed25519
|
||||||
signing key (and Alice verifies the signature before using them), this problem
|
signing key (and Alice verifies the signature before using them), this problem
|
||||||
|
@ -81,38 +65,38 @@ is avoided.
|
||||||
On the other hand, signing the one-time keys leads to a reduction in
|
On the other hand, signing the one-time keys leads to a reduction in
|
||||||
deniability. Recall that the shared secret is calculated as follows:
|
deniability. Recall that the shared secret is calculated as follows:
|
||||||
|
|
||||||
.. math::
|
```math
|
||||||
S = ECDH\left(I_A,\,E_B\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\;
|
S = ECDH\left(I_A,\,E_B\right)\;\parallel\;ECDH\left(E_A,\,I_B\right)\;
|
||||||
\parallel\;ECDH\left(E_A,\,E_B\right)
|
\parallel\;ECDH\left(E_A,\,E_B\right)
|
||||||
|
```
|
||||||
|
|
||||||
If keys are unsigned, a forger can make up values of :math:`E_A` and
|
If keys are unsigned, a forger can make up values of $`E_A`$ and
|
||||||
:math:`E_B`, and construct a transcript of a conversation which looks like it
|
$`E_B`$, and construct a transcript of a conversation which looks like it
|
||||||
was between Alice and Bob. Alice and Bob can therefore plausibly deny their
|
was between Alice and Bob. Alice and Bob can therefore plausibly deny their
|
||||||
partition in any conversation even if they are both forced to divulge their
|
partition in any conversation even if they are both forced to divulge their
|
||||||
private identity keys, since it is impossible to prove that the transcript was
|
private identity keys, since it is impossible to prove that the transcript was
|
||||||
a conversation between the two of them, rather than constructed by a forger.
|
a conversation between the two of them, rather than constructed by a forger.
|
||||||
|
|
||||||
If :math:`E_B` is signed, it is no longer possible to construct arbitrary
|
If $`E_B`$ is signed, it is no longer possible to construct arbitrary
|
||||||
transcripts. Given a transcript and Alice and Bob's identity keys, we can now
|
transcripts. Given a transcript and Alice and Bob's identity keys, we can now
|
||||||
show that at least one of Alice or Bob was involved in the conversation,
|
show that at least one of Alice or Bob was involved in the conversation,
|
||||||
because the ability to calculate :math:`ECDH\left(I_A,\,E_B\right)` requires
|
because the ability to calculate $`ECDH\left(I_A,\,E_B\right)`$ requires
|
||||||
knowledge of the private parts of either :math:`I_A` (proving Alice's
|
knowledge of the private parts of either $`I_A`$ (proving Alice's
|
||||||
involvement) or :math:`E_B` (proving Bob's involvement, via the
|
involvement) or $`E_B`$ (proving Bob's involvement, via the
|
||||||
signature). Note that it remains impossible to show that *both* Alice and Bob
|
signature). Note that it remains impossible to show that *both* Alice and Bob
|
||||||
were involved.
|
were involved.
|
||||||
|
|
||||||
In conclusion, applications should consider whether to sign one-time keys based
|
In conclusion, applications should consider whether to sign one-time keys based
|
||||||
on the trade-off between forward secrecy and deniability.
|
on the trade-off between forward secrecy and deniability.
|
||||||
|
|
||||||
License
|
## License
|
||||||
-------
|
|
||||||
|
|
||||||
This document is licensed under the `Apache License, Version 2.0
|
This document is licensed under the Apache License, Version 2.0
|
||||||
<http://www.apache.org/licenses/LICENSE-2.0>`_.
|
http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
|
||||||
Feedback
|
## Feedback
|
||||||
--------
|
|
||||||
|
|
||||||
Questions and feedback can be sent to olm at matrix.org.
|
Questions and feedback can be sent to olm at matrix.org.
|
||||||
|
|
||||||
.. _`Ed25519`: http://ed25519.cr.yp.to/
|
[Ed25519]: http://ed25519.cr.yp.to/
|
||||||
|
[Olm]: https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/olm.md
|
Loading…
Reference in a new issue