diff --git a/docs/End-To-End-Encryption.md b/docs/End-To-End-Encryption.md index 5bd64a46..31d90de6 100644 --- a/docs/End-To-End-Encryption.md +++ b/docs/End-To-End-Encryption.md @@ -27,6 +27,9 @@ Assume we have the following constants and variables defined in our scheme: - `KEY_EPH_PUB` - Public part of `EPH_KEYPAIR`. - `SHARED_INFO_2` - Input parameter to MAC calculation. +### Temporary Encryption Keys + +To provide required cryptographic features, such as forward secrecy, encryption uses [temporary encryption keys](./Temporary-Encryption-Keys.md) since protocol version `3.3`. ### Encryption Scope @@ -37,9 +40,9 @@ PowerAuth protocol defines two basic usage scopes for ECIES encryption: #### Application Scope -ECIES in application scope has following configuration of parameters: +ECIES in application scope has the following configuration of parameters: -- `KEY_ENC_PUB` is `KEY_SERVER_MASTER_PUBLIC` +- `KEY_ENC_PUB` is a [temporary key](./Temporary-Encryption-Keys.md) with given `TEMP_KEY_ID` identifier fetched from the server associated with a specific application version and signed with `KEY_SERVER_MASTER_PRIVATE` (to prove it was intended for the application scope). - `SHARED_INFO_1` is a pre-shared constant and is different for each endpoint (see [Pre-shared constants](#pre-shared-constants)) - `SHARED_INFO_2_BASE` is calculated from `APPLICATION_SECRET`: ```java @@ -47,7 +50,7 @@ ECIES in application scope has following configuration of parameters: ``` - `ASSOCIATED_DATA` is calculated as: ```java - byte[] ASSOCIATED_DATA = ByteUtils.concatWithSizes(VERSION, APPLICATION_KEY); + byte[] ASSOCIATED_DATA = ByteUtils.concatWithSizes(VERSION, APPLICATION_KEY, TEMP_KEY_ID); ``` @@ -56,9 +59,9 @@ Note that the `APPLICATION_SECRET` constant is in Base64 form, so we need to rei #### Activation Scope -ECIES in activation scope has following configuration of parameters: +ECIES in activation scope has the following configuration of parameters: -- `KEY_ENC_PUB` is `KEY_SERVER_PUBLIC` (e.g. key which is unique for each activation) +- `KEY_ENC_PUB` is a [temporary key](./Temporary-Encryption-Keys.md) with given `TEMP_KEY_ID` identifier fetched from the server associated with a specific application version and activation, and signed with `KEY_SERVER_PUBLIC` (the key which is unique for each activation, to prove it was intended for the activations cope). - `SHARED_INFO_1` is a pre-shared constant and is different for each endpoint (see [Pre-shared constants](#pre-shared-constants)) - `SHARED_INFO_2_BASE` is calculated from `APPLICATION_SECRET` and `KEY_TRANSPORT`: ```java @@ -66,7 +69,7 @@ ECIES in activation scope has following configuration of parameters: ``` - `ASSOCIATED_DATA` is calculated as: ```java - byte[] ASSOCIATED_DATA = ByteUtils.concatWithSizes(VERSION, APPLICATION_KEY, ACTIVATION_ID); + byte[] ASSOCIATED_DATA = ByteUtils.concatWithSizes(VERSION, APPLICATION_KEY, ACTIVATION_ID, TEMP_KEY_ID); ``` @@ -75,7 +78,7 @@ Note that the `APPLICATION_SECRET` constant is in Base64 form, so we need to rei ### ECIES Encryption -Assume we have a public key `KEY_ENC_PUB`, data `PLAINTEXT` to be encrypted, `ASSOCIATED_DATA` to be included in mac calculation and a `SHARED_INFO_1` and `SHARED_INFO_2_BASE` constants (`byte[]`) as encryption parameters. ECIES encryption works in a following way: +Assume we have a public key `KEY_ENC_PUB`, data `PLAINTEXT` to be encrypted, `ASSOCIATED_DATA` to be included in MAC calculation and a `SHARED_INFO_1` and `SHARED_INFO_2_BASE` constants (`byte[]`) as encryption parameters. ECIES encryption works in the following way: 1. Generate an ephemeral key pair: ```java @@ -111,16 +114,16 @@ Assume we have a public key `KEY_ENC_PUB`, data `PLAINTEXT` to be encrypted, `AS 1. Derive `IV` from `NONCE` and encrypt ata using AES. ```java byte[] IV = KDF_INTERNAL.derive(KEY_IV, NONCE); - byte[] DATA_ENCRYPTED = AES.encrypt(PLAINTEXT, IV, KEY_ENC) + byte[] DATA_ENCRYPTED = AES.encrypt(PLAINTEXT, IV, KEY_ENC); ``` 1. Compute the MAC of encrypted data, include `SHARED_INFO_2`. ```java byte[] DATA = Bytes.concat(DATA_ENCRYPTED, SHARED_INFO_2); - byte[] MAC = Mac.hmacSha256(KEY_MAC, DATA) + byte[] MAC = Mac.hmacSha256(KEY_MAC, DATA); ``` 1. Prepare ECIES payload. ```java - EciesPayload payload = (DATA_ENCRYPTED, MAC, KEY_EPH_PUB, NONCE, TIMESTAMP) + EciesPayload payload = (DATA_ENCRYPTED, MAC, KEY_EPH_PUB, NONCE, TIMESTAMP); ``` If this is a response encryption, then we omit `KEY_EPH_PUB` and set it to `null` in steps 3. and 9. to make the response shorter. For example, `SHARED_INFO_2` is then calculated as: @@ -140,13 +143,13 @@ Assume we have a private key `KEY_ENC_PRIV`, encrypted data as an instance of th ``` 1. Derive base secret key from the private key and ephemeral public key from the ECIES payload (in this step, we do not trim the key to 16b only, we keep all 32b). ```java - SecretKey KEY_BASE = ECDH.phase(KEY_ENC_PRIV, KEY_EPH_PUB) + SecretKey KEY_BASE = ECDH.phase(KEY_ENC_PRIV, KEY_EPH_PUB); ``` 1. Derive a secret key using X9.63 KDF function (using SHA256 internally). When calling the KDF, we use `VERSION`, `SHARED_INFO_1` together with `KEY_EPH_PUB` value (as raw `byte[]`) as an `info` parameter. ```java byte[] VERSION_BYTES = ByteUtils.encode(VERSION); byte[] INFO = Bytes.concat(VERSION_BYTES, SHARED_INFO_1, KEY_EPH_PUB); - SecretKey KEY_SECRET = KDF_X9_63_SHA256.derive(KEY_BASE, INFO, 48) + SecretKey KEY_SECRET = KDF_X9_63_SHA256.derive(KEY_BASE, INFO, 48); ``` 1. Split the 48 bytes long `KEY_SECRET` to three 16B keys. The first part is used as an encryption key `KEY_ENC`. The second part is used as MAC key `KEY_MAC`. The final part is a key for IV derivation `KEY_IV`. ```java @@ -166,7 +169,7 @@ Assume we have a private key `KEY_ENC_PRIV`, encrypted data as an instance of th 1. Decrypt the data using AES, with `IV` value derived from `NONCE`. ```java byte[] IV = KDF_INTERNAL.derive(KEY_IV, NONCE); - byte[] PLAINTEXT = AES.decrypt(DATA_ENCRYPTED, IV, KEY_ENC) + byte[] PLAINTEXT = AES.decrypt(DATA_ENCRYPTED, IV, KEY_ENC); ``` If this is a response decryption, then we omit `KEY_EPH_PUB` and set it to `null` in step 1. @@ -175,9 +178,9 @@ If this is a response decryption, then we omit `KEY_EPH_PUB` and set it to `null Practical implementation of ECIES encryption in PowerAuth accounts for a typical request-response cycle, since encrypting RESTful API requests and responses is the most common use-case. -Client implementation creates an encryptor object that allows encrypting the request and decrypting the response. When encrypting the request, encryptor object accepts a `byte[]` and a public key (for example, `MASTER_SERVER_PUBLIC_KEY`) and produces an instance of `EciesPayload` class. After it receives an encrypted response from the server, which is essentially another instance of `EciesPayload`, it is able to use the original encryption context (the shared encryption keys) to decrypt the response. +Client implementation creates an encryptor object that allows encrypting the request and decrypting the response. When encrypting the request, encryptor object accepts a `byte[]` and a [temporary public key](./Temporary-Encryption-Keys.md) . Then, it produces an instance of `EciesPayload` class. After it receives an encrypted response from the server, which is essentially another instance of `EciesPayload`, it is able to use the original encryption context (the shared encryption keys) to decrypt the response. -Server implementation creates a decryptor object that allows decrypting the original request data and encrypting the response. When server receives an encrypted request, essentially as an `EciesPayload` instance again, it uses a private key (for example, `MASTER_SERVER_PRIVATE_KEY`) to decrypt the original bytes and uses the encryption context to encrypt a response to the client. +Server implementation creates a decryptor object that allows decrypting the original request data and encrypting the response. When server receives an encrypted request, essentially as an `EciesPayload` instance again, it uses a [temporary private key](./Temporary-Encryption-Keys.md) (looked up based on the temporary key ID) to decrypt the original bytes and uses the encryption context to encrypt a response to the client. Since the client and server use the same encryption context, the ephemeral public key needs to be only sent with the request from the client. Response may only contain encrypted data and MAC value. @@ -202,6 +205,7 @@ The typical JSON encoded request is following: ```json { + "temporaryKeyId": "dc497e8a-8faa-44bc-a52a-20d8393005d2", "ephemeralPublicKey" : "A97NlW0JPLJfpG0AUvaRHRGSHh+quZu+u0c+yxsK7Xji", "encryptedData" : "qYLONkDWFpXefTKPbaKTA/PWdRYH5pk9uvGjUqSYbeK7Q0aOohK2MknTyviyNuSp", "mac" : "DNlZdsM1wgH8v2mAROjj3vmQu4DI4ZJnuTBzQMrHsew=", @@ -246,18 +250,18 @@ The response doesn't use HTTP headers. PowerAuth protocol defines following `SHARED_INFO_1` (also called as `sh1` or `sharedInfo1`) constants for its own internal purposes: -| RESTful endpoint | ECIES scope | `SHARED_INFO_1` value | -| ------------------------------------- | ------------ | --------------------- | +| RESTful endpoint | ECIES scope | `SHARED_INFO_1` value | +| ------------------------------------- | ------------ |---------------------------| | `/pa/v3/activation/create` (level 1) | application | `/pa/generic/application` | -| `/pa/v3/activation/create` (level 2) | application | `/pa/activation` | -| `/pa/v3/upgrade` | activation | `/pa/upgrade` | -| `/pa/v3/vault/unlock` | activation | `/pa/vault/unlock` | -| `/pa/v3/token/create` | activation | `/pa/token/create` | -| `/pa/v3/recovery/confirm` | activation | `/pa/recovery/confirm` | +| `/pa/v3/activation/create` (level 2) | application | `/pa/activation` | +| `/pa/v3/upgrade` | activation | `/pa/upgrade` | +| `/pa/v3/vault/unlock` | activation | `/pa/vault/unlock` | +| `/pa/v3/token/create` | activation | `/pa/token/create` | +| `/pa/v3/recovery/confirm` | activation | `/pa/recovery/confirm` | On top of that, following constants can be used for application-specific purposes: -| Purpose | ECIES scope | `SHARED_INFO_1` value | -| ---------------------------------------- | ------------ | --------------------- | +| Purpose | ECIES scope | `SHARED_INFO_1` value | +| ---------------------------------------- | ------------ |---------------------------| | Generic encryptor for application scope | application | `/pa/generic/application` | -| Generic encryptor for activation scope | activation | `/pa/generic/activation` | +| Generic encryptor for activation scope | activation | `/pa/generic/activation` |