diff --git a/docs/vault.md b/docs/vault.md
index 6a892a00..e60d244d 100644
--- a/docs/vault.md
+++ b/docs/vault.md
@@ -14,8 +14,10 @@ format of the vault.
### Primitives
Two cryptographic primitives were selected for use in Aegis. An Authenticated
-Encryption with Associated Data (AEAD) cipher and a Key Derivation Function
-(KDF).
+Encryption with Associated Data
+([AEAD](https://en.wikipedia.org/wiki/Authenticated_encryption#Authenticated_encryption_with_associated_data))
+cipher and a Key Derivation Function
+([KDF](https://en.wikipedia.org/wiki/Key_derivation_function)).
#### AEAD
@@ -36,15 +38,16 @@ This is a reasonable assumption to make, because it's highly unlikely that an
Aegis user will ever come close to saving the vault 232 times.
_Switching to a nonce misuse-resistant cipher like AES-GCM-SIV or a cipher with
-a larger (192 bits) nonce like XChaCha-Poly1305 will be considered in the future._
+a larger (192 bits) nonce like XChaCha-Poly1305 will be considered in the
+future._
#### KDF
-__scrypt__ is used as the KDF to derive a key from a user-provided password,
-with the following parameters:
+[__scrypt__](https://en.wikipedia.org/wiki/Scrypt) is used as the KDF to derive
+a key from a user-provided password, with the following parameters:
-| Parameter | Value |
-| :-------- | :------------- |
+| Parameter | Value |
+|:----------|:---------------|
| N | 215 |
| r | 8 |
| p | 1 |
@@ -104,12 +107,17 @@ shown below:
}
```
-It starts with a ``version`` number and a ``header``. If a backwards
-incompatible change is introduced to the content format, the version number will
-be incremented. The vault contents are stored under ``db``. Its value depends on
-whether the vault is encrypted or not. If it is, the value is a string containing
-the Base64 encoded (with padding) ciphertext of the vault contents. Otherwise,
-the value is a JSON object.
+It starts with a ``version`` number. If a forwards incompatible change is
+introduced to the vault format, the version number will be incremented. The
+current version of the vault format is ``1``.
+
+The [``header``](#header), if not empty, contains the list of slots and the
+encryption parameters used for decrypting the vault.
+
+The vault contents are stored under ``db``. Its value depends on whether the
+vault is encrypted or not. If it is, the value is a string containing the Base64
+encoded (with padding) ciphertext of the vault contents. Otherwise, the value is
+a JSON object. See [vault content](#vault-content) for details.
Full examples of a [plain text
vault](/app/src/test/resources/com/beemdevelopment/aegis/importers/aegis_plain.json)
@@ -122,9 +130,14 @@ password: [decrypt.py](/docs/decrypt.py).
### Header
-The header starts with the list of ``slots``. It also has a ``params`` object
-that holds the ``nonce`` and ``tag`` that were produced during encryption,
-encoded as a hexadecimal string.
+The header starts with the list of [``slots``](#slots-1). Each slot contains the
+master key in an encrypted form together with the key wrapping parameters.
+
+It also has a ``params`` object that holds the ``nonce`` and ``tag`` that were
+produced during the AES-GCM encryption, encoded as a hexadecimal string. These
+encryption parameters together with the master key (which can be retrieved by
+decrypting the ``key`` from one of the slots) are used to decrypt the vault
+contents found in the ``db`` field.
Setting ``slots`` and ``params`` to null indicates that the vault is not
encrypted and Aegis will try to parse it as such.
@@ -141,13 +154,13 @@ encrypted and Aegis will try to parse it as such.
#### Slots
-The different slot types are identified with a numerical ID.
+The different slot types are identified with a numerical ID.
-| Type | ID |
-| :---------- | :--- |
-| Raw | 0x00 |
-| Password | 0x01 |
-| Biometric | 0x02 |
+| Type | ID |
+|:----------|:-----|
+| Raw | 0x00 |
+| Password | 0x01 |
+| Biometric | 0x02 |
##### Raw
@@ -182,7 +195,7 @@ Raw slots don't imply use of a particular storage type.
As noted earlier, scrypt is used to derive a 256-bit key from a user-provided
password. A random 256-bit ``salt`` is generated and passed to scrypt to protect
-against rainbow table attacks. Its stored along with the ``N``, ``r`` and ``p``
+against rainbow table attacks. It's stored along with the ``N``, ``r`` and ``p``
parameters.
```json
@@ -201,56 +214,69 @@ parameters.
}
```
-### Content
+### Vault content
The content is a JSON object encoded in UTF-8.
```json
{
- "version": 1,
- "entries": []
+ "version": 3,
+ "entries": [],
+ "groups": []
}
```
-It has a ``version`` number and a list of ``entries``. If a backwards
-incompatible change is introduced to the content format, the version number will
-be incremented.
+It has a ``version`` number, a list of ``entries`` and a list of ``groups``. If
+a forwards incompatible change is introduced to the content format, the version
+number will be incremented. The current version of the vault content format is
+``3``.
+
+| Field | Type | Description |
+|:------------|:------|:-----------------------------------------|
+| ``version`` | int | The version of the vault content format. |
+| ``entries`` | array | A list of [entries](#entries). |
+| ``groups`` | array | A list of [groups](#groups). |
#### Entries
-Each entry has a unique randomly generated ``UUID`` (version 4), as well as a
-``name`` and ``issuer`` to identify the account name and service that the token
-is for. Entries can also have an icon. These are JPEG's encoded in Base64 with
-padding. The ``info`` object holds information specific to the OTP type. The
-``secret`` is encoded in Base32 without padding.
+Each entry has a unique randomly generated ``UUID``, as well as a ``name`` and
+``issuer`` to identify the account name and service that the token is for.
+
+Entries hold the following fields:
+
+| Field | Type | Description |
+|:--------------|:---------------|:--------------------------------------------------------------------------------|
+| ``type`` | string | The type of the OTP algorithm. See table below. |
+| ``uuid`` | string | A UUID (version 4). |
+| ``name`` | string | The account name. |
+| ``issuer`` | string | The service that the token is for. |
+| ``note`` | string | A personal note about the entry. |
+| ``icon`` | string \| null | JPEG's encoded in Base64 with padding. |
+| ``icon_mime`` | string \| null | The MIME type of the icon. Is null if ``icon`` is null. |
+| ``icon_hash`` | string \| null | The SHA-256 hash of the icon. Is null if ``icon`` is null. |
+| ``favorite`` | bool | Whether the entry is a favorite or not. |
+| ``info`` | object | Information specific to the OTP type. |
+| ``groups`` | array | A list of UUIDs of groups that the entry is a member of. See [Groups](#groups). |
+
+The ``info`` object contains different fields depending on the type of the OTP.
There are a number of supported types:
-| Type | ID | Spec |
-| :------------------ | :------- | :-------- |
-| HOTP | "hotp" | [RFC 4226](https://datatracker.ietf.org/doc/html/rfc4226)
-| TOTP | "totp" | [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238)
-| Steam | "steam" | N/A
-| Yandex | "yandex" | N/A
+| Type | ID | Spec |
+|:-------|:---------|:----------------------------------------------------------|
+| HOTP | "hotp" | [RFC 4226](https://datatracker.ietf.org/doc/html/rfc4226) |
+| TOTP | "totp" | [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238) |
+| Steam | "steam" | N/A |
+| MOTP | "motp" | N/A |
+| Yandex | "yandex" | N/A |
-There is no specification available for Steam's OTP algorithm. It's essentially
-the same as TOTP, but it uses a different final encoding step. Aegis'
-implementation of it can be found in
-[crypto/otp/OTP.java](https://github.com/beemdevelopment/Aegis/blob/master/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/OTP.java).
+Common ``info`` fields for all types:
-There is also no specification available for Yandex's OTP algorithm. Aegis'
-implementation can be found in
-[crypto/otp/YAOTP.java](https://github.com/beemdevelopment/Aegis/blob/master/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/YAOTP.java)
-
-The following algorithms are supported for HOTP and TOTP:
-
-| Algorithm | ID |
-| :-------- | :------- |
-| SHA-1 | "SHA1" |
-| SHA-256 | "SHA256" |
-| SHA-512 | "SHA512" |
-
-For Steam, only SHA-1 is supported. For Yandex, only SHA-256 is supported.
+| Field | Type | Description |
+|:-----------|:-------|:-----------------------------------|
+| ``secret`` | string | The Base32 encoded secret. |
+| ``algo`` | string | The hashing algorithm. |
+| ``digits`` | int | The number of digits in the token. |
Example of a TOTP entry:
@@ -260,12 +286,105 @@ Example of a TOTP entry:
"uuid": "01234567-89ab-cdef-0123-456789abcdef",
"name": "Bob",
"issuer": "Google",
+ "note": "Main account",
+ "favorite": false,
"icon": null,
+ "icon_mime": null,
+ "icon_hash": null,
"info": {
"secret": "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
"algo": "SHA1",
"digits": 6,
"period": 30
- }
+ },
+ "groups": [
+ "01234567-89ab-cdef-0123-456789abcdef"
+ ]
+}
+```
+
+##### HOTP and TOTP
+
+TOTP uses the ``period`` field, which is the time step in seconds. HOTP uses the
+``counter`` field, which is incremented every time a token is generated.
+
+The following algorithms are supported for HOTP and TOTP:
+
+| Algorithm | ID |
+|:----------|:---------|
+| SHA-1 | "SHA1" |
+| SHA-256 | "SHA256" |
+| SHA-512 | "SHA512" |
+
+##### Steam
+
+There is no specification available for Steam's OTP algorithm. It's essentially
+the same as TOTP, but it uses a different final encoding step. Aegis'
+implementation of it can be found in
+[crypto/otp/OTP.java](/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/OTP.java).
+
+A couple of fields have fixed values:
+
+| Field | Value |
+|:-----------|:-------|
+| ``algo`` | "SHA1" |
+| ``period`` | 30 |
+| ``digits`` | 5 |
+
+##### MOTP
+
+There is no specification available for MOTP. Aegis' implementation of it can be
+found in
+[crypto/otp/MOTP.java](/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/MOTP.java).
+
+A couple of fields have fixed values:
+
+| Field | Value |
+|:-----------|:------|
+| ``algo`` | "MD5" |
+| ``period`` | 10 |
+| ``digits`` | 6 |
+
+MOTP-specific fields:
+
+| Field | Type | Description |
+|:--------|:-------|:------------|
+| ``pin`` | string | 4-digit PIN |
+
+##### Yandex
+
+There is no specification available for Yandex's OTP algorithm. Aegis'
+implementation can be found in
+[crypto/otp/YAOTP.java](/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/YAOTP.java)
+
+A couple of fields have fixed values:
+
+| Field | Value |
+|:-----------|:---------|
+| ``algo`` | "SHA256" |
+| ``period`` | 30 |
+| ``digits`` | 8 |
+
+Yandex-specific fields:
+
+| Field | Type | Description |
+|:--------|:-------|:---------------|
+| ``pin`` | string | 4-16 digit PIN |
+
+#### Groups
+
+A group consists of a ``name`` and a randomly generated ``uuid`` (version 4).
+
+| Field | Type | Description |
+|:---------|:-------|:-----------------------|
+| ``uuid`` | string | A UUID (version 4). |
+| ``name`` | string | The name of the group. |
+
+Example:
+
+```json
+{
+ "uuid": "01234567-89ab-cdef-0123-456789abcdef",
+ "name": "Personal"
}
```