mirror of
https://github.com/minio/minio.git
synced 2024-12-26 07:05:55 -05:00
b28661b673
This commit adds some documentation about the design of the SSE-C and SSE-S3 implementation. It describes how the Minio server encrypt objects and manages keys.
249 lines
15 KiB
Markdown
249 lines
15 KiB
Markdown
# Minio Security Overview [![Slack](https://slack.minio.io/slack?type=svg)](https://slack.minio.io)
|
|
|
|
## Server-Side Encryption
|
|
|
|
Minio supports two different types of server-side encryption ([SSE](#sse)):
|
|
- **SSE-C**: The Minio server en/decrypts an object with a secret key provided by the S3 client
|
|
as part of the HTTP request headers. Therefore, [SSE-C](#ssec) requires TLS/HTTPS.
|
|
- **SSE-S3**: The Minio server en/decrypts an object with a secret key managed by a KMS.
|
|
Therefore, Minio requires a valid KMS configuration for [SSE-S3](#sses3).
|
|
|
|
### Server-Side Encryption - Preliminaries
|
|
|
|
#### Secret Keys
|
|
|
|
The Minio server uses an unique, randomly generated secret key per object also known as,
|
|
Object Encryption Key ([OEK](#oek)). Neither the client-provided SSE-C key nor the KMS-managed
|
|
key is directly used to en/decrypt an object. Instead, the OEK is stored as part of the object
|
|
metadata next to the object in an encrypted form. To en/decrypt the OEK another secret key is
|
|
needed also known as, Key Encryption Key ([KEK](#kek)).
|
|
|
|
The Minio server runs a key-derivation algorithm to generate the KEK using a pseudo-random
|
|
function ([PRF](#prf)):
|
|
`KEK := PRF(EK, IV, context_values)` where
|
|
- [EK](#ek): is the external key. In case of SSE-C this is the client-provided key. In case of SSE-S3
|
|
this is secret key generated by the KMS. For further details see
|
|
[SSE-C](#Server-Side-Encryption-with-client-provided-Keys) or
|
|
[SSE-S3](#Server-Side-Encryption-with-a-KMS).
|
|
- [IV](#iv): is a randomly generated initialization vector. It is public and part of the
|
|
object metadata.
|
|
- `context_values`: are values like the bucket and object name and other information which should be
|
|
cryptographically bound to the KEK.
|
|
|
|
To summarize for any encrypted object there exists (at least) three different keys:
|
|
1. [OEK](#oek): A secret and unique key used to encrypted the object, stored in an encrypted form as
|
|
part of the object metadata and only loaded to RAM in plaintext during en/decrypting the object.
|
|
2. [KEK](#kek): A secret and unique key used to en/decrypt the OEK and never stored anywhere.
|
|
It is(re-)generated whenever en/decrypting an object using an external secret key and public
|
|
parameters.
|
|
3. [EK](#ek): An external secret key - either the SSE-C client-provided key or a secret key
|
|
generated by the KMS.
|
|
|
|
#### Content Encryption
|
|
|
|
The Minio server uses an authenticated encryption scheme ([AEAD](#aead)) to en/decrypt and
|
|
authenticate the object content. The AEAD is combined with some state to build a
|
|
*Secure Channel*. A *Secure Channel* is a cryptographic construction that ensures confidentiality
|
|
and integrity of the processed data. In particular the *Secure Channel* splits the plaintext content
|
|
into fixed size chunks and en/decrypts each chunk separately using an unique key-nonce combination.
|
|
|
|
```
|
|
plaintext := chunk_0 || chunk_1 || chunk_2 || ...
|
|
| | |
|
|
| | |
|
|
AEAD <- key, nonce + 0 AEAD <- key, nonce + 1 AEAD <- key, nonce + 2 ...
|
|
| | |
|
|
| | |
|
|
ciphertext := sealed_chunk_0 || sealed_chunk_1 || sealed_chunk_2 || ...
|
|
```
|
|
<center>Figure 1 - Secure Channel construction</center>
|
|
|
|
In case of a S3 multi-part operation each part is en/decrypted with the scheme shown in
|
|
Figure 1. However, for each part an unique secret key is derived from the OEK and the part
|
|
number using a PRF. So in case of multi-part not the OEK but the output of `PRF(OEK, part_id)`
|
|
is used as secret key.
|
|
|
|
#### Cryptographic Primitives
|
|
|
|
The SSE schemes described in [Secret Keys](#Secret-Keys) and [Content Encryption](#Content-Encryption)
|
|
are generic over the cryptographic primitives. However, the Minio server uses the following
|
|
cryptographic primitive implementations:
|
|
- [PRF](#prf): HMAC-SHA-256
|
|
- [AEAD](#aead): AES-256-GCM if the CPU supports AES-NI, ChaCha20-Poly1305 otherwise.
|
|
More specifically AES-256-GCM is only selected for X86-64 CPUs with AES-NI extension.
|
|
|
|
Further any secret key (apart from the KMS-generated ones) is 256 bits long. The KMS-generated keys
|
|
may be 256 bits but this depends on the KMS capabilities and configuration.
|
|
|
|
The *Secure Channel* splits the object content into chunks of a fixed size of `65536` bytes. The last
|
|
chunk may be smaller to avoid adding additional overhead and is treated specially to prevent truncation
|
|
attacks. The nonce value is 96 bits long and generated randomly per object / multi-part part. The
|
|
*Secure Channel* supports plaintexts up to `65536 * 2^32 = 256 TiB`.
|
|
|
|
#### Randomness
|
|
|
|
The Minio server generates unique keys and other cryptographic values using a cryptographically
|
|
secure pseudo-random number generator ([CSPRNG](#csprng)). However, in the context of SSE,
|
|
the Minio server does not require that the CSPRNG generates values that are indistinguishable
|
|
from truly random bit strings. Instead, it is sufficient if the generated values are unique - which
|
|
is a weaker requirement. Nevertheless other parts - for example the TLS-stack - may require that
|
|
CSPRNG-generated values are indistinguishable from truly random bit strings.
|
|
|
|
### Server-Side Encryption with client-provided Keys
|
|
|
|
SSE-C allows an S3 client to en/decrypt an object at the Minio server. Therefore the S3 client
|
|
sends a secret key as part of the HTTP request. This secret key is **never** stored by the
|
|
Minio server and only resides in RAM during the en/decryption process.
|
|
|
|
Minio does not assume or require that the client-provided key is unique. It may be used for
|
|
multiple objects or buckets. Especially a single client-provided key may be used for all
|
|
objects - even though all objects must be treated as compromised if that key is ever compromised.
|
|
|
|
#### Key rotation
|
|
|
|
S3 clients can change the client-provided key of an existing object. Therefore an S3 client
|
|
must perform a S3 COPY operation where the copy source and destination are equal. Further the
|
|
COPY request headers must contain the current and the new client key:
|
|
- `X-Amz-Server-Side-Encryption-Customer-Key`: Base64 encoded new key.
|
|
- `X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key`: Base64 encoded current key.
|
|
|
|
Such a special COPY request is also known as S3 SSE-C key rotation.
|
|
|
|
### Server-Side Encryption with a KMS
|
|
|
|
SSE-S3 allows an S3 client to en/decrypt an object at the Minio server using a KMS. The Minio
|
|
server only assumes that the KMS provides two services:
|
|
1. `GenerateKey`: Takes a key ID and generates a new data key from a master key referenced by
|
|
the key ID. It returns the new data key in two different forms: The plain data key
|
|
and the data key encrypted using the master key.
|
|
2. `DecryptKey`: Takes a key ID and an encrypted data key and returns the plain data key - the
|
|
decryption of the encrypted data key using the master key referenced by the key ID -
|
|
on success or an error otherwise.
|
|
|
|
More details about supported KMS implementations and configuration can be found at the [KMS guide](https://github.com/minio/minio/blob/master/docs/kms/README.md).
|
|
|
|
The Minio server requests a new data key from the KMS for each uploaded object and uses that data key
|
|
as EK. Additionally it stores the encrypted form of the data key and the master key ID as part
|
|
of the object metadata. The plain data only resides in RAM during the en/decryption process.
|
|
The Minio server does not store any SSE-related key at the KMS. Instead the KMS is treated as trusted
|
|
component that performs key sealing/unsealing operations to build a key hierarchy:
|
|
|
|
```
|
|
CMK (master key)
|
|
|
|
|
+-----------------------------------+-----------------------------------+
|
|
| | |
|
|
+-------+----------------+ +-------+----------------+ ...
|
|
| EK_1 | EK_1_encrypted | | EK_2 | EK_2_encrypted |
|
|
+---+----------+---------+ +---+----------+---------+
|
|
| | | |
|
|
| | | |
|
|
+---+---+ | +---+---+ |
|
|
| KEK_1 | | | KEK_2 | |
|
|
+---+---+ | +---+---+ |
|
|
| | | |
|
|
| | | |
|
|
+---+---+ | +---+---+ |
|
|
| OEK_1 | | | OEK_2 | |
|
|
+---+---+ | +---+---+ |
|
|
| |
|
|
| |
|
|
| |
|
|
+---------+---------+ +---------+---------+
|
|
| object_metadata_1 | | object_metadata_2 |
|
|
+-------------------+ +-------------------+
|
|
```
|
|
<center>Figure 2 - KMS key hierarchy</center>
|
|
|
|
|
|
#### Key rotation - Basic Operation
|
|
|
|
The Minio server supports key rotation for SSE-S3 encrypted objects. Therefore, an S3 client
|
|
must perform a S3 COPY operation where the copy source and destination are equal and the SSE-S3 HTTP
|
|
header is set. The minio server decrypts the OEK using the current encrypted data key and the
|
|
master key ID of the object metadata. If this succeeds, the server requests a new data key
|
|
from the KMS using the master key ID of the **current Minio KMS configuration** and re-wraps the
|
|
*OEK* with a new *KEK* derived from the new data key / EK:
|
|
|
|
```
|
|
object metadata KMS
|
|
| |
|
|
| +----------------+ 1a | +-------+
|
|
|-------------------->| EK_1_encrypted |-----------|->| CMK_1 |
|
|
| +----------------+ | +---+---+
|
|
| | |
|
|
| +---------------+ +------+ 1b | |
|
|
|------------->| OEK_encrypted | | EK_1 |<---|------+
|
|
| +-------+-------+ +------+ |
|
|
| \ / |
|
|
| \___ 2 ___/ |
|
|
| \___/ |
|
|
| | |
|
|
| +--+--+ |
|
|
| | OEK | | +-------+
|
|
| +--+--+ | | CMK_2 |
|
|
| | | +---+---+
|
|
| | | |
|
|
| 5 +----------------+ |4 +------+ 3a | |
|
|
|<------| OEK_encrypted' |<----+-------| EK_2 |<---|------+
|
|
| +----------------+ +------+ | |
|
|
| +----------------+ 3b | |
|
|
|<-------------------| EK_2_encrypted |<-----------|------+
|
|
| +----------------+ |
|
|
| |
|
|
|
|
|
|
1a) Send encrypted data key and master key ID to KMS.
|
|
1b) Receive decrypted data key.
|
|
2) Decrypt encrypted object key with the KEK derived from the data key.
|
|
3a) Receive new plain data key from the KMS using the master key ID of the server config.
|
|
3b) Receive encrypted form of the data key from the KMS.
|
|
4) Derive a new KEK from the new data key and re-encrypt the OEK with it.
|
|
5) Store the encrypted OEK encrypted data key and master key ID in object metadata.
|
|
```
|
|
<center>Figure 3 - KMS data key rotation</center>
|
|
|
|
#### Key rotation - Extensions
|
|
|
|
The basic SSE-S3 key rotation operation can be used to build more powerful key management
|
|
operations. The following options are possible to perform manually but do not have fully
|
|
functional API's at this time.
|
|
|
|
1. **Master key migration**: The [SSE-S3 key rotation](#Key-rotation---Basic-Operation) can be performed
|
|
on multiple/all objects to move them from one to another master key.
|
|
2. **Secure object erasure**: The [SSE-S3 key rotation](#Key-rotation---Basic-Operation) can be applied
|
|
to one/multiple objects with a randomly generated master key which is
|
|
not stored at the KMS. That leads to an encrypted data key which can
|
|
never be decrypted anymore.
|
|
3. **Periodical key migration**: The [SSE-S3 key rotation](#Key-rotation---Basic-Operation) can be
|
|
invoked after a certain time period to migrate one or more objects
|
|
from one master key to another.
|
|
|
|
#### Secure Erasure and Locking
|
|
|
|
The Minio server requires an available KMS to en/decrypt SSE-S3 encrypted objects. Therefore it
|
|
is possible to erase or lock some or all encrypted objects. For example in case of a detected attack
|
|
or other emergency situations the following actions can be taken:
|
|
- Seal the KMS such that it cannot be accessed by Minio server anymore. That will lock **all**
|
|
SSE-S3 encrypted objects protected by master keys stored on the KMS. All these objects
|
|
can not be decrypted as long as the KMS is sealed.
|
|
- Seal/Unmount one/some master keys. That will lock all SSE-S3 encrypted objects protected by
|
|
these master keys. All these objects can not be decrypted as long as the key(s) are sealed.
|
|
- Delete one/some master keys. From a security standpoint, this is equal to erasing all SSE-S3
|
|
encrypted objects protected by these master keys. All these objects are lost forever as they cannot
|
|
be decrypted. Especially deleting all master keys at the KMS is equivalent to secure erasing all
|
|
SSE-S3 encrypted objects.
|
|
|
|
## Acronyms
|
|
|
|
- <a name="aead"></a>**AEAD**: Authenticated Encryption with Associated Data
|
|
- <a name="csprng"></a>**CSPRNG**: Cryptographically Secure Pseudo Random Number Generator
|
|
- <a name="ek"></a>**EK**: External Key
|
|
- <a name="iv"></a>**IV**: Initialization Vector
|
|
- <a name="kek"></a>**KEK**: Key Encryption Key
|
|
- <a name="oek"></a>**OEK**: Object Encryption Key
|
|
- <a name="prf"></a>**PRF**: Pseudo Random Function
|
|
- <a name="sse"></a>**SSE**: Server-Side Encryption
|
|
- <a name="ssec"></a>**SSE-C**: Server-Side Encryption with client-provided Keys
|
|
- <a name="sses3"></a>**SSE-S3**: Server-Side Encryption with a KMS
|