cipherstash-client 0.12.5

The official CipherStash SDK
Documentation
use lazy_static::lazy_static;

use cipherstash_client::{
    config::{
        console_config::ConsoleConfig, cts_config::CtsConfig, zero_kms_config::ZeroKMSConfig,
    },
    credentials::service_credentials::ServiceCredentials,
    zerokms::{ClientKey, DatasetConfig, EncryptPayload, ZeroKMS},
};

use recipher::keyset::ProxyKeySet;
use zerokms_protocol::{cipherstash_config::list::UniqueList, Dataset};

fn create_zero_kms_config() -> &'static ZeroKMSConfig {
    lazy_static! {
        static ref ZERO_KMS_CONFIG: ZeroKMSConfig = {
            let console_config = ConsoleConfig::builder()
                .with_env()
                .build()
                .expect("failed to build console config");

            let cts_config = CtsConfig::builder()
                .with_env()
                .build()
                .expect("failed to build idp config");

            ZeroKMSConfig::builder()
                .console_config(&console_config)
                .cts_config(&cts_config)
                .with_env()
                .base_url(
                    &std::env::var("CS_TEST_VITUR_HOST")
                        .expect("var CS_TEST_VITUR_HOST should be set with path to dev server"),
                )
                .build()
                .expect("failed to build zero_kms config")
        };
    }

    &ZERO_KMS_CONFIG
}

fn create_base_zero_kms_client() -> ZeroKMS<ServiceCredentials> {
    let config = create_zero_kms_config();
    config.create_client()
}

async fn create_full_zero_kms_client() -> (Dataset, ZeroKMS<ServiceCredentials, ClientKey>) {
    let config = create_zero_kms_config();

    let zero_kms = config.create_client();

    let dataset = zero_kms
        .create_dataset("test-dataset", "test-description")
        .await
        .expect("failed to create dataset for full zero_kms client");

    let client = zero_kms
        .create_client("test-client", "test-description", dataset.id)
        .await
        .expect("failed to create client for full zero_kms client");

    let config = config.clone_with_client_key(ClientKey {
        key_id: client.id,
        keyset: ProxyKeySet::from_bytes(&client.client_key[..])
            .expect("failed to create proxy key set from client response"),
    });

    let zero_kms = config.create_client();

    (dataset, zero_kms)
}

#[tokio::test]
#[ignore = "e2e"]
async fn create_dataset() {
    let client = create_base_zero_kms_client();

    let dataset = client
        .create_dataset("new dataset", "new dataset description")
        .await
        .expect("failed to create dataset");

    assert_eq!(dataset.description, "new dataset description");
    assert_eq!(dataset.name, "new dataset");
}

#[tokio::test]
#[ignore = "e2e"]
async fn list_datasets() {
    let client = create_base_zero_kms_client();

    let dataset = client
        .create_dataset("dataset to list", "dataset to list description")
        .await
        .expect("failed to create dataset");

    let retrieved_dataset = client
        .list_datasets()
        .await
        .expect("failed to list datasets")
        .into_iter()
        .find(|x| x.id == dataset.id)
        .expect("could not find dataset in listed output");

    assert_eq!(dataset.description, retrieved_dataset.description);
    assert_eq!(dataset.name, retrieved_dataset.name);
}

#[tokio::test]
#[ignore = "e2e"]
async fn modify_dataset() {
    let client = create_base_zero_kms_client();

    let dataset = client
        .create_dataset("dataset to modify", "dataset to modify description")
        .await
        .expect("failed to create dataset");

    client
        .modify_dataset(
            dataset.id,
            Some("modified dataset name"),
            Some("modified dataset description"),
        )
        .await
        .expect("failed to modify dataset");

    let retrieved_dataset = client
        .list_datasets()
        .await
        .expect("failed to list datasets")
        .into_iter()
        .find(|x| x.id == dataset.id)
        .expect("could not find modified dataset in listed output");

    assert_eq!(
        retrieved_dataset.description,
        "modified dataset description"
    );
    assert_eq!(retrieved_dataset.name, "modified dataset name");
}

#[tokio::test]
#[ignore = "e2e"]
async fn create_client() {
    let zero_kms = create_base_zero_kms_client();

    let dataset = zero_kms
        .create_dataset("dataset for client", "dataset for client description")
        .await
        .expect("failed to create dataset");

    let client = zero_kms
        .create_client("created client", "created client description", dataset.id)
        .await
        .expect("failed to create client");

    assert_eq!(client.name, "created client");
    assert_eq!(client.description, "created client description");
}

#[tokio::test]
#[ignore = "e2e"]
async fn list_clients() {
    let zero_kms = create_base_zero_kms_client();

    let dataset = zero_kms
        .create_dataset("dataset for client", "dataset for client description")
        .await
        .expect("failed to create dataset");

    let client = zero_kms
        .create_client("client to list", "client to list description", dataset.id)
        .await
        .expect("failed to create client");

    let retrieved_client = zero_kms
        .list_clients()
        .await
        .expect("failed to list clients")
        .into_iter()
        .find(|x| x.id == client.id)
        .expect("failed to find client in list output");

    assert_eq!(retrieved_client.name, client.name);
    assert_eq!(retrieved_client.description, client.description);
}

#[tokio::test]
#[ignore = "e2e"]
async fn save_config() {
    let (_, client) = create_full_zero_kms_client().await;

    client
        .save_dataset_config(DatasetConfig {
            tables: UniqueList::new(),
        })
        .await
        .expect("failed to save dataset config");
}

#[tokio::test]
#[ignore = "e2e"]
async fn load_config() {
    let (_, client) = create_full_zero_kms_client().await;

    client
        .save_dataset_config(DatasetConfig {
            tables: UniqueList::new(),
        })
        .await
        .expect("failed to save dataset config");

    let config = client
        .load_dataset_config()
        .await
        .expect("failed to load dataset config");

    assert_ne!(config.index_root_key, [0_u8; 32]);
}

#[tokio::test]
#[ignore = "e2e"]
async fn encrypt_record() {
    let (_, client) = create_full_zero_kms_client().await;

    let _ = client
        .encrypt_single(
            EncryptPayload {
                msg: b"message to encrypt",
                descriptor: "test-descriptor",
            },
            None,
        )
        .await
        .expect("failed to encrypt record");
}

#[tokio::test]
#[ignore = "e2e"]
async fn decrypt_record() {
    let (_, client) = create_full_zero_kms_client().await;

    let record = client
        .encrypt_single(
            EncryptPayload {
                msg: b"message to decrypt",
                descriptor: "test-descriptor",
            },
            None,
        )
        .await
        .expect("failed to encrypt record");

    let message = client
        .decrypt_single(record)
        .await
        .expect("failed to decrypt record");

    assert_eq!(message, b"message to decrypt")
}

#[tokio::test]
#[ignore = "e2e"]
async fn disable_dataset() {
    let (dataset, client) = create_full_zero_kms_client().await;

    client
        .disable_dataset(dataset.id)
        .await
        .expect("failed to disable dataset");
}

#[tokio::test]
#[ignore = "e2e"]
async fn encrypt_with_disabled_dataset() {
    let (dataset, client) = create_full_zero_kms_client().await;

    client
        .disable_dataset(dataset.id)
        .await
        .expect("failed to disable dataset");

    let _ = client
        .encrypt_single(
            EncryptPayload {
                msg: b"message to fail to encrypt",
                descriptor: "test-descriptor",
            },
            None,
        )
        .await
        .expect_err("encryption should fail against disabled dataset");
}

#[tokio::test]
#[ignore = "e2e"]
async fn decrypt_with_disabled_dataset() {
    let (dataset, client) = create_full_zero_kms_client().await;

    let record = client
        .encrypt_single(
            EncryptPayload {
                msg: b"message to fail to decrypt",
                descriptor: "test-descriptor",
            },
            None,
        )
        .await
        .expect("failed to encrypt record");

    client
        .disable_dataset(dataset.id)
        .await
        .expect("failed to disable dataset");

    let _ = client
        .decrypt_single(record)
        .await
        .expect_err("decryption should fail against disabled dataset");
}

#[tokio::test]
#[ignore = "e2e"]
async fn enable_dataset() {
    let (dataset, client) = create_full_zero_kms_client().await;

    client
        .disable_dataset(dataset.id)
        .await
        .expect("failed to disable dataset");

    client
        .enable_dataset(dataset.id)
        .await
        .expect("failed to enable dataset");
}

#[tokio::test]
#[ignore = "e2e"]
async fn encrypt_record_reenabled() {
    let (dataset, client) = create_full_zero_kms_client().await;

    client
        .disable_dataset(dataset.id)
        .await
        .expect("failed to disable dataset");

    client
        .enable_dataset(dataset.id)
        .await
        .expect("failed to enable dataset");

    let _ = client
        .encrypt_single(
            EncryptPayload {
                msg: b"message to encrypt",
                descriptor: "test-descriptor",
            },
            None,
        )
        .await
        .expect("failed to encrypt record");
}

#[tokio::test]
#[ignore = "e2e"]
async fn decrypt_record_reenabled() {
    let (dataset, client) = create_full_zero_kms_client().await;

    client
        .disable_dataset(dataset.id)
        .await
        .expect("failed to disable dataset");

    client
        .enable_dataset(dataset.id)
        .await
        .expect("failed to enable dataset");

    let record = client
        .encrypt_single(
            EncryptPayload {
                msg: b"message to decrypt after re-enabling",
                descriptor: "test-descriptor",
            },
            None,
        )
        .await
        .expect("failed to encrypt record");

    let message = client
        .decrypt_single(record)
        .await
        .expect("failed to decrypt record");

    assert_eq!(message, b"message to decrypt after re-enabling")
}