The Split-Trust Encryption Tool (STET) allows users to encrypt and decrypt data while splitting trust between multiple Key Management Systems and providing environment attestations to aid in key release policy decisions.
STET can be built manually using either Go's native toolchain (e.g. go get
and go build
commands), or via Bazel.
-
Install
make
andcurl
viaapt-get install build-essential curl
. -
Install Go plugins for the protocol compiler.
- Specifically, run
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26
andgo install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1
, then add$(go env GOPATH)/bin
to yourPATH
.
- Specifically, run
-
From the root of this source repository, run
make all
to vendor.proto
files and compilepb.go
sources. -
Build STET via
go build github.com/GoogleCloudPlatform/stet/cmd/stet
. -
Copy the resulting binary to an executable location, granting it
suid
permissions (root permissions are required to generate attestations in a Confidential VM):$ sudo mv stet /usr/local/bin $ sudo chown root /usr/local/bin/stet $ sudo chmod u+sx,a+rs /usr/local/bin/stet
-
From the root of this source repository, run
bazel build cmd/stet:main
- This will download all necessary dependencies
- The resulting binary will be found at
bazel-bin/cmd/stet/main_/main
-
Copy the resulting binary to an executable location, granting it
suid
permissions (root permissions are required to generate attestations in a Confidential VM):$ sudo mv bazel-bin/cmd/stet/main_/main /usr/local/bin $ sudo chown root /usr/local/bin/stet $ sudo chmod u+sx,a+rx /usr/local/bin/stet
Alternatively, prebuilt binaries are automatically compiled and bundled with
releases of STET, which can be found on the
Releases page. As with
the above instructions, the binary should be copied to an executable location
and granted suid
positions as well:
$ tar -zxf stet_1.0.0_linux_x86_64.tar.gz
$ sudo mv stet /usr/local/bin
$ sudo chown root /usr/local/bin/stet
$ sudo chmod u+sx,a+rx /usr/local/bin/stet
STET configurations are written in YAML.
A key_config
provides information about the encryption and decryption process.
It includes the fields listed below.
One or more kek_infos
fields must be defined, indicating all the KEKs to use
in encryption and decryption for that configuration. Each kek_info
contains
the Google Cloud KMS URI of a KEK.
The number of kek_infos
in a configuration must match the number of shares the
DEK will be split into, as indicated by the Key Splitting Algorithm (see below).
The DEK Algorithm indicates the algorithm used to generate the DEK.
Currently, only AES256-GCM is supported.
This indicates the number of shares the split the DEK into - if at all. It requires exactly one of the following fields to be defined:
no_split: true
indicates the DEK will not be split into any shares.- The
key_config
should contain a singlekek_infos
indicating the KEK to encrypt/decrypt the entire DEK with
- The
shamir
indicates the DEK will be split into multiple shares using Shamir's Secret Sharing. Each share will be encrypted/decrypted with a separate KEK. This configuration requires two fields to be defined:shares
defines the total number of shares to split the DEK into.threshold
defines the minimum number of shares needed to reconstitute the DEK during decryption.- The
key_config
should contain a number ofkek_infos
equal to the values ofshares
key_config
using a single KMS (no split):
key_config:
kek_infos:
- kek_uri: "gcp-kms://projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key"
dek_algorithm: AES256_GCM
no_split: true
key_config
using Shamir's Secret Sharing:
key_config:
kek_infos:
- kek_uri: "gcp-kms://projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key-1"
- kek_uri: "gcp-kms://projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key-2"
dek_algorithm: AES256_GCM
shamir:
shares: 2
threshold: 2
A configuration file for STET consists of two configurations: one for encryption and one for decryption.
An encrypt_config
contains a single key_config
with information about the
key and scheme to encrypt with.
A decrypt_config
contains all the key_configs
known to the client, one of
which should match the key_config
the ciphertext was encrypted with. During
decryption, the STET will search through the provided key_configs
to find the
matching one.
Note that the encryption and decryption configurations could be identical if the
key_config
used in encryption is the only known to the client. However, they
must still be defined separately in the configuration file.
Example of a STET configuration file:
encrypt_config:
key_config:
kek_infos:
- kek_uri: "gcp-kms://projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key-1"
- kek_uri: "gcp-kms://projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key-2"
dek_algorithm: AES256_GCM
shamir:
shares: 2
threshold: 2
decrypt_config:
key_configs:
- kek_infos: # Note the extra hyphen here, because key_configs is a repeated field.
- kek_uri: "gcp-kms://projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key0"
dek_algorithm: AES256_GCM
no_split: true
key_configs:
- kek_infos: # Note the extra hyphen here, because key_configs is a repeated field.
- kek_uri: "gcp-kms://projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key-1"
- kek_uri: "gcp-kms://projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key-2"
dek_algorithm: AES256_GCM
shamir:
shares: 2
threshold: 2
Another method of wrapping shares is to generate an asymmetric keypair locally. In your STET configuration file, you can specify the fingerprint of the keypair rather than the URI of a KMS-stored key and add another stanza describing the path to the public/private keys on disk.
Generate a private, 2048-bit RSA key:
$ openssl genrsa -out my_key.pem 2048
Create a public key file from that private key:
$ openssl rsa -in my_key.pem -pubout -out my_key.pub
Compute the fingerprint of the keypair:
$ openssl rsa -in stet.pem -pubout -outform DER | openssl sha256 -binary | openssl base64
Here is an example config file that encrypts data using a single RSA key:
encrypt_config:
key_config:
kek_infos:
- rsa_fingerprint: "0FkfGnh4KEUBJGLoLUEcIIFTdlcx61ec1M/H2Gdh7tY="
dek_algorithm: AES256_GCM
no_split: true
decrypt_config:
key_configs:
- kek_infos:
- rsa_fingerprint: "0FkfGnh4KEUBJGLoLUEcIIFTdlcx61ec1M/H2Gdh7tY="
dek_algorithm: AES256_GCM
no_split: true
asymmetric_keys:
public_key_files:
- "/home/me/stet.pub"
private_key_files:
- "/home/me/stet.pem"
Example invocations:
$ stet encrypt --config-file=~/stet.cfg ./plaintext.txt ./ciphertext.dat
$ stet decrypt --config-file=~/stet.cfg ./ciphertext.dat ./plaintext2.txt
In place of providing input and output files, stdin and stdout can be used for
input and output respectively by using -
in place of the corresponding file in
the command.
When stdout is used as the output destination, logging from STET is written to stderr to avoid interference with the outputted plaintext or ciphertext.
Using stdin as input:
$ some-process | stet encrypt - ./ciphertext.dat
$ some-process | stet decrypt - ./plaintext.txt
Using stdout as output:
$ stet encrypt ./plaintext.txt - | some-process
$ stet decrypt ./ciphertext.dat - | some-process
Using both stdin as input and stdout as output:
$ some-process | stet encrypt - - | some-other-process
$ some-process | stet decrypt - - | some-other-process
When encrypting and decrypting data using STET, it is important to validate the blob ID and key URIs used. STET will output the blob ID stored in the metadata of the encrypted file, as well as the key URI of the key actually used (e.g. the specific external key URI when using an external key management system):
Blob ID of decrypted data: fa0fc0a8-b7b3-4ae2-bcc8-0aa862d1ff61
Used these key URIs: [https://my-ekm.io/keys/12345]
Checking the key URI used during encryption prevents accidental or malicious misconfiguration resulting in the incorrect (with a potentially less restrictive decryption policy) encryption key.
If the blob ID was set to a known value (e.g. the location of the file when uploaded to a bucket), ensuring a match at decryption time confirms that the data was not swapped out with another piece of data unknowingly.
STET is also integrated with
gsutil
5.0 and higher.
Instead of invoking STET manually, if gsutil
can find STET in your $PATH
and
the --stet
flag is passed, file uploads will be encrypted via STET (and the
blob ID is set to the final upload location), and file downloads will be
decrypted via STET before being written to disk.
$ gsutil cp --stet secrets.txt "gs://my-bucket/my-secrets"
$ gsutil cp --stet "gs://my-bucket/my-secrets" plaintext.txt
The stet_binary_path
and stet_config_path
variables can be set in your
boto configuration file to
override the default path and config file lookups.
To get started using STET with key management systems, see our Quickstart Guide.
For a more in-depth look at the types of workflows that are possible with STET, see our Workflows document.
The Advanced Configuration document explains the different aspects of the key configuration files used to customize the way STET encrypts and decrypts your data.
The Redundancy document provides an overview of utilizing advanced STET configuration options to encrypt data more resiliently.
STET is not an officially supported Google product.
STET is released under the Apache 2.0 license.
Copyright 2021 Google LLC
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.