Skip to content

Commit

Permalink
Merge pull request #7 from cipherstash/sol-103/add-service-token-as-e…
Browse files Browse the repository at this point in the history
…ncrypt-decrypt-opt

Add service token as encrypt and decrypt opt
  • Loading branch information
CDThomas authored Jan 8, 2025
2 parents 0a42eff + c0a5eef commit b67c270
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 8 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/jseql-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ crate-type = ["cdylib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
cipherstash-client = "0.15.0"
cipherstash-client = "0.16.0"
neon = "1"
once_cell = "1.20.2"
thiserror = "2.0.8"
Expand Down
37 changes: 32 additions & 5 deletions crates/jseql-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use cipherstash_client::{
console_config::ConsoleConfig, cts_config::CtsConfig, errors::ConfigError,
zero_kms_config::ZeroKMSConfig,
},
credentials::ServiceCredentials,
credentials::{ServiceCredentials, ServiceToken},
encryption::{
Encrypted, EncryptionError, Plaintext, PlaintextTarget, ReferencedPendingPipeline,
ScopedCipher, TypeParseError,
Expand Down Expand Up @@ -95,6 +95,7 @@ fn encrypt(mut cx: FunctionContext) -> JsResult<JsPromise> {
let plaintext = cx.argument::<JsString>(1)?.value(&mut cx);
let column_name = cx.argument::<JsString>(2)?.value(&mut cx);
let lock_context = encryption_context_from_js_value(cx.argument_opt(3), &mut cx)?;
let service_token = service_token_from_js_value(cx.argument_opt(4), &mut cx)?;

let rt = runtime(&mut cx)?;
let channel = cx.channel();
Expand All @@ -110,7 +111,8 @@ fn encrypt(mut cx: FunctionContext) -> JsResult<JsPromise> {
//
// This task will _not_ block the JavaScript main thread.
rt.spawn(async move {
let ciphertext_result = encrypt_inner(client, plaintext, column_name, lock_context).await;
let ciphertext_result =
encrypt_inner(client, plaintext, column_name, lock_context, service_token).await;

// Settle the promise from the result of a closure. JavaScript exceptions
// will be converted to a Promise rejection.
Expand All @@ -133,6 +135,7 @@ async fn encrypt_inner(
plaintext: String,
column_name: String,
encryption_context: Vec<zerokms::Context>,
service_token: Option<ServiceToken>,
) -> Result<String, Error> {
let column_config = ColumnConfig::build(column_name);
let mut pipeline = ReferencedPendingPipeline::new(client.cipher);
Expand All @@ -141,7 +144,7 @@ async fn encrypt_inner(

pipeline.add_with_ref::<PlaintextTarget>(encryptable, 0)?;

let mut source_encrypted = pipeline.encrypt().await?;
let mut source_encrypted = pipeline.encrypt(service_token).await?;

let encrypted = source_encrypted.remove(0).ok_or_else(|| {
Error::InvariantViolation(
Expand All @@ -166,14 +169,15 @@ fn decrypt(mut cx: FunctionContext) -> JsResult<JsPromise> {
let client = (&**cx.argument::<JsBox<Client>>(0)?).clone();
let ciphertext = cx.argument::<JsString>(1)?.value(&mut cx);
let lock_context = encryption_context_from_js_value(cx.argument_opt(2), &mut cx)?;
let service_token = service_token_from_js_value(cx.argument_opt(3), &mut cx)?;

let rt = runtime(&mut cx)?;
let channel = cx.channel();

let (deferred, promise) = cx.promise();

rt.spawn(async move {
let decrypt_result = decrypt_inner(client, ciphertext, lock_context).await;
let decrypt_result = decrypt_inner(client, ciphertext, lock_context, service_token).await;

deferred.settle_with(&channel, move |mut cx| {
let plaintext = decrypt_result.or_else(|err| cx.throw_error(err.to_string()))?;
Expand All @@ -189,6 +193,7 @@ async fn decrypt_inner(
client: Client,
ciphertext: String,
encryption_context: Vec<zerokms::Context>,
service_token: Option<ServiceToken>,
) -> Result<String, Error> {
let encrypted_record = EncryptedRecord::from_mp_base85(&ciphertext)
// The error type from `to_mp_base85` isn't public, so we don't derive an error for this one.
Expand All @@ -200,7 +205,10 @@ async fn decrypt_inner(
context: encryption_context,
};

let decrypted = client.zerokms.decrypt_single(with_context).await?;
let decrypted = client
.zerokms
.decrypt_single(with_context, service_token)
.await?;

let plaintext = Plaintext::from_slice(&decrypted[..])?;

Expand Down Expand Up @@ -239,6 +247,25 @@ fn encryption_context_from_js_value(
Ok(encryption_context)
}

fn service_token_from_js_value(
value: Option<Handle<JsValue>>,
cx: &mut FunctionContext,
) -> NeonResult<Option<ServiceToken>> {
if let Some(service_token) = value {
let service_token: Handle<JsObject> = service_token.downcast_or_throw(cx)?;

let token = service_token
.get::<JsString, _, _>(cx, "accessToken")?
.value(cx);

let expiry = service_token.get::<JsNumber, _, _>(cx, "expiry")?.value(cx);

Ok(Some(ServiceToken::new(token, expiry as u64)))
} else {
Ok(None)
}
}

#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("newClient", new_client)?;
Expand Down

0 comments on commit b67c270

Please sign in to comment.