Skip to content

Commit eca3b4f

Browse files
authored
Merge pull request #1264 from cipherstash/feat/remove-ffivalue
refactor(stack-encrypt)!: remove FfiValue
2 parents abc15c9 + 63d2183 commit eca3b4f

34 files changed

Lines changed: 2205 additions & 2416 deletions

‎.github/dependabot.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ updates:
299299
- dependency-name: "recipher"
300300
- dependency-name: "cllw-ore"
301301
# vitaminc is pinned at 0.5.0 across the crates and the guests; the
302-
# guests' `FfiValue: Decrypt` bound fails if two versions resolve.
302+
# guests' `ValueFrame: Decrypt` bound fails if two versions resolve.
303303
- dependency-name: "vitaminc*"
304304
# Major bumps are reviewed and applied manually, not by Dependabot.
305305
- dependency-name: "*"

‎languages/golang/encrypt/guest/Cargo.lock‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎languages/golang/encrypt/guest/Cargo.toml‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ zerokms-protocol = "=0.12.31"
6464
# The transport frame and ciphertext codec, shared with vitaminc's own
6565
# guest — one codec, not a fork. Same vitaminc version stack-encrypt builds
6666
# against (see the comment in packages/stack-encrypt/Cargo.toml). A
67-
# different version here fails at the `FfiValue: Decrypt` bound, since the
67+
# different version here fails at the `ValueFrame: Decrypt` bound, since the
6868
# aead crate would be duplicated.
6969
vitaminc-aead-frame = { version = "0.6.1", features = ["serde"] }
7070
vitaminc-protected = "0.6.1"
@@ -76,6 +76,8 @@ uuid = "1"
7676
zeroize = "1"
7777

7878
[dev-dependencies]
79+
# The test tree (`tests/support`) walks values through vitaminc's sinks.
80+
vitaminc-aead = "0.6.1"
7981
stack-kms = { path = "../../../../packages/stack-kms", default-features = false, features = ["test-support"] }
8082
# For minting a valid client-key fixture in the config tests.
8183
recipher = "=0.3.1"

‎languages/golang/encrypt/guest/src/lib.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ pub mod ops;
9595
pub mod options;
9696
pub mod response;
9797
pub mod status;
98+
#[cfg(test)]
99+
#[path = "../tests/support/mod.rs"]
100+
mod support;
98101
pub mod targets;
99102
#[cfg(test)]
100103
mod test_frame;

‎languages/golang/encrypt/guest/src/ops.rs‎

Lines changed: 28 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -653,8 +653,8 @@ fn tree_encoded_len(tree: &BytesTree) -> Option<usize> {
653653
#[cfg(test)]
654654
mod tests {
655655
use super::*;
656-
use stack_encrypt::dynamic::FfiValue;
657-
use vitaminc_protected::{Controlled, Protected};
656+
use crate::support::Tree;
657+
use vitaminc_protected::Controlled;
658658

659659
// `tree_encoded_len` re-derives the codec's arithmetic; the codec exports
660660
// no `encoded_len` of its own, so this pin is the only thing that fails if
@@ -699,7 +699,7 @@ mod tests {
699699
),
700700
]),
701701
CipherText::Passthrough(stack_encrypt::PassthroughFrame(
702-
FfiValue::Bytes(Protected::new(vec![1, 2, 3]))
702+
Tree::Bytes(vec![1, 2, 3])
703703
.to_frame()
704704
.expect("a frame")
705705
.risky_unwrap(),
@@ -715,16 +715,16 @@ mod tests {
715715

716716
// ---- the EQL target path, native, over FakeDataKeySource -----------------
717717

718-
fn encoded(value: FfiValue) -> Vec<u8> {
718+
fn encoded(value: Tree) -> Vec<u8> {
719719
value.to_frame().expect("encode").risky_unwrap()
720720
}
721721

722-
fn text(value: &str) -> FfiValue {
723-
FfiValue::String(value.into())
722+
fn text(value: &str) -> Tree {
723+
Tree::String(value.into())
724724
}
725725

726-
fn object(entries: Vec<(&str, FfiValue)>) -> FfiValue {
727-
FfiValue::Object(
726+
fn object(entries: Vec<(&str, Tree)>) -> Tree {
727+
Tree::Object(
728728
entries
729729
.into_iter()
730730
.map(|(k, v)| (k.to_string(), v))
@@ -739,22 +739,16 @@ mod tests {
739739
(
740740
"email",
741741
object(vec![
742-
(
743-
"context",
744-
FfiValue::Array(vec![text("users"), text("email")]),
745-
),
742+
("context", Tree::Array(vec![text("users"), text("email")])),
746743
("target", text("TextEq")),
747744
("type", text("string")),
748745
]),
749746
),
750747
(
751748
"notes",
752749
object(vec![
753-
(
754-
"context",
755-
FfiValue::Array(vec![text("users"), text("notes")]),
756-
),
757-
("outputs", FfiValue::Array(vec![text("c")])),
750+
("context", Tree::Array(vec![text("users"), text("notes")])),
751+
("outputs", Tree::Array(vec![text("c")])),
758752
("type", text("string")),
759753
]),
760754
),
@@ -764,22 +758,22 @@ mod tests {
764758
#[test]
765759
fn targets_lists_what_this_build_holds_in_the_wire_shape() {
766760
let out = targets().expect("encodes");
767-
let FfiValue::Object(entries) = FfiValue::from_frame(&out).expect("decodes") else {
761+
let Tree::Object(entries) = Tree::from_frame(&out).expect("decodes") else {
768762
panic!("an object");
769763
};
770764
assert_eq!(entries.len(), 1);
771765
assert_eq!(entries[0].0, "targets");
772-
let FfiValue::Array(items) = &entries[0].1 else {
766+
let Tree::Array(items) = &entries[0].1 else {
773767
panic!("a list");
774768
};
775769
if !crate::targets::HOLDS_EQL {
776770
assert!(items.is_empty(), "the build without EQL types lists none");
777771
return;
778772
}
779-
let FfiValue::Object(text_eq) = items
773+
let Tree::Object(text_eq) = items
780774
.iter()
781775
.find(|item| {
782-
matches!(item, FfiValue::Object(fields) if fields.iter().any(|(k, v)| k == "name" && matches!(v, FfiValue::String(s) if s.risky_ref() == b"TextEq")))
776+
matches!(item, Tree::Object(fields) if fields.iter().any(|(k, v)| k == "name" && matches!(v, Tree::String(s) if s.as_bytes() == b"TextEq")))
783777
})
784778
.expect("TextEq is listed")
785779
else {
@@ -802,9 +796,9 @@ mod tests {
802796
"operators"
803797
]
804798
);
805-
assert!(matches!(text_eq[8].1, FfiValue::Bool(true)));
799+
assert!(matches!(text_eq[8].1, Tree::Bool(true)));
806800
assert!(
807-
matches!(&text_eq[10].1, FfiValue::Array(operators) if operators.len() == 1),
801+
matches!(&text_eq[10].1, Tree::Array(operators) if operators.len() == 1),
808802
"TextEq answers one operator"
809803
);
810804
}
@@ -820,10 +814,7 @@ mod tests {
820814
let refused = encoded(object(vec![(
821815
"email",
822816
object(vec![
823-
(
824-
"context",
825-
FfiValue::Array(vec![text("users"), text("email")]),
826-
),
817+
("context", Tree::Array(vec![text("users"), text("email")])),
827818
("target", text(name)),
828819
]),
829820
)]));
@@ -874,10 +865,10 @@ mod tests {
874865
let CipherText::Passthrough(payload) = &outputs[0].1 else {
875866
panic!("a passthrough")
876867
};
877-
let FfiValue::Bytes(bytes) = FfiValue::from_frame(&payload.0).expect("a value") else {
868+
let Tree::Bytes(bytes) = Tree::from_frame(&payload.0).expect("a value") else {
878869
panic!("bytes")
879870
};
880-
let eql: serde_json::Value = serde_json::from_slice(bytes.risky_ref()).expect("JSON");
871+
let eql: serde_json::Value = serde_json::from_slice(bytes.as_slice()).expect("JSON");
881872
assert_eq!(eql["v"], 4, "Stack Encrypt writes EQL v4");
882873
assert_eq!(eql["i"], serde_json::json!({"t": "users", "c": "email"}));
883874
assert!(eql["c"].as_str().unwrap().starts_with("stack-encrypt:1:"));
@@ -887,30 +878,26 @@ mod tests {
887878
validate::record_tree(&sealed, &plan, None).expect("the tree fits");
888879
let opened =
889880
block_on(decrypt_record(Scope::Client(&cipher), &sealed, &plan, None)).expect("opens");
890-
let FfiValue::Object(values) = FfiValue::from_frame(&opened).expect("a value") else {
881+
let Tree::Object(values) = Tree::from_frame(&opened).expect("a value") else {
891882
panic!("an object")
892883
};
893-
assert!(
894-
matches!(&values[0].1, FfiValue::String(s) if s.risky_ref() == b"alice@example.com")
895-
);
896-
assert!(matches!(&values[1].1, FfiValue::String(s) if s.risky_ref() == b"likes cats"));
884+
assert!(matches!(&values[0].1, Tree::String(s) if s.as_bytes() == b"alice@example.com"));
885+
assert!(matches!(&values[1].1, Tree::String(s) if s.as_bytes() == b"likes cats"));
897886

898887
// The eq query's term is the type's query value, and it matches the
899888
// stored term.
900889
let probe = encoded(text("alice@example.com"));
901890
validate::query_term(&probe, &plan, b"email", b"eq").expect("a target field answers eq");
902891
let term = block_on(query_term(&keyset, &probe, &plan, b"email", b"eq")).expect("derives");
903-
let FfiValue::Object(term) = FfiValue::from_frame(&term).expect("a value") else {
892+
let Tree::Object(term) = Tree::from_frame(&term).expect("a value") else {
904893
panic!("a query term is an object")
905894
};
906-
assert!(
907-
matches!(&term[3].1, FfiValue::String(s) if s.risky_ref() == b"eql_v4.query_text_eq")
908-
);
909-
let FfiValue::Bytes(query_bytes) = &term[4].1 else {
895+
assert!(matches!(&term[3].1, Tree::String(s) if s.as_bytes() == b"eql_v4.query_text_eq"));
896+
let Tree::Bytes(query_bytes) = &term[4].1 else {
910897
panic!("the term is the query value's bytes")
911898
};
912899
let probe_json: serde_json::Value =
913-
serde_json::from_slice(query_bytes.risky_ref()).expect("JSON");
900+
serde_json::from_slice(query_bytes.as_slice()).expect("JSON");
914901
assert_eq!(
915902
probe_json["hm"], eql["hm"],
916903
"the query matches the stored value"
@@ -930,7 +917,7 @@ mod tests {
930917
Err(STATUS_ENCODING)
931918
);
932919
assert_eq!(
933-
validate::query_term(&encoded(FfiValue::UInt32(1)), &plan, b"email", b"eq"),
920+
validate::query_term(&encoded(Tree::UInt32(1)), &plan, b"email", b"eq"),
934921
Err(STATUS_ENCODING)
935922
);
936923
}

0 commit comments

Comments
 (0)