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