diff --git a/AwsEncryptionSDK/Makefile b/AwsEncryptionSDK/Makefile index 8cde280e2..69c9b7dd1 100644 --- a/AwsEncryptionSDK/Makefile +++ b/AwsEncryptionSDK/Makefile @@ -9,6 +9,28 @@ DIR_STRUCTURE_V2=V2 PROJECT_SERVICES := \ AwsEncryptionSdk \ +MAIN_SERVICE_FOR_RUST := AwsEncryptionSdk + +RUST_OTHER_FILES := \ + runtimes/rust/src/aes_gcm.rs \ + runtimes/rust/src/aes_kdf_ctr.rs \ + runtimes/rust/src/ddb.rs \ + runtimes/rust/src/concurrent_call.rs \ + runtimes/rust/src/dafny_libraries.rs \ + runtimes/rust/src/digest.rs \ + runtimes/rust/src/ecdh.rs \ + runtimes/rust/src/ecdsa.rs \ + runtimes/rust/src/hmac.rs \ + runtimes/rust/src/kms.rs \ + runtimes/rust/src/local_cmc.rs \ + runtimes/rust/src/random.rs \ + runtimes/rust/src/rsa.rs \ + runtimes/rust/src/sets.rs \ + runtimes/rust/src/software_externs.rs \ + runtimes/rust/src/storm_tracker.rs \ + runtimes/rust/src/time.rs \ + runtimes/rust/src/uuid.rs + SERVICE_NAMESPACE_AwsEncryptionSdk=aws.cryptography.encryptionSdk MAX_RESOURCE_COUNT=90000000 # Order is important diff --git a/AwsEncryptionSDK/runtimes/rust/.gitignore b/AwsEncryptionSDK/runtimes/rust/.gitignore new file mode 100644 index 000000000..a9d37c560 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/AwsEncryptionSDK/runtimes/rust/Cargo.toml b/AwsEncryptionSDK/runtimes/rust/Cargo.toml new file mode 100644 index 000000000..636df0139 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "aws-esdk" +version = "0.1.0" +edition = "2021" +rust-version = "1.80.0" +keywords = ["crypto", "cryptography", "security", "dynamodb", "ddb", "encryption", "client-side", "clientside"] +license = "ISC AND (Apache-2.0 OR ISC)" +description = "aws-esdk is a library for implementing client side encryption." +homepage = "https://github.com/aws/aws-encryption-sdk-dafny" +repository = "https://github.com/aws/aws-encryption-sdk-dafny" +authors = ["AWS-CryptoTools"] +documentation = "https://docs.rs/crate/aws-esdk" +autoexamples = false +readme = "README.md" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aws-config = "1.5.6" +aws-lc-rs = "1.9.0" +aws-lc-sys = "0.21.2" +aws-sdk-dynamodb = "1.47.0" +aws-sdk-kms = "1.44.0" +aws-smithy-runtime-api = {version = "1.7.2", features = ["client"] } +aws-smithy-types = "1.2.6" +chrono = "0.4.38" +dafny_runtime = { path = "dafny_runtime_rust"} +dashmap = "6.1.0" +pem = "3.0.4" +tokio = {version = "1.40.0", features = ["full"] } +uuid = { version = "1.10.0", features = ["v4"] } + +[lib] +path = "src/implementation_from_dafny.rs" + +[[example]] +name = "main" \ No newline at end of file diff --git a/AwsEncryptionSDK/runtimes/rust/README.md b/AwsEncryptionSDK/runtimes/rust/README.md new file mode 100644 index 000000000..43ec8e179 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/README.md @@ -0,0 +1,3 @@ +# This is NOT ready for production + +# Use at your own risk diff --git a/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/.gitignore b/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/.gitignore new file mode 100644 index 000000000..2f7896d1d --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/Cargo.toml b/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/Cargo.toml new file mode 100644 index 000000000..d5244c747 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "dafny_runtime" +version = "0.1.0" +edition = "2021" + +[dependencies] +once_cell = "1.18.0" +num = "0.4" +itertools = "0.11.0" diff --git a/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/Makefile b/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/Makefile new file mode 100644 index 000000000..f3b72d929 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/Makefile @@ -0,0 +1,22 @@ + +# Invoking the CLI this way just to stay platform-independent +DAFNY = dotnet run --project ../../Dafny --no-build -- + +GENERATED_SYSTEM_MODULE_SOURCE=../obj/systemModulePopulator-rust/src/systemModulePopulator.rs +GENERATED_SYSTEM_MODULE_TARGET=src/system/mod.rs + +all: check-system-module test + +build-system-module: + $(DAFNY) translate rs --no-verify --use-basename-for-filename --optimize-erasable-datatype-wrapper:false --system-module:OmitAllOtherModules ../systemModulePopulator.dfy --output:../obj/systemModulePopulator + python -c "import sys; data = sys.stdin.read(); sys.stdout.write(data.replace('::dafny_runtime', 'crate'))" < $(GENERATED_SYSTEM_MODULE_SOURCE) > $(GENERATED_SYSTEM_MODULE_SOURCE).tmp && mv $(GENERATED_SYSTEM_MODULE_SOURCE).tmp $(GENERATED_SYSTEM_MODULE_SOURCE) + +check-system-module: build-system-module + diff $(GENERATED_SYSTEM_MODULE_SOURCE) $(GENERATED_SYSTEM_MODULE_TARGET) + +update-system-module: build-system-module + cp $(GENERATED_SYSTEM_MODULE_SOURCE) $(GENERATED_SYSTEM_MODULE_TARGET) + +test: + cargo test + (cd ../../DafnyRuntime.Tests/DafnyRuntimeRustTest; cargo test) \ No newline at end of file diff --git a/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/src/lib.rs b/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/src/lib.rs new file mode 100644 index 000000000..eb18d07a3 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/src/lib.rs @@ -0,0 +1,3926 @@ +#[cfg(test)] +mod tests; + +mod system; +pub use mem::MaybeUninit; +use num::{bigint::ParseBigIntError, Integer, Num, One, Signed}; +pub use once_cell::unsync::Lazy; +use std::{ + any::Any, + borrow::Borrow, + boxed::Box, + cell::{RefCell, UnsafeCell}, + clone::Clone, + cmp::Ordering, + collections::{HashMap, HashSet}, + convert::From, + fmt::{Debug, Display, Formatter}, + hash::{Hash, Hasher}, + ptr::NonNull, + mem, + ops::{Add, Deref, Div, Fn, Mul, Neg, Rem, Sub}, + rc::{Rc, Weak}, + vec::Vec, +}; + +pub use system::*; + +pub use itertools; +pub use num::bigint::BigInt; +pub use num::rational::BigRational; +pub use num::FromPrimitive; +pub use num::NumCast; +pub use num::ToPrimitive; +pub use num::Zero; +pub use std::convert::Into; + +// An atomic box is just a RefCell in Rust +pub type SizeT = usize; + +pub trait DafnyType: Clone + DafnyPrint + 'static {} + +impl DafnyType for T where T: Clone + DafnyPrint + 'static {} +pub trait DafnyTypeEq: DafnyType + Hash + Eq {} + +impl DafnyTypeEq for T where T: DafnyType + Hash + Eq {} + +// Dafny's type (0) compiles to NontrivialDefault to prevent subset types from being considered as Default if their witness is nonzero +pub trait NontrivialDefault { + fn nontrivial_default() -> Self; +} + +pub mod dafny_runtime_conversions { + use crate::DafnyType; + use crate::DafnyTypeEq; + pub type DafnyInt = crate::DafnyInt; + pub type DafnySequence = crate::Sequence; + pub type DafnyMap = crate::Map; + pub type DafnySet = crate::Set; + pub type DafnyMultiset = crate::Multiset; + pub type DafnyBool = bool; + pub type DafnyChar = crate::DafnyChar; + pub type DafnyCharUTF16 = crate::DafnyCharUTF16; + + use num::BigInt; + use num::ToPrimitive; + + use std::collections::HashMap; + use std::collections::HashSet; + use std::hash::Hash; + use std::rc::Rc; + + pub mod object { + pub type DafnyClass = crate::Object; + pub type DafnyArray = crate::Object<[T]>; + pub type DafnyArray2 = crate::Object>; + pub type DafnyArray3 = crate::Object>; + // Conversion to and from Dafny reference-counted classes. All these methods take ownership of the class. + pub fn dafny_class_to_struct(ptr: DafnyClass) -> T { + let t: &T = crate::rd!(ptr); + t.clone() + } + pub fn dafny_class_to_boxed_struct(ptr: DafnyClass) -> Box { + Box::new(dafny_class_to_struct(ptr)) + } + pub unsafe fn dafny_class_to_rc_struct(ptr: DafnyClass) -> ::std::rc::Rc { + crate::rcmut::to_rc(ptr.0.unwrap()) + } + pub fn struct_to_dafny_class(t: T) -> DafnyClass { + crate::Object::new(t) + } + pub fn boxed_struct_to_dafny_class(t: Box) -> DafnyClass { + struct_to_dafny_class(*t) + } + pub unsafe fn rc_struct_to_dafny_class(t: ::std::rc::Rc) -> DafnyClass { + crate::Object::from_rc(t) + } + // Conversions to and from Dafny arrays. They all take ownership + pub unsafe fn dafny_array_to_vec(ptr: DafnyArray) -> Vec { + ptr.as_ref().to_vec() + } + pub fn vec_to_dafny_array(array: Vec) -> DafnyArray { + // SAFETY: We own the array + unsafe { + crate::Object::from_rc(::std::rc::Rc::from(array.into_boxed_slice())) + } + } + pub unsafe fn dafny_array2_to_vec(ptr: DafnyArray2) -> Vec> { + crate::rd!(ptr).to_vec() + } + } + + pub mod ptr { + pub type DafnyClass = crate::Ptr; + pub type DafnyArray = crate::Ptr<[T]>; + pub type DafnyArray2 = crate::Ptr>; + pub type DafnyArray3 = crate::Ptr>; + // Conversion to and from Dafny reference-counted classes. All these methods take ownership of the class. + pub unsafe fn dafny_class_to_struct(ptr: DafnyClass) -> T { + *dafny_class_to_boxed_struct(ptr) + } + pub unsafe fn dafny_class_to_boxed_struct(ptr: DafnyClass) -> Box { + Box::from_raw(crate::Ptr::into_raw(ptr)) + } + pub fn struct_to_dafny_class(t: T) -> DafnyClass { + boxed_struct_to_dafny_class(Box::new(t)) + } + pub fn boxed_struct_to_dafny_class(t: Box) -> DafnyClass { + crate::Ptr::from_raw_nonnull(Box::into_raw(t)) + } + // Conversions to and from Dafny arrays. They all take ownership + pub unsafe fn dafny_array_to_vec(ptr: DafnyArray) -> Vec { + ptr.as_ref().to_vec() + } + pub fn vec_to_dafny_array(array: Vec) -> DafnyArray { + crate::Ptr::from_box(array.into_boxed_slice()) + } + pub unsafe fn dafny_array2_to_vec(ptr: DafnyArray2) -> Vec> { + Box::from_raw(crate::Ptr::into_raw(ptr)).to_vec() + } + } + + pub fn dafny_int_to_bigint(i: &DafnyInt) -> BigInt { + i.data.as_ref().clone() + } + pub fn bigint_to_dafny_int(i: &BigInt) -> DafnyInt { + DafnyInt { + data: Rc::new(i.clone()), + } + } + + pub fn dafny_sequence_to_vec(s: &DafnySequence, elem_converter: fn(&T) -> X) -> Vec + where + T: DafnyType, + { + let mut array: Vec = Vec::with_capacity(s.cardinality_usize()); + DafnySequence::::append_recursive(&mut array, s); + array.iter().map(|x| elem_converter(x)).collect() + } + + // Used for external conversions + pub fn vec_to_dafny_sequence( + array: &Vec, + elem_converter: fn(&X) -> T, + ) -> DafnySequence + where + T: DafnyType, + { + let mut result: Vec = Vec::with_capacity(array.len()); + for elem in array.iter() { + result.push(elem_converter(elem)); + } + DafnySequence::::from_array_owned(result) + } + + pub fn dafny_map_to_hashmap( + m: &DafnyMap, + converter_k: fn(&K) -> K2, + converter_v: fn(&V) -> V2, + ) -> HashMap + where + K: DafnyTypeEq, + V: DafnyTypeEq, + K2: Eq + Hash, + V2: Clone, + { + m.to_hashmap_owned(converter_k, converter_v) + } + + pub fn hashmap_to_dafny_map( + map: &HashMap, + converter_k: fn(&K2) -> K, + converter_v: fn(&V2) -> V, + ) -> DafnyMap + where + K: DafnyTypeEq, + V: DafnyTypeEq, + K2: Eq + Hash, + V2: Clone, + { + DafnyMap::::from_hashmap(map, converter_k, converter_v) + } + + // --unicode-chars:true + pub mod unicode_chars_true { + use crate::Sequence; + + type DafnyChar = crate::DafnyChar; + type DafnyString = Sequence; + + pub fn string_to_dafny_string(s: &str) -> DafnyString { + Sequence::from_array_owned(s.chars().map(|v| crate::DafnyChar(v)).collect()) + } + pub fn dafny_string_to_string(s: &DafnyString) -> String { + let characters = s.to_array(); + characters.iter().map(|v| v.0).collect::() + } + } + + // --unicode-chars:false + pub mod unicode_chars_false { + use crate::Sequence; + + type DafnyCharUTF16 = crate::DafnyCharUTF16; + type DafnyString = Sequence; + + pub fn string_to_dafny_string(s: &str) -> DafnyString { + Sequence::from_array_owned(s.encode_utf16().map(|v| crate::DafnyCharUTF16(v)).collect()) + } + pub fn dafny_string_to_string(s: &DafnyString) -> String { + let characters = s + .to_array() + .as_ref() + .iter() + .map(|v| v.0) + .collect::>(); + String::from_utf16_lossy(&characters) + } + } + + pub fn set_to_dafny_set( + set: &HashSet, + converter: fn(&U) -> T, + ) -> DafnySet { + DafnySet::from_iterator(set.iter().map(converter)) + } + pub fn dafny_set_to_set(set: &DafnySet, converter: fn(&T) -> U) -> HashSet + where + T: DafnyTypeEq, + U: Clone + Eq + Hash, + { + let mut result: HashSet = HashSet::new(); + for s in set.data.iter() { + result.insert(converter(s)); + } + result + } + + pub fn dafny_multiset_to_owned_vec( + ms: &DafnyMultiset, + converter: fn(&T) -> U, + ) -> Vec + where + T: DafnyTypeEq, + U: Clone + Eq, + { + let mut result: Vec = Vec::new(); + for s in ms.data.iter() { + // Push T as many times as its size + for _ in 0..s.1.data.to_usize().unwrap() { + result.push(converter(&s.0)); + } + } + result + } + + pub fn vec_to_dafny_multiset(vec: &Vec, converter: fn(&U) -> T) -> DafnyMultiset + where + T: DafnyTypeEq, + U: Clone + Eq + Hash, + { + DafnyMultiset::from_iterator(vec.into_iter().map(|u: &U| converter(u))) + } +} + +pub trait DafnyUsize { + fn into_usize(self) -> usize; +} + +// ************** +// Dafny integers +// ************** + +// Zero-cost abstraction over a Rc +#[derive(Clone)] +pub struct DafnyInt { + data: Rc, +} + +impl DafnyInt { + pub fn new(data: Rc) -> DafnyInt { + DafnyInt { data } + } + pub fn as_usize(&self) -> usize { + self.to_usize().unwrap() + } +} + +impl DafnyUsize for DafnyInt { + fn into_usize(self) -> usize { + self.as_usize() + } +} + +impl AsRef for DafnyInt { + fn as_ref(&self) -> &BigInt { + &self.data + } +} + +// truncate_u(x, u64) +// = ::to_u128(&x).unwrap() as u64; +#[macro_export] +macro_rules! truncate { + ($x:expr, $t:ty) => { + <$crate::DafnyInt as ::std::convert::Into<$t>>::into($x) + }; +} + +impl Into for DafnyInt { + fn into(self) -> u8 { + self.data.to_u8().unwrap() + } +} +impl Into for DafnyInt { + fn into(self) -> u16 { + self.data.to_u16().unwrap() + } +} +impl Into for DafnyInt { + fn into(self) -> u32 { + self.data.to_u32().unwrap() + } +} +impl Into for DafnyInt { + fn into(self) -> u64 { + self.data.to_u64().unwrap() + } +} +impl Into for DafnyInt { + fn into(self) -> u128 { + self.data.to_u128().unwrap() + } +} +impl Into for DafnyInt { + fn into(self) -> i8 { + self.data.to_i8().unwrap() + } +} +impl Into for DafnyInt { + fn into(self) -> i16 { + self.data.to_i16().unwrap() + } +} +impl Into for DafnyInt { + fn into(self) -> i32 { + self.data.to_i32().unwrap() + } +} +impl Into for DafnyInt { + fn into(self) -> i64 { + self.data.to_i64().unwrap() + } +} +impl Into for DafnyInt { + fn into(self) -> i128 { + self.data.to_i128().unwrap() + } +} +impl Into for DafnyInt { + fn into(self) -> usize { + self.data.to_usize().unwrap() + } +} + +impl ToPrimitive for DafnyInt { + fn to_i64(&self) -> Option { + self.data.to_i64() + } + + fn to_u64(&self) -> Option { + self.data.to_u64() + } + + // Override of functions + fn to_u128(&self) -> Option { + self.data.to_u128() + } + + fn to_i128(&self) -> Option { + self.data.to_i128() + } +} + +impl Default for DafnyInt { + fn default() -> Self { + DafnyInt::new(Rc::new(BigInt::zero())) + } +} + +impl NontrivialDefault for DafnyInt { + fn nontrivial_default() -> Self { + Self::default() + } +} + +impl PartialEq for DafnyInt { + fn eq(&self, other: &DafnyInt) -> bool { + self.data.eq(&other.data) + } +} +impl Eq for DafnyInt {} +impl Hash for DafnyInt { + fn hash(&self, state: &mut H) { + self.data.hash(state); + } +} + +impl DafnyPrint for DafnyInt { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + write!(f, "{}", self.data) + } +} + +impl ::std::fmt::Debug for DafnyInt { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.data) + } +} + +impl Add for DafnyInt { + type Output = DafnyInt; + + fn add(self, rhs: DafnyInt) -> Self::Output { + DafnyInt { + data: Rc::new(self.data.as_ref() + rhs.data.as_ref()), + } + } +} + +impl Mul for DafnyInt { + type Output = DafnyInt; + + fn mul(self, rhs: DafnyInt) -> Self::Output { + DafnyInt { + data: Rc::new(self.data.as_ref() * rhs.data.as_ref()), + } + } +} + +impl Div for DafnyInt { + type Output = DafnyInt; + + fn div(self, rhs: DafnyInt) -> Self::Output { + DafnyInt { + data: Rc::new(self.data.as_ref() / rhs.data.as_ref()), + } + } +} + +impl Sub for DafnyInt { + type Output = DafnyInt; + + fn sub(self, rhs: DafnyInt) -> Self::Output { + DafnyInt { + data: Rc::new(self.data.as_ref() - rhs.data.as_ref()), + } + } +} +impl Rem for DafnyInt { + type Output = DafnyInt; + + fn rem(self, rhs: DafnyInt) -> Self::Output { + DafnyInt { + data: Rc::new(self.data.as_ref() % rhs.data.as_ref()), + } + } +} +impl Neg for DafnyInt { + type Output = DafnyInt; + + #[inline] + fn neg(self) -> Self::Output { + DafnyInt { + data: Rc::new(-self.data.as_ref()), + } + } +} +impl Zero for DafnyInt { + #[inline] + fn zero() -> Self { + DafnyInt { + data: Rc::new(BigInt::zero()), + } + } + #[inline] + fn is_zero(&self) -> bool { + self.data.is_zero() + } +} +impl One for DafnyInt { + #[inline] + fn one() -> Self { + DafnyInt { + data: Rc::new(BigInt::one()), + } + } +} +impl Num for DafnyInt { + type FromStrRadixErr = ParseBigIntError; + + #[inline] + fn from_str_radix(s: &str, radix: u32) -> Result { + Ok(DafnyInt { + data: Rc::new(BigInt::from_str_radix(s, radix)?), + }) + } +} +impl Ord for DafnyInt { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.data.cmp(&other.data) + } +} +impl Signed for DafnyInt { + #[inline] + fn abs(&self) -> Self { + DafnyInt { + data: Rc::new(self.data.as_ref().abs()), + } + } + + #[inline] + fn abs_sub(&self, other: &Self) -> Self { + DafnyInt { + data: Rc::new(self.data.as_ref().abs_sub(other.data.as_ref())), + } + } + + #[inline] + fn signum(&self) -> Self { + DafnyInt { + data: Rc::new(self.data.as_ref().signum()), + } + } + + #[inline] + fn is_positive(&self) -> bool { + self.data.as_ref().is_positive() + } + + #[inline] + fn is_negative(&self) -> bool { + self.data.as_ref().is_negative() + } +} + +// Comparison +impl PartialOrd for DafnyInt { + #[inline] + fn partial_cmp(&self, other: &DafnyInt) -> Option { + self.data.partial_cmp(&other.data) + } +} + +impl DafnyInt { + #[inline] + pub fn parse_bytes(number: &[u8], radix: u32) -> DafnyInt { + DafnyInt { + data: ::std::rc::Rc::new(BigInt::parse_bytes(number, radix).unwrap()), + } + } + pub fn from_usize(usize: usize) -> DafnyInt { + DafnyInt { + data: Rc::new(BigInt::from(usize)), + } + } + pub fn from_i32(i: i32) -> DafnyInt { + DafnyInt { + data: Rc::new(BigInt::from(i)), + } + } +} + +macro_rules! impl_dafnyint_from { + () => {}; + ($type:ident) => { + impl ::std::convert::From<$type> for $crate::DafnyInt { + fn from(n: $type) -> Self { + $crate::DafnyInt { + data: ::std::rc::Rc::new(n.into()), + } + } + } + impl $crate::DafnyUsize for $type { + fn into_usize(self) -> usize { + self as usize + } + } + }; +} + +impl_dafnyint_from! { u8 } +impl_dafnyint_from! { u16 } +impl_dafnyint_from! { u32 } +impl_dafnyint_from! { u64 } +impl_dafnyint_from! { u128 } +impl_dafnyint_from! { i8 } +impl_dafnyint_from! { i16 } +impl_dafnyint_from! { i32 } +impl_dafnyint_from! { i64 } +impl_dafnyint_from! { i128 } +impl_dafnyint_from! { usize } + +impl<'a> From<&'a [u8]> for DafnyInt { + fn from(number: &[u8]) -> Self { + DafnyInt::parse_bytes(number, 10) + } +} + +// Now the same but for &[u8, N] for any kind of such references +impl<'a, const N: usize> From<&'a [u8; N]> for DafnyInt { + fn from(number: &[u8; N]) -> Self { + DafnyInt::parse_bytes(number, 10) + } +} + +impl From for DafnyInt { + fn from(c: char) -> Self { + let cu32: u32 = c.into(); + int!(cu32) + } +} + +impl From for DafnyInt { + fn from(c: DafnyChar) -> Self { + int!(c.0) + } +} + +impl From for DafnyInt { + fn from(c: DafnyCharUTF16) -> Self { + int!(c.0) + } +} + +// ************** +// Immutable sequences +// ************** + +impl Eq for Sequence {} + +impl Add<&Sequence> for &Sequence { + type Output = Sequence; + + fn add(self, rhs: &Sequence) -> Self::Output { + Sequence::new_concat_sequence(self, rhs) + } +} + +impl Hash for Sequence { + fn hash(&self, state: &mut H) { + self.cardinality_usize().hash(state); + let array = self.to_array(); + // Iterate over the elements + for elt in array.iter() { + elt.hash(state); + } + } +} + +// Clone can be derived automatically +#[derive(Clone)] +pub enum Sequence +where + T: DafnyType, +{ + ArraySequence { + // Values could be a native array because we will know statically that all + // accesses are in bounds when using this data structure + values: Rc>, + }, + ConcatSequence { + left: Rc>>, + right: Rc>>, + length: SizeT, + boxed: Rc>>>>, + }, +} + +impl Sequence +where + T: DafnyType, +{ + pub fn from_array(values: Ptr<[T]>) -> Sequence { + let mut v = vec![]; + v.extend_from_slice(read!(values)); + Sequence::ArraySequence { + values: Rc::new(v), + } + } + pub fn from_array_object(values: &Object<[T]>) -> Sequence { + let mut v = vec![]; + v.extend_from_slice(rd!(values)); + Sequence::ArraySequence { + values: Rc::new(v), + } + } + pub fn from_array_slice(values: Ptr<[T]>, start: &DafnyInt, end: &DafnyInt) -> Sequence { + let mut v = vec![]; + v.extend_from_slice(&read!(values)[start.to_usize().unwrap()..end.to_usize().unwrap()]); + Sequence::ArraySequence { + values: Rc::new(v), + } + } + pub fn from_array_slice_object(values: &Object<[T]>, start: &DafnyInt, end: &DafnyInt) -> Sequence { + let mut v = vec![]; + v.extend_from_slice(&rd!(values)[start.to_usize().unwrap()..end.to_usize().unwrap()]); + Sequence::ArraySequence { + values: Rc::new(v), + } + } + pub fn from_array_take(values: Ptr<[T]>, n: &DafnyInt) -> Sequence { + let mut v = vec![]; + v.extend_from_slice(&read!(values)[..n.to_usize().unwrap()]); + Sequence::ArraySequence { + values: Rc::new(v), + } + } + pub fn from_array_take_object(values: &Object<[T]>, n: &DafnyInt) -> Sequence { + let mut v = vec![]; + v.extend_from_slice(&rd!(values)[..n.to_usize().unwrap()]); + Sequence::ArraySequence { + values: Rc::new(v), + } + } + pub fn from_array_drop(values: Ptr<[T]>, n: &DafnyInt) -> Sequence { + let mut v = vec![]; + v.extend_from_slice(&read!(values)[n.to_usize().unwrap()..]); + Sequence::ArraySequence { + values: Rc::new(v), + } + } + pub fn from_array_drop_object(values: &Object<[T]>, n: &DafnyInt) -> Sequence { + let mut v = vec![]; + v.extend_from_slice(&rd!(values)[n.to_usize().unwrap()..]); + Sequence::ArraySequence { + values: Rc::new(v), + } + } + pub fn from_array_owned(values: Vec) -> Sequence { + Sequence::ArraySequence { + values: Rc::new(values), + } + } + pub fn new_concat_sequence(left: &Sequence, right: &Sequence) -> Sequence { + Sequence::ConcatSequence { + left: Rc::new(UnsafeCell::new(left.clone())), + right: Rc::new(UnsafeCell::new(right.clone())), + length: left.cardinality_usize() + right.cardinality_usize(), + boxed: Rc::new(RefCell::new(None)), + } + } + pub fn to_array(&self) -> Rc> { + // Let's convert the if then else below to a proper match statement + match self { + Sequence::ArraySequence { values, .. } => + // The length of the elements + { + Rc::clone(values) + } + Sequence::ConcatSequence { + length, + boxed, + left, + right, + } => { + let into_boxed = boxed.as_ref().clone(); + let into_boxed_borrowed = into_boxed.borrow(); + let borrowed: Option<&Rc>> = into_boxed_borrowed.as_ref(); + if let Some(cache) = borrowed.as_ref() { + return Rc::clone(cache); + } + // Let's create an array of size length and fill it up recursively + // We don't materialize nested arrays because most of the time they are forgotten + let mut array: Vec = Vec::with_capacity(*length); + Sequence::::append_recursive(&mut array, self); + let result = Rc::new(array); + let mut cache = boxed.borrow_mut(); + let mutable_left: *mut Sequence = left.get(); + let mutable_right: *mut Sequence = right.get(); + // safety: Once the array is computed, left and right won't ever be read again. + unsafe { *mutable_left = seq!() }; + unsafe { *mutable_right = seq!() }; + *cache = Some(result.clone()); + result + } + } + } + + pub fn append_recursive(array: &mut Vec, this: &Sequence) { + match this { + Sequence::ArraySequence { values, .. } => + // The length of the elements + { + for value in values.iter() { + array.push(value.clone()); + } + } + Sequence::ConcatSequence { + boxed, left, right, .. + } => + // Let's create an array of size length and fill it up recursively + { + let into_boxed = boxed.as_ref().clone(); + let into_boxed_borrowed = into_boxed.borrow(); + let borrowed: Option<&Rc>> = into_boxed_borrowed.as_ref(); + if let Some(values) = borrowed.as_ref() { + for value in values.iter() { + array.push(value.clone()); + } + return; + } + // safety: When a concat is initialized, the left and right are well defined + Sequence::::append_recursive(array, unsafe { &mut *left.get() }); + Sequence::::append_recursive(array, unsafe { &mut *right.get() }); + } + } + } + /// Returns the cardinality of this [`Sequence`]. + // The cardinality returns the length of the sequence + pub fn cardinality_usize(&self) -> SizeT { + match self { + Sequence::ArraySequence { values, .. } => + // The length of the elements + { + values.len() + } + Sequence::ConcatSequence { length, .. } => *length, + } + } + pub fn cardinality(&self) -> DafnyInt { + DafnyInt::from_usize(self.cardinality_usize()) + } + pub fn get_usize(&self, index: SizeT) -> T { + let array = self.to_array(); + array[index].clone() + } + + pub fn slice(&self, start: &DafnyInt, end: &DafnyInt) -> Sequence { + let start_index = start.data.as_ref().to_usize().unwrap(); + let end_index = end.data.as_ref().to_usize().unwrap(); + let new_data = Sequence::from_array_owned(self.to_array()[start_index..end_index].to_vec()); + new_data + } + pub fn take(&self, end: &DafnyInt) -> Sequence { + let end_index = end.data.as_ref().to_usize().unwrap(); + let new_data = Sequence::from_array_owned(self.to_array()[..end_index].to_vec()); + new_data + } + pub fn drop(&self, start: &DafnyInt) -> Sequence { + let start_index = start.data.as_ref().to_usize().unwrap(); + let new_data = Sequence::from_array_owned(self.to_array()[start_index..].to_vec()); + new_data + } + + pub fn update_index(&self, index: &DafnyInt, value: &T) -> Self { + let mut result = self.to_array().as_ref().clone(); + result[index.data.to_usize().unwrap()] = value.clone(); + Sequence::from_array_owned(result) + } + + pub fn concat(&self, other: &Sequence) -> Sequence { + Sequence::new_concat_sequence(self, other) + } + + pub fn get(&self, index: &DafnyInt) -> T { + self.get_usize(index.data.to_usize().unwrap()) + } + pub fn iter(&self) -> SequenceIter { + SequenceIter { + array: self.to_array(), + index: 0, + } + } +} + +pub struct SequenceIter { + array: Rc>, + index: SizeT, +} +impl Iterator for SequenceIter { + type Item = T; + fn next(&mut self) -> Option { + if self.index < self.array.len() { + let result = self.array[self.index].clone(); + self.index += 1; + Some(result) + } else { + None + } + } +} + +impl Default for Sequence { + fn default() -> Self { + Sequence::from_array_owned(vec![]) + } +} +impl NontrivialDefault for Sequence { + fn nontrivial_default() -> Self { + Self::default() + } +} + +impl Sequence { + pub fn as_dafny_multiset(&self) -> Multiset { + Multiset::from_array(&self.to_array()) + } +} + +// Makes it possible to write iterator.collect::> and obtain a sequence +impl FromIterator for Sequence { + fn from_iter>(iter: I) -> Self { + Sequence::from_array_owned(iter.into_iter().collect()) + } +} + +impl Sequence { + pub fn contains(&self, value: &T) -> bool { + self.to_array().contains(value) + } +} +impl PartialEq> for Sequence +where + T: DafnyType + PartialEq, +{ + fn eq(&self, other: &Sequence) -> bool { + // Iterate through both elements and verify that they are equal + let values: Rc> = self.to_array(); + if other.cardinality_usize() != values.len() { + return false; + } + let mut i: usize = 0; + for value in values.iter() { + if value != &other.get_usize(i) { + return false; + } + i += 1; + } + true + } +} + +impl PartialOrd for Sequence { + fn partial_cmp(&self, other: &Sequence) -> Option { + // Comparison is only prefix-based + match self.cardinality_usize().cmp(&other.cardinality_usize()) { + Ordering::Equal => { + if self == other { + Some(Ordering::Equal) + } else { + None + } + } + Ordering::Less => { + for i in 0..self.cardinality_usize() { + if self.get_usize(i) != other.get_usize(i) { + return None; + } + } + Some(Ordering::Less) + } + Ordering::Greater => { + for i in 0..other.cardinality_usize() { + if self.get_usize(i) != other.get_usize(i) { + return None; + } + } + Some(Ordering::Greater) + } + } + } +} + +impl DafnyPrint for Sequence { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + if !V::is_char() { + write!(f, "[")?; + } + let mut first = true; + for value in self.to_array().iter() { + if !first && !V::is_char() { + write!(f, ", ")?; + } + first = false; + value.fmt_print(f, true)?; + } + if !V::is_char() { + write!(f, "]") + } else { + write!(f, "") + } + } +} + +impl Debug for Sequence { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.fmt_print(f, false) + } +} + +// ************** +// Immutable maps +// ************** + +#[derive(Clone)] +pub struct Map +where + K: DafnyTypeEq, + V: DafnyType, +{ + data: Rc>, +} + +impl Default for Map { + fn default() -> Self { + Map { + data: Rc::new(HashMap::new()), + } + } +} + +impl NontrivialDefault for Map { + fn nontrivial_default() -> Self { + Self::default() + } +} + +impl Hash for Map { + fn hash(&self, state: &mut H) { + self.data.len().hash(state); // Worst performance for things that are not hashable like maps + } +} + +impl PartialEq> for Map +where + K: DafnyTypeEq, + V: DafnyTypeEq, +{ + fn eq(&self, other: &Map) -> bool { + if self.data.len() != other.data.len() { + return false; + } + for (k, v) in self.data.iter() { + if other.data.get(k) != Some(v) { + return false; + } + } + return true; + } +} + +impl Eq for Map {} + +impl Map { + pub fn new_empty() -> Map { + Map { + data: Rc::new(HashMap::new()), + } + } + pub fn from_array(values: &Vec<(K, V)>) -> Map { + Self::from_iterator(values.iter().map(|(k, v)| (k.clone(), v.clone()))) + } + pub fn from_iterator(data: I) -> Map + where + I: Iterator, + { + let mut result: HashMap = HashMap::new(); + for (k, v) in data { + result.insert(k, v); + } + Self::from_hashmap_owned(result) + } + pub fn from_hashmap_owned(values: HashMap) -> Map { + Map { + data: Rc::new(values), + } + } + pub fn to_hashmap_owned( + &self, + converter_k: fn(&K) -> K2, + converter_v: fn(&V) -> V2, + ) -> HashMap + where + K2: Eq + std::hash::Hash, + V2: Clone, + { + let mut result: HashMap = HashMap::new(); + for (k, v) in self.data.iter() { + result.insert(converter_k(k), converter_v(v)); + } + result + } + pub fn cardinality_usize(&self) -> usize { + self.data.len() + } + pub fn cardinality(&self) -> DafnyInt { + DafnyInt::from_usize(self.cardinality_usize()) + } + pub fn contains(&self, key: &K) -> bool { + self.data.contains_key(key) + } + pub fn get_or_none(&self, key: &K) -> Option { + self.data.get(key).cloned() + } + // Dafny will normally guarantee that the key exists. + pub fn get(&self, key: &K) -> V { + self.data[key].clone() + } + pub fn merge(&self, other: &Map) -> Map { + if other.cardinality_usize() == 0 { + return self.clone(); + } + if self.cardinality_usize() == 0 { + return other.clone(); + } + let mut new_data = (*other.data).clone(); + // Overriding self's keys with other's keys if there are some. + for (k, v) in self.data.iter() { + if !other.contains(k) { + new_data.insert(k.clone(), v.clone()); + } + } + Self::from_hashmap_owned(new_data) + } + pub fn subtract(&self, keys: &Set) -> Self { + if keys.cardinality_usize() == 0 { + return self.clone(); + } + let mut result: HashMap = HashMap::new(); + for (k, v) in self.data.iter() { + if !keys.contains(k) { + result.insert(k.clone(), v.clone()); + } + } + Self::from_hashmap_owned(result) + } + + pub fn from_hashmap( + map: &HashMap, + converter_k: fn(&K2) -> K, + converter_v: fn(&V2) -> V, + ) -> Map + where + K: DafnyTypeEq, + V: DafnyTypeEq, + K2: Eq + Hash, + V2: Clone, + { + let mut result: HashMap = HashMap::new(); + for (k, v) in map.iter() { + result.insert(converter_k(k), converter_v(v)); + } + Map { + data: Rc::new(result), + } + } + pub fn keys(&self) -> Set { + let mut result: HashSet = HashSet::new(); + for (k, _) in self.data.iter() { + result.insert(k.clone()); + } + Set::from_hashset_owned(result) + } + + pub fn update_index(&self, index: &K, value: &V) -> Self { + let mut result = self.data.as_ref().clone(); + result.insert(index.clone(), value.clone()); + Map::from_hashmap_owned(result) + } + + pub fn update_index_owned(&self, index: K, value: V) -> Self { + let mut result = self.data.as_ref().clone(); + result.insert(index, value); + Map::from_hashmap_owned(result) + } + + pub fn iter_raw(&self) -> std::collections::hash_map::Iter<'_, K, V> { + self.data.iter() + } + + pub fn iter(&self) -> impl Iterator + '_ { + self.data.iter().map(|(k, _v)| k).cloned() + } +} + + +impl Map { + pub fn values(&self) -> Set { + let mut result: Vec = Vec::new(); + for (_, v) in self.data.iter() { + result.push(v.clone()); + } + Set::from_array(&result) + } + pub fn items(&self) -> Set<(K, V)> { + let mut result: Vec<(K, V)> = Vec::new(); + for (k, v) in self.data.iter() { + result.push((k.clone(), v.clone())); + } + Set::from_array(&result) + } +} + +impl Map { + pub fn as_dafny_multiset(&self) -> Multiset { + Multiset::from_hashmap(&self.data) + } +} + +pub struct MapBuilder +where + K: Clone + Eq + std::hash::Hash, + V: Clone, +{ + data: HashMap, +} + +impl MapBuilder +where + K: DafnyTypeEq, + V: DafnyType, +{ + pub fn new() -> MapBuilder { + MapBuilder { + data: HashMap::new(), + } + } + pub fn add(&mut self, key: &K, value: &V) { + // Dafny will prove that overriding has the same value anyway + self.data.insert(key.clone(), value.clone()); + } + pub fn build(self) -> Map { + Map::from_hashmap_owned(self.data) + } +} + +impl DafnyPrint for Map +where + K: DafnyTypeEq, + V: DafnyType, +{ + fn fmt_print(&self, f: &mut Formatter<'_>, in_seq: bool) -> std::fmt::Result { + f.write_str("map[")?; + let mut first = true; + for (k, v) in self.data.iter() { + if !first { + f.write_str(", ")?; + } + first = false; + k.fmt_print(f, in_seq)?; + f.write_str(" := ")?; + v.fmt_print(f, in_seq)?; + } + f.write_str("]") + } +} + +impl Debug for Map +where + K: DafnyTypeEq, + V: DafnyTypeEq, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.fmt_print(f, false) + } +} + +// ************** +// Immutable sets +// ************** + +#[derive(Clone)] +pub struct Set { + data: Rc>, +} + +// Since there is no canonical way to iterate over a set to compute the hash. +impl ::std::hash::Hash for Set { + fn hash<_H: ::std::hash::Hasher>(&self, _state: &mut _H) { + self.cardinality_usize().hash(_state) + } +} + +impl Eq for Set {} + +impl Default for Set +where + T: DafnyTypeEq, +{ + fn default() -> Self { + Self::new_empty() + } +} +impl NontrivialDefault for Set { + fn nontrivial_default() -> Self { + Self::default() + } +} + +impl PartialEq> for Set +where + V: DafnyTypeEq, +{ + fn eq(&self, other: &Set) -> bool { + // 1. Same cardinality + // 2. All the elements of self are in the other + if self.cardinality_usize() != other.cardinality_usize() { + false + } else { + for value in self.data.iter() { + if !other.contains(value) { + return false; + } + } + for value in other.data.iter() { + if !self.contains(value) { + return false; + } + } + true + } + } +} + +impl PartialOrd for Set { + fn partial_cmp(&self, other: &Self) -> Option { + // Partial ordering is inclusion + if self.cardinality_usize() <= other.cardinality_usize() { + for value in self.data.iter() { + if !other.contains(value) { + return None; + } + } + if self.cardinality_usize() == other.cardinality_usize() { + Some(Ordering::Equal) + } else { + Some(Ordering::Less) + } + } else { + for value in other.data.iter() { + if !self.contains(value) { + return None; + } + } + Some(Ordering::Greater) + } + } +} + +impl Set { + pub fn new_empty() -> Set { + Self::from_hashset_owned(HashSet::new()) + } + pub fn from_array(array: &Vec) -> Set { + Self::from_iterator(array.iter().map(|v| v.clone())) + } + pub fn from_iterator(data: I) -> Set + where + I: Iterator, + { + let mut set: HashSet = HashSet::new(); + for value in data { + set.insert(value); + } + Self::from_hashset_owned(set) + } + pub fn from_sequence(data: &Rc>) -> Set { + Self::from_array(data.to_array().borrow()) + } + pub fn from_hashset_owned(hashset: HashSet) -> Set { + Set { + data: Rc::new(hashset), + } + } + pub fn cardinality_usize(&self) -> usize { + self.data.len() + } + pub fn cardinality(&self) -> DafnyInt { + DafnyInt::from_usize(self.data.len()) + } + pub fn contains(&self, value: &V) -> bool { + self.data.contains(value) + } + pub fn merge(self: &Self, other: &Set) -> Set { + if self.cardinality_usize() == 0 { + return other.clone(); + } + if other.cardinality_usize() == 0 { + return self.clone(); + } + let mut result = self.data.as_ref().clone(); + // iterate over the other, add only not contained elements + for value in other.data.iter() { + if !result.contains(value) { + result.insert(value.clone()); + } + } + Set::from_hashset_owned(result) + } + + pub fn intersect(self: &Self, other: &Set) -> Set { + if self.cardinality_usize() == 0 { + return self.clone(); + } + if other.cardinality_usize() == 0 { + return other.clone(); + } + // Start with an empty vec with capacity the smallest of both sets + let mut result = HashSet::new(); + + // iterate over the other, take only elements in common + for value in self.data.iter() { + if other.data.contains(value) { + result.insert(value.clone()); + } + } + Set::from_hashset_owned(result) + } + + pub fn subtract(&self, other: &Set) -> Set { + if self.cardinality_usize() == 0 { + return self.clone(); + } + if other.cardinality_usize() == 0 { + return self.clone(); + } + // Start with a vec the size of the first one + let mut result = HashSet::new(); + + // iterate over the other, take only elements not in second + for value in self.data.iter() { + if !other.contains(value) { + result.insert(value.clone()); + } + } + Set::from_hashset_owned(result) + } + + pub fn disjoint(&self, other: &Set) -> bool { + if self.cardinality_usize() == 0 { + return true; + } + if other.cardinality_usize() == 0 { + return true; + } + if other.data.len() < self.data.len() { + // iterate over the other, take only elements not in self + for value in other.data.iter() { + if self.contains(value) { + return false; + } + } + } else { + // iterate over the self, take only elements not in other + for value in self.data.iter() { + if other.contains(value) { + return false; + } + } + } + true + } + + pub fn equals(&self, other: &Set) -> bool { + if self.cardinality_usize() != other.cardinality_usize() { + return false; + } + // iterate over the other, take only elements not in second + for value in other.data.iter() { + if !self.contains(value) { + return false; + } + } + true + } + + pub fn elements(self: &Self) -> Set { + self.clone() + } + + pub fn as_dafny_multiset(&self) -> Multiset { + Multiset::from_set(self) + } + + pub fn iter(&self) -> std::collections::hash_set::Iter<'_, V> { + self.data.iter() + } + + pub fn peek(&self) -> V { + self.data.iter().next().unwrap().clone() + } +} + +pub struct SetBuilder +where + T: Clone + Eq + std::hash::Hash, +{ + data: HashMap, +} + +impl SetBuilder { + pub fn new() -> SetBuilder { + SetBuilder { + data: HashMap::new(), + } + } + pub fn add(&mut self, value: &T) { + // Dafny will prove that overriding has the same value anyway + self.data.insert(value.clone(), true); + } + pub fn build(self) -> Set { + // Iterate over all the key values of the hashmap and add them to an array + let mut result: Vec = Vec::new(); + for (k, _v) in self.data.iter() { + result.push(k.clone()); + } + + Set::from_array(&result) + } +} + +impl DafnyPrint for Set { + fn fmt_print(&self, f: &mut Formatter<'_>, in_seq: bool) -> std::fmt::Result { + f.write_str("{")?; + let mut first = true; + for value in self.data.iter() { + if !first { + f.write_str(", ")?; + } + first = false; + value.fmt_print(f, in_seq)?; + } + f.write_str("}") + } +} + +impl Debug for Set +where + V: DafnyTypeEq, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.fmt_print(f, false) + } +} + +// ******************* +// Immutable multisets +// ******************* + +#[derive(Clone)] +pub struct Multiset { + pub data: HashMap, + pub size: DafnyInt, +} + +impl Multiset { + pub fn new_empty() -> Multiset { + Self::from_array(&vec![]) + } + pub fn get_total(map: &HashMap) -> DafnyInt { + let mut total = DafnyInt::zero(); + for (_, v) in map.iter() { + total = total + v.clone(); + } + total + } + pub fn from_hashmap_owned(map: HashMap) -> Multiset { + Multiset { + size: Self::get_total(&map), + data: map, + } + } + pub fn from_hashmap(map: &HashMap) -> Multiset { + Self::from_hashmap_owned(map.clone()) + } + pub fn from_array(data: &Vec) -> Multiset { + Self::from_iterator(data.iter().map(|x| x.clone())) + } + pub fn from_iterator(data: I) -> Multiset + where + I: Iterator, + { + let mut hashmap: HashMap = HashMap::new(); + let mut total: DafnyInt = DafnyInt::zero(); + for value in data { + let count = hashmap.entry(value.clone()).or_insert(DafnyInt::zero()); + *count = count.clone() + DafnyInt::one(); + total = total + DafnyInt::one(); + } + Multiset { + data: hashmap, + size: total, + } + } + pub fn from_set(set: &Set) -> Multiset { + Self::from_iterator(set.data.iter().map(|v| v.clone())) + } + + pub fn cardinality_usize(&self) -> SizeT { + self.size.data.to_usize().unwrap() + } + pub fn cardinality(&self) -> DafnyInt { + self.size.clone() + } + pub fn contains(&self, value: &V) -> bool { + self.data.contains_key(value) && self.data.get(value).unwrap() > &DafnyInt::zero() + } + pub fn get(&self, value: &V) -> DafnyInt { + if self.data.contains_key(value) { + self.data.get(value).unwrap().clone() + } else { + DafnyInt::zero() + } + } + pub fn update_count(&self, value: &V, new_count: &DafnyInt) -> Multiset { + let mut result = self.clone(); + let old_count = self.get(value); + if new_count == &DafnyInt::zero() { + result.data.remove(value); + } else { + result.data.insert(value.clone(), new_count.clone()); + } + result.size = self.size.clone() + new_count.clone() - old_count; + result + } + pub fn merge(&self, other: &Multiset) -> Multiset { + if other.size.is_zero() { + return self.clone(); + } + if self.size.is_zero() { + return other.clone(); + } + let mut result = self.data.clone(); + for (k, v) in other.data.iter() { + let old_count = self.get(k); + let new_count = old_count.clone() + v.clone(); + result.insert(k.clone(), new_count); + } + Multiset { + data: result, + size: self.size.clone() + other.size.clone(), + } + } + pub fn intersect(&self, other: &Multiset) -> Multiset { + if other.size.is_zero() { + return other.clone(); + } + if self.size.is_zero() { + return self.clone(); + } + let mut result = HashMap::::new(); + let mut total = DafnyInt::zero(); + for (k, other_count) in other.data.iter() { + let self_count = self.get(k); + let resulting_count = if self_count < *other_count { + self_count + } else { + other_count.clone() + }; + if resulting_count.is_positive() { + result.insert(k.clone(), resulting_count.clone()); + total = total + resulting_count; + } + } + Multiset { + data: result, + size: total, + } + } + pub fn subtract(&self, other: &Multiset) -> Multiset { + if other.size.is_zero() { + return self.clone(); + } + if self.size.is_zero() { + return self.clone(); + } + let mut result = self.data.clone(); + let mut total = self.size.clone(); + for (k, v) in other.data.iter() { + let old_count = self.get(k); + let new_count = old_count.clone() - v.clone(); + if !new_count.is_positive() { + total = total - old_count.clone(); + result.remove(k); + } else { + total = total - v.clone(); + result.insert(k.clone(), new_count); + } + } + Multiset { + data: result, + size: total, + } + } + pub fn disjoint(&self, other: &Multiset) -> bool { + for value in other.data.keys() { + if self.contains(value) { + return false; + } + } + true + } + + pub fn as_dafny_multiset(&self) -> Multiset { + self.clone() + } + + pub fn peek(&self) -> V { + self.data.iter().next().unwrap().0.clone() + } + + pub fn iter_raw(&self) -> std::collections::hash_map::Iter<'_, V, DafnyInt> { + self.data.iter() + } + + pub fn iter(&self) -> impl Iterator + '_ { + self.data.iter().map(|(k, _v)| k).cloned() + } +} + +impl Default for Multiset +where + T: DafnyTypeEq, +{ + fn default() -> Self { + Self::new_empty() + } +} +impl NontrivialDefault for Multiset { + fn nontrivial_default() -> Self { + Self::default() + } +} + +impl PartialOrd> for Multiset { + fn partial_cmp(&self, other: &Multiset) -> Option { + match self.cardinality().cmp(&other.cardinality()) { + Ordering::Less => { + for value in self.data.keys() { + if !other.contains(value) || self.get(value) > other.get(value) { + return None; + } + } + Some(Ordering::Less) + } + Ordering::Equal => { + for value in self.data.keys() { + if self.get(value) != other.get(value) { + return None; + } + } + Some(Ordering::Equal) + } + Ordering::Greater => { + for value in other.data.keys() { + if !self.contains(value) || self.get(value) < other.get(value) { + return None; + } + } + Some(Ordering::Greater) + } + } + } +} + +impl DafnyPrint for Multiset { + fn fmt_print(&self, f: &mut Formatter<'_>, in_seq: bool) -> std::fmt::Result { + f.write_str("multiset{")?; + let mut first = true; + for value in self.data.iter() { + for _count in 0..value.1.data.to_usize().unwrap() { + if !first { + f.write_str(", ")?; + } + first = false; + value.0.fmt_print(f, in_seq)?; + } + } + f.write_str("}") + } +} + +impl Debug for Multiset +where + V: DafnyTypeEq, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.fmt_print(f, false) + } +} + +impl PartialEq> for Multiset { + fn eq(&self, other: &Multiset) -> bool { + if self.cardinality() != other.cardinality() { + return false; + } + // iterate over the other, take only elements not in second + for value in other.data.iter() { + if !self.contains(value.0) || self.get(value.0) != *value.1 { + return false; + } + } + true + } +} +impl Eq for Multiset {} +impl Hash for Multiset { + fn hash(&self, state: &mut H) { + self.cardinality().hash(state); + } +} + +pub fn dafny_rational_to_int(r: &BigRational) -> BigInt { + euclidian_division(r.numer().clone(), r.denom().clone()) +} + +pub fn nullable_referential_equality(left: Option>, right: Option>) -> bool { + match (left, right) { + (Some(l), Some(r)) => Rc::ptr_eq(&l, &r), + (None, None) => true, + _ => false, + } +} + +pub fn euclidian_division(a: A, b: A) -> A { + if !a.is_negative() { + if !b.is_negative() { + a / b + } else { + -(a / -b) + } + } else { + if !b.is_negative() { + -((-(a + One::one())) / b) - One::one() + } else { + (-(a + One::one())) / (-b) + One::one() + } + } +} + +pub fn euclidian_modulo(a: A, b: A) -> A { + if !a.is_negative() { + if !b.is_negative() { + a % b + } else { + a % -b + } + } else { + let bp = b.abs(); + let c = (-a) % bp.clone(); + if c == Zero::zero() { + Zero::zero() + } else { + bp - c + } + } +} + +pub struct IntegerRange + One + Ord + Clone> { + hi: A, + current: A, +} + +impl + One + Ord + Clone> Iterator for IntegerRange { + type Item = A; + + fn next(&mut self) -> Option { + if self.current < self.hi { + let result = self.current.clone(); + self.current = self.current.clone() + One::one(); + Some(result) + } else { + None + } + } +} + +pub fn integer_range + One + Ord + Clone>( + low: A, + hi: A, +) -> impl Iterator { + IntegerRange { hi, current: low } +} + +pub struct IntegerRangeDown + One + Ord + Clone> { + current: A, + low: A, +} + +impl + One + Ord + Clone> Iterator for IntegerRangeDown { + type Item = A; + + fn next(&mut self) -> Option { + if self.current > self.low { + self.current = self.current.clone() - One::one(); + Some(self.current.clone()) + } else { + None + } + } +} + +pub fn integer_range_down + One + Ord + Clone>( + hi: A, + low: A, +) -> impl Iterator { + IntegerRangeDown { current: hi, low } +} + +// Unbounded versions + +pub struct IntegerRangeUnbounded + One + Clone> { + current: A, +} +impl + One + Clone> Iterator for IntegerRangeUnbounded { + type Item = A; + + fn next(&mut self) -> Option { + let result = self.current.clone(); + self.current = self.current.clone() + One::one(); + Some(result) + } +} +pub fn integer_range_unbounded + One + Clone>( + low: A, +) -> impl Iterator { + IntegerRangeUnbounded { current: low } +} + +pub struct IntegerRangeDownUnbounded + One + Clone> { + current: A, +} + +impl + One + Clone> Iterator for IntegerRangeDownUnbounded { + type Item = A; + + fn next(&mut self) -> Option { + self.current = self.current.clone() - One::one(); + Some(self.current.clone()) + } +} + +pub fn integer_range_down_unbounded + One + Clone>( + hi: A, +) -> impl Iterator { + IntegerRangeDownUnbounded { current: hi } +} + +pub struct LazyFieldWrapper(pub Lazy A>>); + +impl PartialEq for LazyFieldWrapper { + fn eq(&self, other: &Self) -> bool { + self.0.deref() == other.0.deref() + } +} + +impl Default for LazyFieldWrapper { + fn default() -> Self { + Self(Lazy::new(Box::new(A::default))) + } +} + +impl DafnyPrint for LazyFieldWrapper { + fn fmt_print(&self, f: &mut Formatter<'_>, in_seq: bool) -> std::fmt::Result { + self.0.deref().fmt_print(f, in_seq) + } +} + +// Convert the DafnyPrint above into a macro so that we can create it for functions of any input arity +macro_rules! dafny_print_function { + ($($n:tt)*) => { + impl $crate::DafnyPrint for ::std::rc::Rc B> { + fn fmt_print(&self, f: &mut ::std::fmt::Formatter<'_>, _in_seq: bool) -> ::std::fmt::Result { + write!(f, "") + } + } + } +} +// Now create a loop like impl_tuple_print_loop so that we can create functions up to size 32 +macro_rules! dafny_print_function_loop { + ($first:ident $($rest:ident)*) => { + dafny_print_function_loop! { $($rest)* } + dafny_print_function! { $first $($rest)* } + }; + () => { + } +} +// Emit functions till 32 parameters +dafny_print_function_loop! { A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 A16 +A17 A18 A19 A20 A21 A22 A23 A24 A25 A26 A27 A28 A29 A30 A31 A32 } + +pub struct FunctionWrapper(pub A); +impl DafnyPrint for FunctionWrapper { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + write!(f, "") + } +} + +impl Clone for FunctionWrapper { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl PartialEq for FunctionWrapper> { + fn eq(&self, other: &Self) -> bool { + Rc::ptr_eq(&self.0, &other.0) + } +} + +pub struct DafnyPrintWrapper(pub T); +impl Display for DafnyPrintWrapper<&T> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.0.fmt_print(f, false) + } +} + +// from gazebo +#[inline] +pub unsafe fn transmute_unchecked(x: A) -> B { + assert_eq!(std::mem::size_of::(), std::mem::size_of::()); + debug_assert_eq!(0, (&x as *const A).align_offset(std::mem::align_of::())); + let b = std::ptr::read(&x as *const A as *const B); + std::mem::forget(x); + b +} + +pub trait DafnyPrint { + fn fmt_print(&self, f: &mut Formatter<'_>, in_seq: bool) -> std::fmt::Result; + + // Vec gets special treatment so we carry that information here + #[inline] + fn is_char() -> bool { + false + } +} + +impl DafnyPrint for *const T { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + write!(f, "<{} object>", std::any::type_name::()) + } +} + +macro_rules! impl_print_display { + ($name:ty) => { + impl $crate::DafnyPrint for $name { + fn fmt_print(&self, f: &mut ::std::fmt::Formatter<'_>, _in_seq: bool) -> ::std::fmt::Result { + ::std::fmt::Display::fmt(&self, f) + } + } + }; +} + +impl_print_display! { String } +impl_print_display! { bool } +impl_print_display! { u8 } +impl_print_display! { u16 } +impl_print_display! { u32 } +impl_print_display! { u64 } +impl_print_display! { u128 } +impl_print_display! { i8 } +impl_print_display! { i16 } +impl_print_display! { i32 } +impl_print_display! { i64 } +impl_print_display! { i128 } +impl_print_display! { usize } + +impl DafnyPrint for f32 { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + write!(f, "{:.1}", self) + } +} + +impl DafnyPrint for f64 { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + write!(f, "{:.1}", self) + } +} + +impl DafnyPrint for () { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + write!(f, "()") + } +} + +#[derive(Clone)] +pub struct DafnyCharUTF16(pub u16); +pub type DafnyStringUTF16 = Sequence; + +impl Default for DafnyCharUTF16 { + fn default() -> Self { + Self('a' as u16) + } +} + +impl DafnyPrint for DafnyCharUTF16 { + #[inline] + fn fmt_print(&self, f: &mut Formatter<'_>, in_seq: bool) -> std::fmt::Result { + let real_char = char::decode_utf16(vec![self.clone()].iter().map(|v| v.0)) + .map(|r| r.map_err(|e| e.unpaired_surrogate())) + .collect::>()[0]; + let rendered_char = match real_char { + Ok(c) => c, + Err(e) => { + return write!(f, "Invalid UTF-16 code point: {}", e); + } + }; + + if in_seq { + write!(f, "{}", rendered_char) + } else { + write!(f, "'{}'", rendered_char) + } + } + + #[inline] + fn is_char() -> bool { + true + } +} + +impl Debug for DafnyCharUTF16 { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.fmt_print(f, false) + } +} + +impl PartialEq for DafnyCharUTF16 { + fn eq(&self, other: &DafnyCharUTF16) -> bool { + self.0 == other.0 + } +} +impl Eq for DafnyCharUTF16 {} +impl Hash for DafnyCharUTF16 { + fn hash(&self, state: &mut H) { + self.0.hash(state) + } +} + +impl PartialOrd for DafnyCharUTF16 { + fn partial_cmp(&self, other: &DafnyCharUTF16) -> Option { + (self.0).partial_cmp(&other.0) + } +} + +impl Add for DafnyCharUTF16 { + type Output = DafnyCharUTF16; + + fn add(self, rhs: DafnyCharUTF16) -> Self::Output { + DafnyCharUTF16(self.0 + rhs.0) + } +} + +impl Sub for DafnyCharUTF16 { + type Output = DafnyCharUTF16; + + fn sub(self, rhs: DafnyCharUTF16) -> Self::Output { + DafnyCharUTF16(self.0 - rhs.0) + } +} + +#[derive(Clone)] +pub struct DafnyChar(pub char); +pub type DafnyString = Sequence; + +impl Default for DafnyChar { + fn default() -> Self { + Self('a') + } +} + +impl DafnyPrint for DafnyChar { + #[inline] + fn fmt_print(&self, f: &mut Formatter<'_>, in_seq: bool) -> std::fmt::Result { + if in_seq { + write!(f, "{}", self.0) + } else { + write!(f, "'{}'", self.0) + } + } + + #[inline] + fn is_char() -> bool { + true + } +} + +impl Debug for DafnyChar { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.fmt_print(f, false) + } +} + +impl PartialEq for DafnyChar { + fn eq(&self, other: &DafnyChar) -> bool { + self.0 == other.0 + } +} + +impl PartialOrd for DafnyChar { + fn partial_cmp(&self, other: &DafnyChar) -> Option { + (self.0 as u32).partial_cmp(&(other.0 as u32)) + } +} +impl Eq for DafnyChar {} +impl Hash for DafnyChar { + fn hash(&self, state: &mut H) { + self.0.hash(state) + } +} + +impl Add for DafnyChar { + type Output = DafnyChar; + + fn add(self, rhs: DafnyChar) -> Self::Output { + DafnyChar(unsafe { char::from_u32_unchecked(self.0 as u32 + rhs.0 as u32) }) + } +} + +impl Sub for DafnyChar { + type Output = DafnyChar; + + fn sub(self, rhs: DafnyChar) -> Self::Output { + DafnyChar(unsafe { char::from_u32_unchecked(self.0 as u32 - rhs.0 as u32) }) + } +} + +impl DafnyPrint for Option { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + match self { + Some(x) => x.fmt_print(f, false), + None => write!(f, "null"), + } + } +} + +impl DafnyPrint for BigInt { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + write!(f, "{}", self) + } +} + +fn divides_a_power_of_10(mut i: BigInt) -> (bool, BigInt, usize) { + let one: BigInt = One::one(); + + let mut factor = one.clone(); + let mut log10 = 0; + + let zero = Zero::zero(); + let ten = BigInt::from_i32(10).unwrap(); + + if i <= zero { + return (false, factor, log10); + } + + while i.is_multiple_of(&ten) { + i /= BigInt::from_i32(10).unwrap(); + log10 += 1; + } + + let two = BigInt::from_i32(2).unwrap(); + let five = BigInt::from_i32(5).unwrap(); + + while i.is_multiple_of(&five) { + i /= &five; + factor *= &two; + log10 += 1; + } + + while i.is_multiple_of(&two) { + i /= &two; + factor *= &two; + log10 += 1; + } + + (i == one, factor, log10) +} + +impl DafnyPrint for BigRational { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + if self.denom() == &One::one() || self.numer() == &Zero::zero() { + write!(f, "{}.0", self.numer()) + } else { + let (divides, factor, log10) = divides_a_power_of_10(self.denom().clone()); + if divides { + let mut num = self.numer().clone(); + num *= factor; + + if num.is_negative() { + write!(f, "-")?; + num = -num; + } + + let digits = num.to_string(); + + if log10 < digits.len() { + let digit_count = digits.len() - log10; + write!(f, "{}", &digits[..digit_count])?; + write!(f, ".")?; + write!(f, "{}", &digits[digit_count..]) + } else { + let z = log10 - digits.len(); + write!(f, "0.")?; + for _ in 0..z { + write!(f, "0")?; + } + write!(f, "{}", digits) + } + } else { + write!(f, "({}.0 / {}.0)", self.numer(), self.denom()) + } + } + } +} + +impl DafnyPrint for Rc { + fn fmt_print(&self, f: &mut Formatter<'_>, in_seq: bool) -> std::fmt::Result { + self.as_ref().fmt_print(f, in_seq) + } +} + +impl DafnyPrint for Vec { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + if !T::is_char() { + write!(f, "[")?; + } + + for (i, item) in self.iter().enumerate() { + if !T::is_char() { + if i > 0 { + write!(f, ", ")?; + } + + item.fmt_print(f, false)?; + } else { + item.fmt_print(f, true)?; + } + } + + if !T::is_char() { + write!(f, "]") + } else { + Ok(()) + } + } +} + +impl DafnyPrint for RefCell { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + self.borrow().fmt_print(f, _in_seq) + } +} + +impl DafnyPrint for HashSet { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + write!(f, "{{")?; + + let mut i = 0; + + for item in self.iter() { + if i > 0 { + write!(f, ", ")?; + } + + item.fmt_print(f, false)?; + + i += 1; + } + + write!(f, "}}") + } +} + +pub fn char_lt(left: char, right: char) -> bool { + let left_code = left as u32; + let right_code = right as u32; + + left_code < right_code +} + +pub fn string_of(s: &str) -> DafnyString { + s.chars() + .map(|v| DafnyChar(v)) + .collect::>() +} + +pub fn string_utf16_of(s: &str) -> DafnyStringUTF16 { + Sequence::from_array_owned(s.encode_utf16().map(|v| DafnyCharUTF16(v)).collect()) +} + +macro_rules! impl_tuple_print { + ($($items:ident)*) => { + impl <$($items,)*> $crate::DafnyPrint for ($($items,)*) + where + $($items: $crate::DafnyPrint,)* + { + #[allow(unused_assignments)] + fn fmt_print(&self, f: &mut ::std::fmt::Formatter<'_>, _in_seq: bool) -> ::std::fmt::Result { + #[allow(non_snake_case)] + let ($($items,)*) = self; + + write!(f, "(")?; + + let mut i = 0; + + $( + if (i > 0) { + write!(f, ", ")?; + } + + $items.fmt_print(f, false)?; + + i += 1; + )* + + write!(f, ")") + } + } + }; +} + +macro_rules! impl_tuple_print_loop { + () => {}; + ($first:ident $($rest:ident)*) => { + impl_tuple_print_loop! { $($rest)* } + impl_tuple_print! { $first $($rest)* } + }; +} + +// 32 elements ought to be enough for everyone +impl_tuple_print_loop! { + A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 + A11 A12 A13 A14 A15 A16 A17 A18 A19 A20 + A21 A22 A23 A24 A25 A26 A27 A28 A29 A30 + A31 +} + +// seq!(1, 2, 3) is rewritten to Sequence::from_array_owned(vec!(1, 2, 3)) +#[macro_export] +macro_rules! seq { + ($($x:expr),*) => { + $crate::Sequence::from_array_owned(vec![$($x), *]) + } +} + +#[macro_export] +macro_rules! set { + ($($x:expr), *) => { + { + // No warning about this variable not needing to be mut in the case of an empty set + #[allow(unused_mut)] + let mut temp_hash = ::std::collections::HashSet::new(); + $( + temp_hash.insert($x); + )* + $crate::Set::from_hashset_owned(temp_hash) + } + } +} + +#[macro_export] +macro_rules! multiset { + ($($x:expr), *) => { + { + #[allow(unused_mut)] + let mut temp_hash = ::std::collections::HashMap::new(); + #[allow(unused_mut)] + let mut total_size: usize = 0; + $( { + #[allow(unused_mut)] + let mut entry = temp_hash.entry($x).or_insert($crate::DafnyInt::from(0)); + *entry = (*entry).clone() + $crate::DafnyInt::from(1); + total_size += 1; + } + )* + $crate::Multiset { + data: temp_hash, + size: $crate::DafnyInt::from(total_size), + } + } + } +} + +// we enable the syntax map![k1 => v1, k2 => v2] +#[macro_export] +macro_rules! map { + ($($k:expr => $v:expr), *) => { + { + #[allow(unused_mut)] + let mut temp_hash = ::std::collections::HashMap::new(); + $( + temp_hash.insert($k.clone(), $v.clone()); + )* + $crate::Map::from_hashmap_owned(temp_hash) + } + } +} + +#[macro_export] +macro_rules! int { + ($x:expr) => { + $crate::DafnyInt::from($x) + }; +} + +////////// +// Arrays +////////// + +macro_rules! ARRAY_GETTER_LENGTH0 { + () => { + #[inline] + pub fn length0(&self) -> $crate::DafnyInt { + $crate::DafnyInt::from(self.length0_usize()) + } + #[inline] + pub fn length0_usize(&self) -> usize { + self.data.len() + } + } +} +macro_rules! ARRAY_GETTER_LENGTH { + ($field: ident, $field_usize: ident) => { + #[inline] + pub fn $field(&self) -> $crate::DafnyInt { + $crate::DafnyInt::from(self.$field_usize()) + } + #[inline] + pub fn $field_usize(&self) -> usize { + self.$field + } + } +} + +// An 1-dimensional Dafny array is a zero-cost abstraction over a pointer on a native array +#[macro_export] +macro_rules! array { + ($($x:expr), *) => { + $crate::array::from_native(::std::boxed::Box::new([$($x), *])) + } +} + +macro_rules! ARRAY_INIT { + {$length: ident, $inner: expr} => { + $crate::array::initialize_box_usize($length, { + ::std::rc::Rc::new(move |_| { $inner }) + }) + } +} + +macro_rules! ARRAY_INIT_INNER { + ($length: ident) => { + $crate::array::placebos_box_usize::($length) + } +} + +// ARRAY_DATA_TYPE(length0, length1, length2) will return +// Box<[Box<[Box<[T]>]>]> +macro_rules! ARRAY_DATA_TYPE { + ($length:ident) => { + ::std::boxed::Box<[T]> + }; + ($length:ident, $($rest_length:ident),+) => { + ::std::boxed::Box<[ARRAY_DATA_TYPE!($($rest_length),+)]> + }; +} + +// Macro to generate generalizations of the function placebos_usize to higher-dimensions arrays + +#[macro_export] +macro_rules! INIT_ARRAY_DATA { + // Handle the innermost array initialization + ($ArrayType:ident, $last_length:ident) => { + ARRAY_INIT_INNER!($last_length) + }; + // Handle recursive array initialization for multiple dimensions + ($ArrayType:ident, $first_length:ident, $($rest_length:ident),+) => { + ARRAY_INIT!($first_length, INIT_ARRAY_DATA!($ArrayType, $($rest_length),+)) + }; +} + +macro_rules! ARRAY_METHODS { + // Accepts any number of length identifiers + ($ArrayType:ident, $length0: ident, $($length:ident),+) => { + pub fn placebos_box_usize( + $length0: usize, + $($length: usize),+ + ) -> ::std::boxed::Box<$ArrayType<$crate::MaybeUninit>> { + ::std::boxed::Box::new($ArrayType { + $($length: $length),+, + data: INIT_ARRAY_DATA!($ArrayType, $length0, $($length),+), + }) + } + + pub fn placebos_usize( + $length0: usize, + $($length: usize),+ + ) -> $crate::Ptr<$ArrayType<$crate::MaybeUninit>> { + $crate::Ptr::from_box(Self::placebos_box_usize( + $length0, + $($length),+ + )) + } + + pub fn placebos_usize_object( + $length0: usize, + $($length: usize),+ + ) -> $crate::Object<$ArrayType<$crate::MaybeUninit>> { + // SAFETY: We know the object is owned and never referred to by anything else + $crate::Object::new($ArrayType { + $($length: $length),+, + data: INIT_ARRAY_DATA!($ArrayType, $length0, $($length),+), + }) + } + + pub fn placebos( + $length0: &$crate::DafnyInt, + $($length: &$crate::DafnyInt),+ + ) -> $crate::Ptr<$ArrayType<$crate::MaybeUninit>> { + Self::placebos_usize( + $length0.as_usize(), + $($length.as_usize()),+ + ) + } + + // Once all the elements have been initialized, transform the signature of the pointer + pub fn construct(p: $crate::Ptr<$ArrayType<$crate::MaybeUninit>>) -> $crate::Ptr<$ArrayType> { + unsafe { std::mem::transmute(p) } + } + // Once all the elements have been initialized, transform the signature of the pointer + pub fn construct_object(p: $crate::Object<$ArrayType<$crate::MaybeUninit>>) -> $crate::Object<$ArrayType> { + unsafe { std::mem::transmute(p) } + } + }; +} + + +macro_rules! ARRAY_STRUCT { + ($ArrayType:ident, $length0: ident, $($length:ident),+) => { + pub struct $ArrayType { + $($length: usize),+, + pub data: ARRAY_DATA_TYPE!($length0, $($length),+), + } + } +} + +macro_rules! ARRAY_TO_VEC_LOOP { + (@inner $self: ident, $tmp: ident, $data: expr) => { + $tmp.push($data.clone()); + }; + (@for $self: ident, $tmp: ident, $data: expr, $length_usize: ident $(, $rest_length_usize: ident)*) => { + for i in 0..$self.$length_usize() { + ARRAY_TO_VEC_LOOP!(@inner $self, $tmp, $data[i] $(, $rest_length_usize)*); + } + }; + (@inner $self: ident, $outerTmp: ident, $data: expr $(, $rest_length_usize: ident)*) => { + { + let mut tmp = ::std::vec::Vec::new(); + ARRAY_TO_VEC_LOOP!(@for $self, tmp, $data $(, $rest_length_usize)*); + $outerTmp.push(tmp); + } + }; + + ($self: ident, $data: expr $(, $rest_length_usize: ident)*) => { + { + let mut tmp = ::std::vec::Vec::new(); + ARRAY_TO_VEC_LOOP!(@for $self, tmp, $data $(, $rest_length_usize)*); + tmp + } + }; +} + +macro_rules! ARRAY_TO_VEC_TYPE { + ($length0: ident) => { + ::std::vec::Vec + }; + ($length0: ident $(, $res_length: ident)*) => { + ::std::vec::Vec + } +} + +macro_rules! ARRAY_TO_VEC { + ($length0_usize: ident $(, $res_length_usize: ident)*) => { + pub fn to_vec(&self) -> ARRAY_TO_VEC_TYPE!($length0_usize, $($res_length_usize),*) { + ARRAY_TO_VEC_LOOP!(self, self.data, $length0_usize, $($res_length_usize),*) + } + } +} + +macro_rules! ARRAY_LENGTHS { + () => { + + }; + (($length0: ident, $length0_usize: ident) $(, $rest: tt)*) => { + ARRAY_GETTER_LENGTH0!(); + ARRAY_LENGTHS!($(, $rest)*); + }; + (, ($length: ident, $length_usize: ident) $(, $rest: tt)*) => { + ARRAY_GETTER_LENGTH!($length, $length_usize); + ARRAY_LENGTHS!($(, $rest)*); + } +} + +macro_rules! ARRAY_METHODS_WRAPPER { + ($ArrayType:ident, $(($length:ident, $length_usize: ident)), +) => { + ARRAY_METHODS!($ArrayType, $($length), +); + } +} + +macro_rules! ARRAY_TO_VEC_WRAPPER { + ($(($length:ident, $length_usize: ident)), +) => { + ARRAY_TO_VEC!($($length_usize), +); + } +} + +macro_rules! ARRAY_STRUCT_WRAPPER { + ($ArrayType:ident, $(($length:ident, $length_usize: ident)), +) => { + ARRAY_STRUCT!($ArrayType, $($length), +); + } +} + +macro_rules! ARRAY_DEF { + ($ArrayType:ident, $(($length:ident, $length_usize: ident)), +) => { + ARRAY_STRUCT_WRAPPER!($ArrayType, $(($length, $length_usize)), +); + impl $ArrayType { + ARRAY_LENGTHS!{ + $(($length, $length_usize)), + + } + ARRAY_METHODS_WRAPPER!{$ArrayType, + $(($length, $length_usize)), + + } + } + impl $ArrayType { + ARRAY_TO_VEC_WRAPPER!{ + $(($length, $length_usize)), + + } + } + } +} + +// Array2 to Array16 + +ARRAY_DEF!{Array2, + (length0, length0_usize), + (length1, length1_usize) +} + +ARRAY_DEF!{Array3, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize) +} + +ARRAY_DEF!{Array4, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize) +} + +ARRAY_DEF!{Array5, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize), + (length4, length4_usize) +} + +ARRAY_DEF!{Array6, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize), + (length4, length4_usize), + (length5, length5_usize) +} + +ARRAY_DEF!{Array7, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize), + (length4, length4_usize), + (length5, length5_usize), + (length6, length6_usize) +} + +ARRAY_DEF!{Array8, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize), + (length4, length4_usize), + (length5, length5_usize), + (length6, length6_usize), + (length7, length7_usize) +} + +ARRAY_DEF!{Array9, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize), + (length4, length4_usize), + (length5, length5_usize), + (length6, length6_usize), + (length7, length7_usize), + (length8, length8_usize) +} + +ARRAY_DEF!{Array10, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize), + (length4, length4_usize), + (length5, length5_usize), + (length6, length6_usize), + (length7, length7_usize), + (length8, length8_usize), + (length9, length9_usize) +} + +ARRAY_DEF!{Array11, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize), + (length4, length4_usize), + (length5, length5_usize), + (length6, length6_usize), + (length7, length7_usize), + (length8, length8_usize), + (length9, length9_usize), + (length10, length10_usize) +} + +ARRAY_DEF!{Array12, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize), + (length4, length4_usize), + (length5, length5_usize), + (length6, length6_usize), + (length7, length7_usize), + (length8, length8_usize), + (length9, length9_usize), + (length10, length10_usize), + (length11, length11_usize) +} + +ARRAY_DEF!{Array13, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize), + (length4, length4_usize), + (length5, length5_usize), + (length6, length6_usize), + (length7, length7_usize), + (length8, length8_usize), + (length9, length9_usize), + (length10, length10_usize), + (length11, length11_usize), + (length12, length12_usize) +} + +ARRAY_DEF!{Array14, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize), + (length4, length4_usize), + (length5, length5_usize), + (length6, length6_usize), + (length7, length7_usize), + (length8, length8_usize), + (length9, length9_usize), + (length10, length10_usize), + (length11, length11_usize), + (length12, length12_usize), + (length13, length13_usize) +} + +ARRAY_DEF!{Array15, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize), + (length4, length4_usize), + (length5, length5_usize), + (length6, length6_usize), + (length7, length7_usize), + (length8, length8_usize), + (length9, length9_usize), + (length10, length10_usize), + (length11, length11_usize), + (length12, length12_usize), + (length13, length13_usize), + (length14, length14_usize) +} + +ARRAY_DEF!{Array16, + (length0, length0_usize), + (length1, length1_usize), + (length2, length2_usize), + (length3, length3_usize), + (length4, length4_usize), + (length5, length5_usize), + (length6, length6_usize), + (length7, length7_usize), + (length8, length8_usize), + (length9, length9_usize), + (length10, length10_usize), + (length11, length11_usize), + (length12, length12_usize), + (length13, length13_usize), + (length14, length14_usize), + (length15, length15_usize) +} + +pub mod array { + use super::DafnyInt; + use num::ToPrimitive; + use std::mem::MaybeUninit; + use std::{boxed::Box, rc::Rc, vec::Vec}; + use super::Ptr; + + #[inline] + pub fn from_native(v: Box<[T]>) -> Ptr<[T]> { + Ptr::from_box(v) + } + #[inline] + pub fn from_vec(v: Vec) -> Ptr<[T]> { + from_native(v.into_boxed_slice()) + } + pub fn to_vec(v: Ptr<[T]>) -> Vec { + unsafe { Box::<[T]>::from_raw(v.into_raw()) }.into_vec() + } + pub fn initialize_usize(n: usize, initializer: Rc T>) -> Ptr<[T]> { + let mut v = Vec::with_capacity(n); + for i in 0..n { + v.push(initializer(i)); + } + from_vec(v) + } + + pub fn placebos(n: &DafnyInt) -> Ptr<[MaybeUninit]> { + placebos_usize(n.as_usize()) + } + pub fn placebos_usize(n: usize) -> Ptr<[MaybeUninit]> { + Ptr::from_box(placebos_box_usize(n)) + } + pub fn placebos_usize_object(n: usize) -> super::Object<[MaybeUninit]> { + super::rcmut::array_object_from_box(placebos_box_usize(n)) + } + // Once all the elements have been initialized, transform the signature of the pointer + pub fn construct(p: Ptr<[MaybeUninit]>) -> Ptr<[T]> { + unsafe { std::mem::transmute(p) } + } + pub fn construct_object(p: super::Object<[MaybeUninit]>) -> super::Object<[T]> { + unsafe { std::mem::transmute(p) } + } + + pub fn placebos_box(n: &DafnyInt) -> Box<[MaybeUninit]> { + placebos_box_usize(n.to_usize().unwrap()) + } + pub fn placebos_box_usize(n_usize: usize) -> Box<[MaybeUninit]> { + // This code is optimized to take a constant time. See: + // https://users.rust-lang.org/t/allocate-a-boxed-array-of-maybeuninit/110169/7 + std::iter::repeat_with(MaybeUninit::uninit) + .take(n_usize) + .collect() + } + + pub fn initialize(n: &DafnyInt, initializer: Rc T>) -> Ptr<[T]> { + super::Ptr::from_box(initialize_box(n, initializer)) + } + + pub fn initialize_box(n: &DafnyInt, initializer: Rc T>) -> Box<[T]> { + initialize_box_usize(n.to_usize().unwrap(), initializer) + } + pub fn initialize_box_usize( + n_usize: usize, + initializer: Rc T>, + ) -> Box<[T]> { + let mut v = Vec::with_capacity(n_usize); + for i in 0..n_usize { + v.push(initializer(&int!(i))); + } + v.into_boxed_slice() + } + + #[inline] + pub fn length_usize(this: Ptr<[T]>) -> usize { + // safety: Dafny won't call this function unless it can guarantee the array is still allocated + super::read!(this).len() + } + #[inline] + pub fn length(this: Ptr<[T]>) -> DafnyInt { + int!(length_usize(this)) + } + #[inline] + pub fn get_usize(this: Ptr<[T]>, i: usize) -> T { + // safety: Dafny won't call this function unless it can guarantee the array is still allocated + this.as_ref()[i].clone() + } + #[inline] + pub fn get(this: Ptr<[T]>, i: &DafnyInt) -> T { + get_usize(this, i.to_usize().unwrap()) + } + #[inline] + pub fn update_usize(this: Ptr<[T]>, i: usize, val: T) { + // safety: Dafny won't call this function unless it can guarantee the array is still allocated + crate::modify!(this)[i] = val; + } + #[inline] + pub fn update(this: Ptr<[T]>, i: &DafnyInt, val: T) { + update_usize(this, i.to_usize().unwrap(), val); + } +} + +/////////////////// +// Class helpers // +/////////////////// +pub fn allocate() -> Ptr { + let this_ptr = Box::into_raw(Box::new(MaybeUninit::uninit())) as *mut MaybeUninit as *mut T; + Ptr::from_raw_nonnull(this_ptr) +} +// Generic function to safely deallocate a raw pointer +#[inline] +pub fn deallocate(pointer: Ptr) { + // safety: Dafny won't call this function unless it can guarantee the object is still allocated + unsafe { + // Takes ownership of the reference, + // so that it's deallocated at the end of the method + let _ = Box::from_raw(pointer.into_raw()); + } +} + +pub struct ExactPool { + current: T, + yielded: bool, +} + +// Implement iterator for an exact pool, yielding +impl Iterator for ExactPool { + type Item = T; + fn next(&mut self) -> Option { + if self.yielded { + None + } else { + self.yielded = true; + Some(self.current.clone()) + } + } +} +pub fn exact_range(value: T) -> ExactPool { + ExactPool { + current: value, + yielded: false, + } +} + +// Any Dafny trait must require classes extending it to have a method "as_any_mut" +// that can convert the reference from that trait to a reference of Any + +// 'is' is meant to be used on references only, to check if a trait reference is a class reference +#[macro_export] +macro_rules! is { + ($raw:expr, $id:ty) => { + $crate::modify!($crate::cast_any!($raw)) + .downcast_mut::<$id>() + .is_some() + }; +} + +#[macro_export] +macro_rules! is_object { + ($raw:expr, $id:ty) => { + $crate::md!($crate::cast_any_object!($raw)) + .downcast_mut::<$id>() + .is_some() + }; +} + +// cast_any is meant to be used on references only, to convert any references (classes or traits)* +// to an Any reference trait +#[macro_export] +macro_rules! cast_any { + ($raw:expr) => { + $crate::Upcast::::upcast($crate::read!($raw)) + }; +} +// cast_any_object is meant to be used on references only, to convert any references (classes or traits)* +// to an Any reference trait +#[macro_export] +macro_rules! cast_any_object { + ($obj:expr) => { + $crate::UpcastObject::::upcast($crate::md!($obj)) + }; +} + + +// When initializing an uninitialized field for the first time, +// we ensure we don't drop the previous content +// This is problematic if the same field is overwritten multiple times +/// In that case, prefer to use update_uninit +#[macro_export] +macro_rules! update_field_nodrop { + ($ptr:expr, $field:ident, $value:expr) => { + $crate::update_nodrop!($crate::modify!($ptr).$field, $value) + }; +} + +// When initializing an uninitialized field for the first time, +// we ensure we don't drop the previous content +#[macro_export] +macro_rules! update_nodrop { + ($ptr:expr, $value:expr) => { + // safety: Dafny won't call this function unless it can guarantee the value at the address was not + // yet initialized, so that not dropping it won't create a memory leak + unsafe { ::std::ptr::addr_of_mut!($ptr).write($value) } + } +} + +// Given a class or array pointer, transforms it to a mutable reference +#[macro_export] +macro_rules! modify { + ($ptr:expr) => { + { + #[allow(unused_unsafe)] + let tmp = unsafe {&mut *(::std::cell::UnsafeCell::raw_get($ptr.0.unwrap_unchecked().as_ptr()))}; + tmp + } + } +} + +// Given a class or array pointer, transforms it to a read-only reference +#[macro_export] +macro_rules! read { + ($ptr:expr) => { + { + #[allow(unused_unsafe)] + let tmp = unsafe {&*(::std::cell::UnsafeCell::raw_get($ptr.0.unwrap_unchecked().as_ptr()))}; + tmp + } + } +} + +// If the field is guaranteed to be assigned only once, update_field_nodrop is sufficient +#[macro_export] +macro_rules! update_field_uninit { + ($t:expr, $field:ident, $field_assigned:expr, $value:expr) => {{ + let computed_value = $value; + #[allow(unused_assignments)] + if $field_assigned { + $crate::modify!($t).$field = computed_value; + } else { + $crate::update_field_nodrop!($t, $field, computed_value); + $field_assigned = true; + } + }}; +} + +// Macro to call at the end of the first new; constructor when not every field is guaranteed to be assigned. +#[macro_export] +macro_rules! update_field_if_uninit { + ($t:expr, $field:ident, $field_assigned:expr, $value:expr) => {{ + let computed_value = $value; + if !$field_assigned { + $crate::update_field_nodrop!($t, $field, computed_value); + $field_assigned = true; + } + }}; +} + +///////////////// +// Raw pointers (require wrapping because of equality) +///////////////// + +// This Ptr has the same run-time space as *mut +pub struct Ptr(pub Option>>); + +impl Ptr { + pub fn null() -> Self { + Ptr(None) + } + pub fn is_null(&self) -> bool { + self.0.is_none() + } + #[inline] + pub fn from_raw_nonnull(t: *mut T) -> Ptr { + unsafe { Ptr(Some(::std::mem::transmute::, NonNull>>(NonNull::new_unchecked(t)))) } + } + pub fn from_box(t: Box) -> Ptr { + Self::from_raw_nonnull(Box::into_raw(t)) + } + pub fn into_raw(self) -> *mut T { + if self.is_null() { + panic!("Cannot turn a null pointer into a raw pointer"); + } + let nonnull = unsafe { self.0.unwrap_unchecked() }; + unsafe { ::std::mem::transmute::<_, *mut T>(nonnull.as_ptr()) } + } +} + +impl > Ptr { + pub fn is_instance_of(self) -> bool { + if self.is_null() { + false + } else { + read!(Upcast::::upcast(read!(self))) + .downcast_ref::() + .is_some() + } + } +} + +impl NontrivialDefault for Ptr { + fn nontrivial_default() -> Self { + // Create a null pointer + Self::null() + } +} + +impl Ptr { + pub fn new(val: T) -> Ptr { + Self::from_box(Box::new(val)) + } +} + + +impl Eq for Ptr {} + +impl Clone for Ptr { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Ptr { } + +impl Default for Ptr { + fn default() -> Self { + Ptr::null() + } +} + +impl Debug for Ptr { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + self.fmt_print(f, false) + } +} +impl DafnyPrint for Ptr { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + write!(f, "") + } +} + + +impl PartialEq> for Ptr { + fn eq(&self, other: &Ptr) -> bool { + if !self.is_null() { + if !other.is_null() { + // To compare addresses, we need to ensure we only compare thin pointers + // https://users.rust-lang.org/t/comparing-addresses-between-fat-and-thin-pointers/89008 + ::std::ptr::eq( + self.clone().into_raw() as *const (), + other.clone().into_raw() as *const ()) + } else { + false + } + } else if !other.is_null() { + false + } else { + true + } + } +} + +impl std::hash::Hash for Ptr { + fn hash(&self, state: &mut H) { + if !self.is_null() { + (read!(self.clone()) as *const T as *const ()).hash(state); + } else { + 0.hash(state); + } + } +} + +impl AsMut for Ptr { + fn as_mut(&mut self) -> &mut T { + modify!(self.clone()) + } +} +impl AsRef for Ptr { + fn as_ref(&self) -> &T { + read!(self.clone()) + } +} + +impl Ptr { + // Never use on local values, only on &self types previously called on Ptr types. + pub fn from_ref(r: &T) -> Ptr { + Ptr(unsafe {::std::mem::transmute::<_, Option>>>(r as *const T)}) + } +} +// cast is meant to be used on references only, to downcast a trait reference to a class reference +#[macro_export] +macro_rules! cast { + ($raw:expr, $id:ty) => { + { + #[allow(unused_unsafe)] + let tmp = + unsafe { + let expr = $raw; + let res: $crate::Ptr<$id> = + if expr.is_null() { + $crate::Ptr::null() + } else { + $crate::Ptr::from_raw_nonnull(expr.into_raw() as *mut $id) + }; + res + }; + tmp + } + }; + +} + +///////////////// +// Reference-counted classes mode +///////////////// + +pub struct Object(pub Option>); + +impl Object { + pub unsafe fn from_rc(rc: Rc) -> Object { + Object(Some(rcmut::from_rc(rc))) + } + pub fn null() -> Object { + Object(None) + } + pub fn is_null(&self) -> bool { + self.0.is_none() + } +} +impl > Object { + pub fn is_instance_of(self) -> bool { + // safety: Dafny won't call this function unless it can guarantee the object is still allocated + rd!(UpcastObject::::upcast(rd!(self))) + .downcast_ref::() + .is_some() + } +} +impl Object { + pub fn new(val: T) -> Object { + Object(Some(rcmut::new(val))) + } +} +impl Eq for Object {} + +impl Clone for Object { + fn clone(&self) -> Self { + Object(self.0.clone()) + } +} + +impl Default for Object { + fn default() -> Self { + Object(None) + } +} + +impl Debug for Object { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + self.fmt_print(f, false) + } +} +impl DafnyPrint for Object { + fn fmt_print(&self, f: &mut Formatter<'_>, _in_seq: bool) -> std::fmt::Result { + write!(f, "") + } +} + + +impl PartialEq> for Object { + fn eq(&self, other: &Object) -> bool { + if let Some(p) = &self.0 { + if let Some(q) = &other.0 { + // To compare addresses, we need to ensure we only compare thin pointers + // https://users.rust-lang.org/t/comparing-addresses-between-fat-and-thin-pointers/89008 + ::std::ptr::eq(p.as_ref().get() as *const (), q.as_ref().get() as *const ()) + } else { + false + } + } else if let Some(_q) = &other.0 { + false + } else { + true + } + } +} + +impl std::hash::Hash for Object { + fn hash(&self, state: &mut H) { + if let Some(p) = &self.0 { + (p.as_ref().get() as *const ()).hash(state); + } else { + 0.hash(state); + } + } +} + +impl AsMut for Object { + fn as_mut(&mut self) -> &mut T { + unsafe { &mut *(&self.0).as_ref().unwrap_unchecked().as_ref().get() } + } +} +impl AsRef for Object { + fn as_ref(&self) -> &T { + unsafe { &*(&self.0).as_ref().unwrap_unchecked().as_ref().get() } + } +} + +// Never inline because, when compiling with cargo --release, sometimes it gets rid of this statement +#[inline(never)] +fn increment_strong_count(data: *const T) { + // SAFETY: This method is called only on values that were constructed from an Rc + unsafe { + Rc::increment_strong_count(data); + } +} + +impl Object { + pub fn from_ref(r: &T) -> Object { + let pt = r as *const T as *const UnsafeCell; + crate::increment_strong_count(pt); + let rebuilt = unsafe { Rc::from_raw(pt) }; + Object(Some(rebuilt)) + } +} + +#[macro_export] +macro_rules! cast_object { + ($raw:expr, $id:ty) => { + unsafe { + let res: $crate::Object<$id> = + $crate::Object(Some(::std::rc::Rc::from_raw( + ::std::rc::Rc::into_raw($raw.0.unwrap()) as _))); + res + } + }; +} + +// Returns an object whose fields are yet initialized. Only use update_field_uninit_object and update_field_if_uninit_object to initialize fields. +pub fn allocate_object() -> Object { + unsafe { mem::transmute(object::new::>(MaybeUninit::uninit())) } +} + +pub struct AllocationTracker { + allocations: Vec> +} + +pub fn allocate_object_track(allocation_tracker: &mut AllocationTracker) -> Object { + let res = allocate_object::(); + allocation_tracker.allocations.push(Rc::>::downgrade(&res.0.clone().unwrap())); + res +} + +// Equivalent of update_field_nodrop but for rcmut +#[macro_export] +macro_rules! update_field_nodrop_object { + ($ptr:expr, $field: ident, $value:expr) => { + $crate::update_nodrop_object!(($crate::rcmut::borrow_mut(&mut $ptr.0.clone().unwrap())).$field, $value) + }; +} + +// Equivalent of update_nodrop but for rcmut +#[macro_export] +macro_rules! update_nodrop_object { + ($ptr:expr, $value:expr) => { + unsafe { ::std::ptr::addr_of_mut!($ptr).write($value) } + }; +} + +// Equivalent of update_field_if_uninit but for rcmut +#[macro_export] +macro_rules! update_field_if_uninit_object { + ($t:expr, $field:ident, $field_assigned:expr, $value:expr) => {{ + let computed_value = $value; + #[allow(unused_assignments)] + if !$field_assigned { + $crate::update_field_nodrop_object!($t, $field, computed_value); + $field_assigned = true; + } + }}; +} + +// Equivalent of update_field_uninit but for rcmut +#[macro_export] +macro_rules! update_field_uninit_object { + ($t:expr, $field:ident, $field_assigned:expr, $value:expr) => {{ + let computed_value = $value; + #[allow(unused_assignments)] + if $field_assigned { + $crate::md!($t).$field = computed_value; + } else { + $crate::update_field_nodrop_object!($t, $field, computed_value); + $field_assigned = true; + } + }}; +} + +// Equivalent of modify but for rcmut +#[macro_export] +macro_rules! md { + ($x:expr) => { + $x.clone().as_mut() + }; +} + +// Equivalent of read but for rcmut +#[macro_export] +macro_rules! rd { + ($x:expr) => { + $x.as_ref() + }; +} + +// Count the number of references to the given object +#[macro_export] +macro_rules! refcount { + ($x:expr) => { + ::std::rc::Rc::strong_count(unsafe { $crate::rcmut::as_rc($x.0.as_ref().unwrap()) }) + }; +} + +pub mod object { + use std::any::Any; + + pub fn new(val: T) -> crate::Object { + crate::Object(Some(crate::rcmut::new(val))) + } + pub fn downcast(_self: crate::Object) -> crate::Object { + unsafe { + crate::Object(Some(crate::rcmut::downcast::(_self.0.unwrap()).unwrap())) // Use unwrap_unchecked? + } + } + #[inline] + pub fn is(_self: crate::Object) -> bool { + is_object!(_self, T) + } +} + +// Inspired from https://crates.io/crates/rcmut +pub mod rcmut { + use std::cell::UnsafeCell; + use std::mem::{self, MaybeUninit}; + use std::rc::Rc; + use std::sync::Arc; + + pub fn array_object_from_rc(data: Rc<[T]>) -> crate::Object<[T]> { + crate::Object(Some(unsafe { crate::rcmut::from_rc(data) })) + } + pub fn array_object_from_box(data: Box<[T]>) -> crate::Object<[T]> { + let data: Rc<[T]> = data.into(); + crate::Object(Some(unsafe { crate::rcmut::from_rc(data) })) + } + pub struct Array { + pub data: Box<[T]>, + } + impl Array { + pub fn new(data: Box<[T]>) -> crate::Object> { + crate::Object(Some(crate::rcmut::new(Array { data }))) + } + + pub fn placebos_usize_object(length: usize) -> crate::Object>> { + let x = crate::array::placebos_box_usize::(length); + crate::rcmut::Array::>::new(x) + } + } + /// A reference counted smart pointer with unrestricted mutability. + pub type RcMut = Rc>; + + /// Create a new RcMut for a value. + pub fn new(val: T) -> RcMut { + Rc::new(UnsafeCell::new(val)) + } + /// Retrieve the inner Rc as a reference. + pub unsafe fn from(value: Box) -> RcMut { + mem::transmute(Rc::new(*value)) + } + + pub unsafe fn from_rc(value: Rc) -> RcMut { + mem::transmute(value) + } + + pub unsafe fn as_rc(this: &RcMut) -> &Rc { + mem::transmute(this) + } + pub unsafe fn to_rc(this: RcMut) -> Rc { + mem::transmute(this) + } + + /// Retrieve the inner Rc as a mutable reference. + pub unsafe fn as_rc_mut(this: &mut RcMut) -> &mut Rc { + mem::transmute(this) + } + + /// Get a reference to the value. + #[inline] + pub unsafe fn borrow(this: &RcMut) -> &T { + mem::transmute(this.get()) + } + + /// Get a mutable reference to the value. + #[inline] + pub unsafe fn borrow_mut(this: &mut RcMut) -> &mut T { + mem::transmute(this.get()) + } + + pub unsafe fn downcast(this: RcMut) -> Option> { + let t: Rc = to_rc(this); + let t: Rc = Rc::downcast::(t).ok()?; + mem::transmute(t) + } + + /// A reference counted smart pointer with unrestricted mutability. + pub struct ArcMut { + inner: Arc>, + } + + impl Clone for ArcMut { + fn clone(&self) -> ArcMut { + ArcMut { + inner: self.inner.clone(), + } + } + } + + impl ArcMut { + /// Create a new ArcMut for a value. + pub fn new(val: T) -> ArcMut { + ArcMut { + inner: Arc::new(UnsafeCell::new(val)), + } + } + } + + impl ArcMut { + /// Retrieve the inner Rc as a reference. + pub unsafe fn as_arc(&self) -> &Arc { + mem::transmute(&self.inner) + } + + /// Retrieve the inner Rc as a mutable reference. + pub unsafe fn as_arc_mut(&mut self) -> &mut Arc { + mem::transmute(&mut self.inner) + } + + /// Get a reference to the value. + pub unsafe fn borrow(&self) -> &T { + mem::transmute(self.inner.get()) + } + + /// Get a mutable reference to the value. + pub unsafe fn borrow_mut(&mut self) -> &mut T { + mem::transmute(self.inner.get()) + } + } +} + +///////////////// +// Method helpers +///////////////// + +// A MaybePlacebo is a value that is either a placebo or a real value. +// It is a wrapper around a MaybeUninit value, but knowing whether the value is a placebo or not. +// That way, when it is dropped, the underlying value is only dropped if it is not a placebo. + +#[derive(Clone)] +pub struct MaybePlacebo(Option); +impl MaybePlacebo { + #[inline] + pub fn read(&self) -> T { + // safety: Dafny will guarantee we will never read a placebo value + unsafe { self.0.clone().unwrap_unchecked() } + } +} + +impl MaybePlacebo { + #[inline] + pub fn new() -> Self { + MaybePlacebo(None) + } + #[inline] + pub fn from(v: T) -> Self { + MaybePlacebo(Some(v)) + } +} + +#[macro_export] +macro_rules! tuple_extract_index { + ($x:expr, $i:expr) => { + $x.$i + }; +} + +// A macro that maps tuple (a, b, c...) to produce (MaybePlacebo::from(a), MaybePlacebo::from(b), MaybePlacebo::from(c)) +// maybe_placebos_from!(expr, 0, 1, 2, 3) +// = let x = expr; +// (MaybePlacebo::from(x.0), MaybePlacebo::from(x.1), MaybePlacebo::from(x.2), MaybePlacebo::from(x.3)) +#[macro_export] +macro_rules! maybe_placebos_from { + ($x:expr, $($i:tt), *) => { + { + let x = $x; + ( + $( $crate::MaybePlacebo::from(x.$i), )* + ) + } + } +} + +//////////////// +// Coercion +//////////////// + +pub fn upcast_object() -> Rc) -> Object> + where A : UpcastObject +{ + Rc::new(|x: Object| x.as_ref().upcast()) +} + +pub fn upcast() -> Rc) -> Ptr> + where A: Upcast +{ + Rc::new(|x: Ptr| read!(x).upcast()) +} + +pub fn upcast_id() -> Rc A> +{ + Rc::new(|x: A| x) +} + +pub fn rc_coerce(f: Rc U>) -> Rc) -> Rc> { + Rc::new(move |x: Rc| Rc::new(f.as_ref()(x.as_ref().clone()))) +} +pub fn box_coerce(f: Box U>) -> Box) -> Box> { + Box::new(move |x: Box| Box::new(f.as_ref()(x.as_ref().clone()))) +} + +pub fn fn1_coerce( + a_to_r: Rc R + 'static>) -> + Rc A>) -> Rc R> + 'static> { + Rc::new(move |t_to_a: Rc A>| { + let a_to_r = a_to_r.clone(); + let t_to_a = t_to_a.clone(); + let r: Rc R + 'static> = Rc::new(move |t: &T| a_to_r(t_to_a(t))); + r + }) +} + +// For pointers +pub trait Upcast { + fn upcast(&self) -> Ptr; +} +pub trait UpcastObject { + fn upcast(&self) -> Object; +} + +impl Upcast for T { + fn upcast(&self) -> Ptr { + Ptr::from_raw_nonnull(self as *const T as *mut T) + } +} +impl UpcastObject for T { + fn upcast(&self) -> Object { + Object::from_ref(self) + } +} + +#[macro_export] +macro_rules! Extends { + ($traitType: tt) => { + $traitType + ::dafny_runtime::Upcast + } +} + +#[macro_export] +macro_rules! UpcastFn { + ($B:ty) => { + fn upcast(&self) -> $crate::Ptr<$B> { + $crate::Ptr::from_raw_nonnull(self as *const Self as *mut Self as *mut $B) + } + }; +} + +#[macro_export] +macro_rules! UpcastObjectFn { + ($B:ty) => { + fn upcast(&self) -> $crate::Object<$B> { + $crate::Object::from_ref(self as &$B) + } + }; +} + + + +// It works only when there is no type parameters for $A... +#[macro_export] +macro_rules! UpcastDef { + ($A:ty, $B:ty) => { + impl $crate::Upcast<$B> for $A { + $crate::UpcastFn!($B); + } + }; + + ($A:ty, $B:ty, $($C: ty),*) => { + $crate::UpcastDef!($A, $B); + $crate::UpcastDef!($A, $($C),*); + } +} + +#[macro_export] +macro_rules! UpcastDefObject { + ($A:ty, $B:ty) => { + impl $crate::UpcastObject<$B> for $A { + $crate::UpcastObjectFn!($B); + } + }; + + ($A:ty, $B:ty, $($C: ty),*) => { + $crate::UpcastDefObject!($A, $B); + $crate::UpcastDefObject!($A, $($C),*); + } +} + +// Coercions for sets +impl Set +{ + pub fn coerce(f: Rc V>) -> Rc) -> Set> { + Rc::new(move |x: Set| { + // We need to upcast individual elements + let f2 = f.clone(); + let mut new_set: HashSet = HashSet::::default(); + for value in x.data.iter() { + new_set.insert(f2(value.clone())); + } + Set::from_hashset_owned(new_set) + }) + } +} + +// Coercions for sequences +impl Sequence +{ + pub fn coerce(f: Rc V>) -> Rc) -> Sequence> { + // We need to upcast individual elements + Rc::new(move |x: Sequence| { + let mut new_seq: Vec = Vec::::default(); + let f2 = f.clone(); + for value in x.to_array().iter() { + new_seq.push(f2(value.clone())); + } + Sequence::from_array_owned(new_seq) + }) + } +} + +// Coercions for multisets +impl Multiset +{ + pub fn coerce(f: Rc V>) -> Rc) -> Multiset> { + // We need to upcast individual elements + Rc::new(move |x: Multiset| { + let f2 = f.clone(); + // We need to upcast individual elements + let mut new_multiset: HashMap = HashMap::::default(); + for (value, count) in x.data.into_iter() { + new_multiset.insert(f2(value), count.clone()); + } + Multiset::from_hashmap_owned(new_multiset) + }) + } +} + +// Coercions for Maps +impl Map +{ + pub fn coerce(f: Rc V>) -> Rc) -> Map> { + // We need to upcast individual elements + Rc::new(move |x: Map| { + let f2 = f.clone(); + let mut new_map: HashMap = HashMap::::default(); + for (key, value) in x.data.iter() { + new_map.insert(key.clone(), f2(value.clone())); + } + Map::from_hashmap_owned(new_map) + }) + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/src/system/mod.rs b/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/src/system/mod.rs new file mode 100644 index 000000000..d30d8671e --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/src/system/mod.rs @@ -0,0 +1,3771 @@ +#![allow(warnings, unconditional_panic)] +#![allow(nonstandard_style)] + +pub mod _System { + pub use crate::DafnyInt; + pub use crate::DafnyType; + pub use ::std::fmt::Debug; + pub use ::std::fmt::Formatter; + pub use ::std::fmt::Result; + pub use crate::DafnyPrint; + pub use ::std::rc::Rc; + pub use ::std::cmp::Eq; + pub use ::std::hash::Hash; + pub use ::std::hash::Hasher; + pub use ::std::default::Default; + pub use ::std::convert::AsRef; + pub use crate::SequenceIter; + pub use crate::seq; + + pub type nat = DafnyInt; + + #[derive(PartialEq, Clone)] + pub enum Tuple2 { + _T2 { + _0: T0, + _1: T1 + } + } + + impl Tuple2 { + pub fn _0(&self) -> &T0 { + match self { + Tuple2::_T2{_0, _1, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple2::_T2{_0, _1, } => _1, + } + } + } + + impl Debug + for Tuple2 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple2 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple2::_T2{_0, _1, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple2 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>) -> Rc) -> Tuple2> { + Rc::new(move |this: Self| -> Tuple2{ + match this { + Tuple2::_T2{_0, _1, } => { + Tuple2::_T2 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1) + } + }, + } + }) + } + } + + impl Eq + for Tuple2 {} + + impl Hash + for Tuple2 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple2::_T2{_0, _1, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state) + }, + } + } + } + + impl Default + for Tuple2 { + fn default() -> Tuple2 { + Tuple2::_T2 { + _0: Default::default(), + _1: Default::default() + } + } + } + + impl AsRef> + for &Tuple2 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple0 { + _T0 {} + } + + impl Tuple0 {} + + impl Debug + for Tuple0 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple0 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple0::_T0{} => { + write!(_formatter, "")?; + Ok(()) + }, + } + } + } + + impl Tuple0 { + pub fn _AllSingletonConstructors() -> SequenceIter> { + seq![Rc::new(Tuple0::_T0 {})].iter() + } + } + + impl Eq + for Tuple0 {} + + impl Hash + for Tuple0 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple0::_T0{} => { + + }, + } + } + } + + impl Default + for Tuple0 { + fn default() -> Tuple0 { + Tuple0::_T0 {} + } + } + + impl AsRef + for &Tuple0 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple1 { + _T1 { + _0: T0 + } + } + + impl Tuple1 { + pub fn _0(&self) -> &T0 { + match self { + Tuple1::_T1{_0, } => _0, + } + } + } + + impl Debug + for Tuple1 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple1 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple1::_T1{_0, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple1 { + pub fn coerce(f_0: Rc r#__T0 + 'static>) -> Rc) -> Tuple1> { + Rc::new(move |this: Self| -> Tuple1{ + match this { + Tuple1::_T1{_0, } => { + Tuple1::_T1 { + _0: f_0.clone()(_0) + } + }, + } + }) + } + } + + impl Eq + for Tuple1 {} + + impl Hash + for Tuple1 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple1::_T1{_0, } => { + Hash::hash(_0, _state) + }, + } + } + } + + impl Default + for Tuple1 { + fn default() -> Tuple1 { + Tuple1::_T1 { + _0: Default::default() + } + } + } + + impl AsRef> + for &Tuple1 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple3 { + _T3 { + _0: T0, + _1: T1, + _2: T2 + } + } + + impl Tuple3 { + pub fn _0(&self) -> &T0 { + match self { + Tuple3::_T3{_0, _1, _2, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple3::_T3{_0, _1, _2, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple3::_T3{_0, _1, _2, } => _2, + } + } + } + + impl Debug + for Tuple3 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple3 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple3::_T3{_0, _1, _2, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple3 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>) -> Rc) -> Tuple3> { + Rc::new(move |this: Self| -> Tuple3{ + match this { + Tuple3::_T3{_0, _1, _2, } => { + Tuple3::_T3 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2) + } + }, + } + }) + } + } + + impl Eq + for Tuple3 {} + + impl Hash + for Tuple3 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple3::_T3{_0, _1, _2, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state) + }, + } + } + } + + impl Default + for Tuple3 { + fn default() -> Tuple3 { + Tuple3::_T3 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default() + } + } + } + + impl AsRef> + for &Tuple3 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple4 { + _T4 { + _0: T0, + _1: T1, + _2: T2, + _3: T3 + } + } + + impl Tuple4 { + pub fn _0(&self) -> &T0 { + match self { + Tuple4::_T4{_0, _1, _2, _3, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple4::_T4{_0, _1, _2, _3, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple4::_T4{_0, _1, _2, _3, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple4::_T4{_0, _1, _2, _3, } => _3, + } + } + } + + impl Debug + for Tuple4 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple4 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple4::_T4{_0, _1, _2, _3, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple4 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>) -> Rc) -> Tuple4> { + Rc::new(move |this: Self| -> Tuple4{ + match this { + Tuple4::_T4{_0, _1, _2, _3, } => { + Tuple4::_T4 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3) + } + }, + } + }) + } + } + + impl Eq + for Tuple4 {} + + impl Hash + for Tuple4 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple4::_T4{_0, _1, _2, _3, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state) + }, + } + } + } + + impl Default + for Tuple4 { + fn default() -> Tuple4 { + Tuple4::_T4 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default() + } + } + } + + impl AsRef> + for &Tuple4 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple5 { + _T5 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4 + } + } + + impl Tuple5 { + pub fn _0(&self) -> &T0 { + match self { + Tuple5::_T5{_0, _1, _2, _3, _4, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple5::_T5{_0, _1, _2, _3, _4, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple5::_T5{_0, _1, _2, _3, _4, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple5::_T5{_0, _1, _2, _3, _4, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple5::_T5{_0, _1, _2, _3, _4, } => _4, + } + } + } + + impl Debug + for Tuple5 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple5 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple5::_T5{_0, _1, _2, _3, _4, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple5 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>) -> Rc) -> Tuple5> { + Rc::new(move |this: Self| -> Tuple5{ + match this { + Tuple5::_T5{_0, _1, _2, _3, _4, } => { + Tuple5::_T5 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4) + } + }, + } + }) + } + } + + impl Eq + for Tuple5 {} + + impl Hash + for Tuple5 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple5::_T5{_0, _1, _2, _3, _4, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state) + }, + } + } + } + + impl Default + for Tuple5 { + fn default() -> Tuple5 { + Tuple5::_T5 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default() + } + } + } + + impl AsRef> + for &Tuple5 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple6 { + _T6 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5 + } + } + + impl Tuple6 { + pub fn _0(&self) -> &T0 { + match self { + Tuple6::_T6{_0, _1, _2, _3, _4, _5, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple6::_T6{_0, _1, _2, _3, _4, _5, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple6::_T6{_0, _1, _2, _3, _4, _5, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple6::_T6{_0, _1, _2, _3, _4, _5, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple6::_T6{_0, _1, _2, _3, _4, _5, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple6::_T6{_0, _1, _2, _3, _4, _5, } => _5, + } + } + } + + impl Debug + for Tuple6 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple6 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple6::_T6{_0, _1, _2, _3, _4, _5, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple6 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>) -> Rc) -> Tuple6> { + Rc::new(move |this: Self| -> Tuple6{ + match this { + Tuple6::_T6{_0, _1, _2, _3, _4, _5, } => { + Tuple6::_T6 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5) + } + }, + } + }) + } + } + + impl Eq + for Tuple6 {} + + impl Hash + for Tuple6 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple6::_T6{_0, _1, _2, _3, _4, _5, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state) + }, + } + } + } + + impl Default + for Tuple6 { + fn default() -> Tuple6 { + Tuple6::_T6 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default() + } + } + } + + impl AsRef> + for &Tuple6 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple7 { + _T7 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6 + } + } + + impl Tuple7 { + pub fn _0(&self) -> &T0 { + match self { + Tuple7::_T7{_0, _1, _2, _3, _4, _5, _6, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple7::_T7{_0, _1, _2, _3, _4, _5, _6, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple7::_T7{_0, _1, _2, _3, _4, _5, _6, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple7::_T7{_0, _1, _2, _3, _4, _5, _6, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple7::_T7{_0, _1, _2, _3, _4, _5, _6, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple7::_T7{_0, _1, _2, _3, _4, _5, _6, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple7::_T7{_0, _1, _2, _3, _4, _5, _6, } => _6, + } + } + } + + impl Debug + for Tuple7 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple7 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple7::_T7{_0, _1, _2, _3, _4, _5, _6, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple7 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>) -> Rc) -> Tuple7> { + Rc::new(move |this: Self| -> Tuple7{ + match this { + Tuple7::_T7{_0, _1, _2, _3, _4, _5, _6, } => { + Tuple7::_T7 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6) + } + }, + } + }) + } + } + + impl Eq + for Tuple7 {} + + impl Hash + for Tuple7 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple7::_T7{_0, _1, _2, _3, _4, _5, _6, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state) + }, + } + } + } + + impl Default + for Tuple7 { + fn default() -> Tuple7 { + Tuple7::_T7 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default() + } + } + } + + impl AsRef> + for &Tuple7 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple8 { + _T8 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7 + } + } + + impl Tuple8 { + pub fn _0(&self) -> &T0 { + match self { + Tuple8::_T8{_0, _1, _2, _3, _4, _5, _6, _7, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple8::_T8{_0, _1, _2, _3, _4, _5, _6, _7, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple8::_T8{_0, _1, _2, _3, _4, _5, _6, _7, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple8::_T8{_0, _1, _2, _3, _4, _5, _6, _7, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple8::_T8{_0, _1, _2, _3, _4, _5, _6, _7, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple8::_T8{_0, _1, _2, _3, _4, _5, _6, _7, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple8::_T8{_0, _1, _2, _3, _4, _5, _6, _7, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple8::_T8{_0, _1, _2, _3, _4, _5, _6, _7, } => _7, + } + } + } + + impl Debug + for Tuple8 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple8 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple8::_T8{_0, _1, _2, _3, _4, _5, _6, _7, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple8 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>) -> Rc) -> Tuple8> { + Rc::new(move |this: Self| -> Tuple8{ + match this { + Tuple8::_T8{_0, _1, _2, _3, _4, _5, _6, _7, } => { + Tuple8::_T8 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7) + } + }, + } + }) + } + } + + impl Eq + for Tuple8 {} + + impl Hash + for Tuple8 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple8::_T8{_0, _1, _2, _3, _4, _5, _6, _7, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state) + }, + } + } + } + + impl Default + for Tuple8 { + fn default() -> Tuple8 { + Tuple8::_T8 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default() + } + } + } + + impl AsRef> + for &Tuple8 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple9 { + _T9 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7, + _8: T8 + } + } + + impl Tuple9 { + pub fn _0(&self) -> &T0 { + match self { + Tuple9::_T9{_0, _1, _2, _3, _4, _5, _6, _7, _8, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple9::_T9{_0, _1, _2, _3, _4, _5, _6, _7, _8, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple9::_T9{_0, _1, _2, _3, _4, _5, _6, _7, _8, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple9::_T9{_0, _1, _2, _3, _4, _5, _6, _7, _8, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple9::_T9{_0, _1, _2, _3, _4, _5, _6, _7, _8, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple9::_T9{_0, _1, _2, _3, _4, _5, _6, _7, _8, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple9::_T9{_0, _1, _2, _3, _4, _5, _6, _7, _8, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple9::_T9{_0, _1, _2, _3, _4, _5, _6, _7, _8, } => _7, + } + } + pub fn _8(&self) -> &T8 { + match self { + Tuple9::_T9{_0, _1, _2, _3, _4, _5, _6, _7, _8, } => _8, + } + } + } + + impl Debug + for Tuple9 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple9 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple9::_T9{_0, _1, _2, _3, _4, _5, _6, _7, _8, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_8, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple9 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>, f_8: Rc r#__T8 + 'static>) -> Rc) -> Tuple9> { + Rc::new(move |this: Self| -> Tuple9{ + match this { + Tuple9::_T9{_0, _1, _2, _3, _4, _5, _6, _7, _8, } => { + Tuple9::_T9 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7), + _8: f_8.clone()(_8) + } + }, + } + }) + } + } + + impl Eq + for Tuple9 {} + + impl Hash + for Tuple9 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple9::_T9{_0, _1, _2, _3, _4, _5, _6, _7, _8, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state); + Hash::hash(_8, _state) + }, + } + } + } + + impl Default + for Tuple9 { + fn default() -> Tuple9 { + Tuple9::_T9 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default(), + _8: Default::default() + } + } + } + + impl AsRef> + for &Tuple9 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple10 { + _T10 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7, + _8: T8, + _9: T9 + } + } + + impl Tuple10 { + pub fn _0(&self) -> &T0 { + match self { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => _7, + } + } + pub fn _8(&self) -> &T8 { + match self { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => _8, + } + } + pub fn _9(&self) -> &T9 { + match self { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => _9, + } + } + } + + impl Debug + for Tuple10 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple10 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_8, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_9, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple10 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>, f_8: Rc r#__T8 + 'static>, f_9: Rc r#__T9 + 'static>) -> Rc) -> Tuple10> { + Rc::new(move |this: Self| -> Tuple10{ + match this { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => { + Tuple10::_T10 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7), + _8: f_8.clone()(_8), + _9: f_9.clone()(_9) + } + }, + } + }) + } + } + + impl Eq + for Tuple10 {} + + impl Hash + for Tuple10 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple10::_T10{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state); + Hash::hash(_8, _state); + Hash::hash(_9, _state) + }, + } + } + } + + impl Default + for Tuple10 { + fn default() -> Tuple10 { + Tuple10::_T10 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default(), + _8: Default::default(), + _9: Default::default() + } + } + } + + impl AsRef> + for &Tuple10 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple11 { + _T11 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7, + _8: T8, + _9: T9, + _10: T10 + } + } + + impl Tuple11 { + pub fn _0(&self) -> &T0 { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => _7, + } + } + pub fn _8(&self) -> &T8 { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => _8, + } + } + pub fn _9(&self) -> &T9 { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => _9, + } + } + pub fn _10(&self) -> &T10 { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => _10, + } + } + } + + impl Debug + for Tuple11 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple11 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_8, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_9, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_10, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple11 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>, f_8: Rc r#__T8 + 'static>, f_9: Rc r#__T9 + 'static>, f_10: Rc r#__T10 + 'static>) -> Rc) -> Tuple11> { + Rc::new(move |this: Self| -> Tuple11{ + match this { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => { + Tuple11::_T11 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7), + _8: f_8.clone()(_8), + _9: f_9.clone()(_9), + _10: f_10.clone()(_10) + } + }, + } + }) + } + } + + impl Eq + for Tuple11 {} + + impl Hash + for Tuple11 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple11::_T11{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state); + Hash::hash(_8, _state); + Hash::hash(_9, _state); + Hash::hash(_10, _state) + }, + } + } + } + + impl Default + for Tuple11 { + fn default() -> Tuple11 { + Tuple11::_T11 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default(), + _8: Default::default(), + _9: Default::default(), + _10: Default::default() + } + } + } + + impl AsRef> + for &Tuple11 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple12 { + _T12 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7, + _8: T8, + _9: T9, + _10: T10, + _11: T11 + } + } + + impl Tuple12 { + pub fn _0(&self) -> &T0 { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => _7, + } + } + pub fn _8(&self) -> &T8 { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => _8, + } + } + pub fn _9(&self) -> &T9 { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => _9, + } + } + pub fn _10(&self) -> &T10 { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => _10, + } + } + pub fn _11(&self) -> &T11 { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => _11, + } + } + } + + impl Debug + for Tuple12 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple12 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_8, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_9, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_10, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_11, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple12 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>, f_8: Rc r#__T8 + 'static>, f_9: Rc r#__T9 + 'static>, f_10: Rc r#__T10 + 'static>, f_11: Rc r#__T11 + 'static>) -> Rc) -> Tuple12> { + Rc::new(move |this: Self| -> Tuple12{ + match this { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => { + Tuple12::_T12 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7), + _8: f_8.clone()(_8), + _9: f_9.clone()(_9), + _10: f_10.clone()(_10), + _11: f_11.clone()(_11) + } + }, + } + }) + } + } + + impl Eq + for Tuple12 {} + + impl Hash + for Tuple12 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple12::_T12{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state); + Hash::hash(_8, _state); + Hash::hash(_9, _state); + Hash::hash(_10, _state); + Hash::hash(_11, _state) + }, + } + } + } + + impl Default + for Tuple12 { + fn default() -> Tuple12 { + Tuple12::_T12 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default(), + _8: Default::default(), + _9: Default::default(), + _10: Default::default(), + _11: Default::default() + } + } + } + + impl AsRef> + for &Tuple12 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple13 { + _T13 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7, + _8: T8, + _9: T9, + _10: T10, + _11: T11, + _12: T12 + } + } + + impl Tuple13 { + pub fn _0(&self) -> &T0 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _7, + } + } + pub fn _8(&self) -> &T8 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _8, + } + } + pub fn _9(&self) -> &T9 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _9, + } + } + pub fn _10(&self) -> &T10 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _10, + } + } + pub fn _11(&self) -> &T11 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _11, + } + } + pub fn _12(&self) -> &T12 { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => _12, + } + } + } + + impl Debug + for Tuple13 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple13 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_8, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_9, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_10, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_11, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_12, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple13 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>, f_8: Rc r#__T8 + 'static>, f_9: Rc r#__T9 + 'static>, f_10: Rc r#__T10 + 'static>, f_11: Rc r#__T11 + 'static>, f_12: Rc r#__T12 + 'static>) -> Rc) -> Tuple13> { + Rc::new(move |this: Self| -> Tuple13{ + match this { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => { + Tuple13::_T13 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7), + _8: f_8.clone()(_8), + _9: f_9.clone()(_9), + _10: f_10.clone()(_10), + _11: f_11.clone()(_11), + _12: f_12.clone()(_12) + } + }, + } + }) + } + } + + impl Eq + for Tuple13 {} + + impl Hash + for Tuple13 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple13::_T13{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state); + Hash::hash(_8, _state); + Hash::hash(_9, _state); + Hash::hash(_10, _state); + Hash::hash(_11, _state); + Hash::hash(_12, _state) + }, + } + } + } + + impl Default + for Tuple13 { + fn default() -> Tuple13 { + Tuple13::_T13 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default(), + _8: Default::default(), + _9: Default::default(), + _10: Default::default(), + _11: Default::default(), + _12: Default::default() + } + } + } + + impl AsRef> + for &Tuple13 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple14 { + _T14 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7, + _8: T8, + _9: T9, + _10: T10, + _11: T11, + _12: T12, + _13: T13 + } + } + + impl Tuple14 { + pub fn _0(&self) -> &T0 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _7, + } + } + pub fn _8(&self) -> &T8 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _8, + } + } + pub fn _9(&self) -> &T9 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _9, + } + } + pub fn _10(&self) -> &T10 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _10, + } + } + pub fn _11(&self) -> &T11 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _11, + } + } + pub fn _12(&self) -> &T12 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _12, + } + } + pub fn _13(&self) -> &T13 { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => _13, + } + } + } + + impl Debug + for Tuple14 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple14 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_8, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_9, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_10, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_11, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_12, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_13, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple14 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>, f_8: Rc r#__T8 + 'static>, f_9: Rc r#__T9 + 'static>, f_10: Rc r#__T10 + 'static>, f_11: Rc r#__T11 + 'static>, f_12: Rc r#__T12 + 'static>, f_13: Rc r#__T13 + 'static>) -> Rc) -> Tuple14> { + Rc::new(move |this: Self| -> Tuple14{ + match this { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => { + Tuple14::_T14 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7), + _8: f_8.clone()(_8), + _9: f_9.clone()(_9), + _10: f_10.clone()(_10), + _11: f_11.clone()(_11), + _12: f_12.clone()(_12), + _13: f_13.clone()(_13) + } + }, + } + }) + } + } + + impl Eq + for Tuple14 {} + + impl Hash + for Tuple14 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple14::_T14{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state); + Hash::hash(_8, _state); + Hash::hash(_9, _state); + Hash::hash(_10, _state); + Hash::hash(_11, _state); + Hash::hash(_12, _state); + Hash::hash(_13, _state) + }, + } + } + } + + impl Default + for Tuple14 { + fn default() -> Tuple14 { + Tuple14::_T14 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default(), + _8: Default::default(), + _9: Default::default(), + _10: Default::default(), + _11: Default::default(), + _12: Default::default(), + _13: Default::default() + } + } + } + + impl AsRef> + for &Tuple14 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple15 { + _T15 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7, + _8: T8, + _9: T9, + _10: T10, + _11: T11, + _12: T12, + _13: T13, + _14: T14 + } + } + + impl Tuple15 { + pub fn _0(&self) -> &T0 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _7, + } + } + pub fn _8(&self) -> &T8 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _8, + } + } + pub fn _9(&self) -> &T9 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _9, + } + } + pub fn _10(&self) -> &T10 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _10, + } + } + pub fn _11(&self) -> &T11 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _11, + } + } + pub fn _12(&self) -> &T12 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _12, + } + } + pub fn _13(&self) -> &T13 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _13, + } + } + pub fn _14(&self) -> &T14 { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => _14, + } + } + } + + impl Debug + for Tuple15 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple15 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_8, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_9, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_10, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_11, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_12, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_13, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_14, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple15 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>, f_8: Rc r#__T8 + 'static>, f_9: Rc r#__T9 + 'static>, f_10: Rc r#__T10 + 'static>, f_11: Rc r#__T11 + 'static>, f_12: Rc r#__T12 + 'static>, f_13: Rc r#__T13 + 'static>, f_14: Rc r#__T14 + 'static>) -> Rc) -> Tuple15> { + Rc::new(move |this: Self| -> Tuple15{ + match this { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => { + Tuple15::_T15 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7), + _8: f_8.clone()(_8), + _9: f_9.clone()(_9), + _10: f_10.clone()(_10), + _11: f_11.clone()(_11), + _12: f_12.clone()(_12), + _13: f_13.clone()(_13), + _14: f_14.clone()(_14) + } + }, + } + }) + } + } + + impl Eq + for Tuple15 {} + + impl Hash + for Tuple15 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple15::_T15{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state); + Hash::hash(_8, _state); + Hash::hash(_9, _state); + Hash::hash(_10, _state); + Hash::hash(_11, _state); + Hash::hash(_12, _state); + Hash::hash(_13, _state); + Hash::hash(_14, _state) + }, + } + } + } + + impl Default + for Tuple15 { + fn default() -> Tuple15 { + Tuple15::_T15 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default(), + _8: Default::default(), + _9: Default::default(), + _10: Default::default(), + _11: Default::default(), + _12: Default::default(), + _13: Default::default(), + _14: Default::default() + } + } + } + + impl AsRef> + for &Tuple15 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple16 { + _T16 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7, + _8: T8, + _9: T9, + _10: T10, + _11: T11, + _12: T12, + _13: T13, + _14: T14, + _15: T15 + } + } + + impl Tuple16 { + pub fn _0(&self) -> &T0 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _7, + } + } + pub fn _8(&self) -> &T8 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _8, + } + } + pub fn _9(&self) -> &T9 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _9, + } + } + pub fn _10(&self) -> &T10 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _10, + } + } + pub fn _11(&self) -> &T11 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _11, + } + } + pub fn _12(&self) -> &T12 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _12, + } + } + pub fn _13(&self) -> &T13 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _13, + } + } + pub fn _14(&self) -> &T14 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _14, + } + } + pub fn _15(&self) -> &T15 { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => _15, + } + } + } + + impl Debug + for Tuple16 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple16 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_8, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_9, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_10, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_11, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_12, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_13, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_14, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_15, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple16 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>, f_8: Rc r#__T8 + 'static>, f_9: Rc r#__T9 + 'static>, f_10: Rc r#__T10 + 'static>, f_11: Rc r#__T11 + 'static>, f_12: Rc r#__T12 + 'static>, f_13: Rc r#__T13 + 'static>, f_14: Rc r#__T14 + 'static>, f_15: Rc r#__T15 + 'static>) -> Rc) -> Tuple16> { + Rc::new(move |this: Self| -> Tuple16{ + match this { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => { + Tuple16::_T16 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7), + _8: f_8.clone()(_8), + _9: f_9.clone()(_9), + _10: f_10.clone()(_10), + _11: f_11.clone()(_11), + _12: f_12.clone()(_12), + _13: f_13.clone()(_13), + _14: f_14.clone()(_14), + _15: f_15.clone()(_15) + } + }, + } + }) + } + } + + impl Eq + for Tuple16 {} + + impl Hash + for Tuple16 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple16::_T16{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state); + Hash::hash(_8, _state); + Hash::hash(_9, _state); + Hash::hash(_10, _state); + Hash::hash(_11, _state); + Hash::hash(_12, _state); + Hash::hash(_13, _state); + Hash::hash(_14, _state); + Hash::hash(_15, _state) + }, + } + } + } + + impl Default + for Tuple16 { + fn default() -> Tuple16 { + Tuple16::_T16 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default(), + _8: Default::default(), + _9: Default::default(), + _10: Default::default(), + _11: Default::default(), + _12: Default::default(), + _13: Default::default(), + _14: Default::default(), + _15: Default::default() + } + } + } + + impl AsRef> + for &Tuple16 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple17 { + _T17 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7, + _8: T8, + _9: T9, + _10: T10, + _11: T11, + _12: T12, + _13: T13, + _14: T14, + _15: T15, + _16: T16 + } + } + + impl Tuple17 { + pub fn _0(&self) -> &T0 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _7, + } + } + pub fn _8(&self) -> &T8 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _8, + } + } + pub fn _9(&self) -> &T9 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _9, + } + } + pub fn _10(&self) -> &T10 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _10, + } + } + pub fn _11(&self) -> &T11 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _11, + } + } + pub fn _12(&self) -> &T12 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _12, + } + } + pub fn _13(&self) -> &T13 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _13, + } + } + pub fn _14(&self) -> &T14 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _14, + } + } + pub fn _15(&self) -> &T15 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _15, + } + } + pub fn _16(&self) -> &T16 { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => _16, + } + } + } + + impl Debug + for Tuple17 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple17 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_8, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_9, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_10, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_11, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_12, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_13, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_14, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_15, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_16, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple17 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>, f_8: Rc r#__T8 + 'static>, f_9: Rc r#__T9 + 'static>, f_10: Rc r#__T10 + 'static>, f_11: Rc r#__T11 + 'static>, f_12: Rc r#__T12 + 'static>, f_13: Rc r#__T13 + 'static>, f_14: Rc r#__T14 + 'static>, f_15: Rc r#__T15 + 'static>, f_16: Rc r#__T16 + 'static>) -> Rc) -> Tuple17> { + Rc::new(move |this: Self| -> Tuple17{ + match this { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => { + Tuple17::_T17 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7), + _8: f_8.clone()(_8), + _9: f_9.clone()(_9), + _10: f_10.clone()(_10), + _11: f_11.clone()(_11), + _12: f_12.clone()(_12), + _13: f_13.clone()(_13), + _14: f_14.clone()(_14), + _15: f_15.clone()(_15), + _16: f_16.clone()(_16) + } + }, + } + }) + } + } + + impl Eq + for Tuple17 {} + + impl Hash + for Tuple17 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple17::_T17{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state); + Hash::hash(_8, _state); + Hash::hash(_9, _state); + Hash::hash(_10, _state); + Hash::hash(_11, _state); + Hash::hash(_12, _state); + Hash::hash(_13, _state); + Hash::hash(_14, _state); + Hash::hash(_15, _state); + Hash::hash(_16, _state) + }, + } + } + } + + impl Default + for Tuple17 { + fn default() -> Tuple17 { + Tuple17::_T17 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default(), + _8: Default::default(), + _9: Default::default(), + _10: Default::default(), + _11: Default::default(), + _12: Default::default(), + _13: Default::default(), + _14: Default::default(), + _15: Default::default(), + _16: Default::default() + } + } + } + + impl AsRef> + for &Tuple17 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple18 { + _T18 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7, + _8: T8, + _9: T9, + _10: T10, + _11: T11, + _12: T12, + _13: T13, + _14: T14, + _15: T15, + _16: T16, + _17: T17 + } + } + + impl Tuple18 { + pub fn _0(&self) -> &T0 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _7, + } + } + pub fn _8(&self) -> &T8 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _8, + } + } + pub fn _9(&self) -> &T9 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _9, + } + } + pub fn _10(&self) -> &T10 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _10, + } + } + pub fn _11(&self) -> &T11 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _11, + } + } + pub fn _12(&self) -> &T12 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _12, + } + } + pub fn _13(&self) -> &T13 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _13, + } + } + pub fn _14(&self) -> &T14 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _14, + } + } + pub fn _15(&self) -> &T15 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _15, + } + } + pub fn _16(&self) -> &T16 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _16, + } + } + pub fn _17(&self) -> &T17 { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => _17, + } + } + } + + impl Debug + for Tuple18 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple18 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_8, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_9, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_10, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_11, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_12, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_13, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_14, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_15, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_16, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_17, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple18 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>, f_8: Rc r#__T8 + 'static>, f_9: Rc r#__T9 + 'static>, f_10: Rc r#__T10 + 'static>, f_11: Rc r#__T11 + 'static>, f_12: Rc r#__T12 + 'static>, f_13: Rc r#__T13 + 'static>, f_14: Rc r#__T14 + 'static>, f_15: Rc r#__T15 + 'static>, f_16: Rc r#__T16 + 'static>, f_17: Rc r#__T17 + 'static>) -> Rc) -> Tuple18> { + Rc::new(move |this: Self| -> Tuple18{ + match this { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => { + Tuple18::_T18 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7), + _8: f_8.clone()(_8), + _9: f_9.clone()(_9), + _10: f_10.clone()(_10), + _11: f_11.clone()(_11), + _12: f_12.clone()(_12), + _13: f_13.clone()(_13), + _14: f_14.clone()(_14), + _15: f_15.clone()(_15), + _16: f_16.clone()(_16), + _17: f_17.clone()(_17) + } + }, + } + }) + } + } + + impl Eq + for Tuple18 {} + + impl Hash + for Tuple18 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple18::_T18{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state); + Hash::hash(_8, _state); + Hash::hash(_9, _state); + Hash::hash(_10, _state); + Hash::hash(_11, _state); + Hash::hash(_12, _state); + Hash::hash(_13, _state); + Hash::hash(_14, _state); + Hash::hash(_15, _state); + Hash::hash(_16, _state); + Hash::hash(_17, _state) + }, + } + } + } + + impl Default + for Tuple18 { + fn default() -> Tuple18 { + Tuple18::_T18 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default(), + _8: Default::default(), + _9: Default::default(), + _10: Default::default(), + _11: Default::default(), + _12: Default::default(), + _13: Default::default(), + _14: Default::default(), + _15: Default::default(), + _16: Default::default(), + _17: Default::default() + } + } + } + + impl AsRef> + for &Tuple18 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple19 { + _T19 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7, + _8: T8, + _9: T9, + _10: T10, + _11: T11, + _12: T12, + _13: T13, + _14: T14, + _15: T15, + _16: T16, + _17: T17, + _18: T18 + } + } + + impl Tuple19 { + pub fn _0(&self) -> &T0 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _7, + } + } + pub fn _8(&self) -> &T8 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _8, + } + } + pub fn _9(&self) -> &T9 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _9, + } + } + pub fn _10(&self) -> &T10 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _10, + } + } + pub fn _11(&self) -> &T11 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _11, + } + } + pub fn _12(&self) -> &T12 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _12, + } + } + pub fn _13(&self) -> &T13 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _13, + } + } + pub fn _14(&self) -> &T14 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _14, + } + } + pub fn _15(&self) -> &T15 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _15, + } + } + pub fn _16(&self) -> &T16 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _16, + } + } + pub fn _17(&self) -> &T17 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _17, + } + } + pub fn _18(&self) -> &T18 { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => _18, + } + } + } + + impl Debug + for Tuple19 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple19 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_8, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_9, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_10, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_11, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_12, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_13, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_14, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_15, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_16, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_17, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_18, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple19 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>, f_8: Rc r#__T8 + 'static>, f_9: Rc r#__T9 + 'static>, f_10: Rc r#__T10 + 'static>, f_11: Rc r#__T11 + 'static>, f_12: Rc r#__T12 + 'static>, f_13: Rc r#__T13 + 'static>, f_14: Rc r#__T14 + 'static>, f_15: Rc r#__T15 + 'static>, f_16: Rc r#__T16 + 'static>, f_17: Rc r#__T17 + 'static>, f_18: Rc r#__T18 + 'static>) -> Rc) -> Tuple19> { + Rc::new(move |this: Self| -> Tuple19{ + match this { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => { + Tuple19::_T19 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7), + _8: f_8.clone()(_8), + _9: f_9.clone()(_9), + _10: f_10.clone()(_10), + _11: f_11.clone()(_11), + _12: f_12.clone()(_12), + _13: f_13.clone()(_13), + _14: f_14.clone()(_14), + _15: f_15.clone()(_15), + _16: f_16.clone()(_16), + _17: f_17.clone()(_17), + _18: f_18.clone()(_18) + } + }, + } + }) + } + } + + impl Eq + for Tuple19 {} + + impl Hash + for Tuple19 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple19::_T19{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state); + Hash::hash(_8, _state); + Hash::hash(_9, _state); + Hash::hash(_10, _state); + Hash::hash(_11, _state); + Hash::hash(_12, _state); + Hash::hash(_13, _state); + Hash::hash(_14, _state); + Hash::hash(_15, _state); + Hash::hash(_16, _state); + Hash::hash(_17, _state); + Hash::hash(_18, _state) + }, + } + } + } + + impl Default + for Tuple19 { + fn default() -> Tuple19 { + Tuple19::_T19 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default(), + _8: Default::default(), + _9: Default::default(), + _10: Default::default(), + _11: Default::default(), + _12: Default::default(), + _13: Default::default(), + _14: Default::default(), + _15: Default::default(), + _16: Default::default(), + _17: Default::default(), + _18: Default::default() + } + } + } + + impl AsRef> + for &Tuple19 { + fn as_ref(&self) -> Self { + self + } + } + + #[derive(PartialEq, Clone)] + pub enum Tuple20 { + _T20 { + _0: T0, + _1: T1, + _2: T2, + _3: T3, + _4: T4, + _5: T5, + _6: T6, + _7: T7, + _8: T8, + _9: T9, + _10: T10, + _11: T11, + _12: T12, + _13: T13, + _14: T14, + _15: T15, + _16: T16, + _17: T17, + _18: T18, + _19: T19 + } + } + + impl Tuple20 { + pub fn _0(&self) -> &T0 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _0, + } + } + pub fn _1(&self) -> &T1 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _1, + } + } + pub fn _2(&self) -> &T2 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _2, + } + } + pub fn _3(&self) -> &T3 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _3, + } + } + pub fn _4(&self) -> &T4 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _4, + } + } + pub fn _5(&self) -> &T5 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _5, + } + } + pub fn _6(&self) -> &T6 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _6, + } + } + pub fn _7(&self) -> &T7 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _7, + } + } + pub fn _8(&self) -> &T8 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _8, + } + } + pub fn _9(&self) -> &T9 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _9, + } + } + pub fn _10(&self) -> &T10 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _10, + } + } + pub fn _11(&self) -> &T11 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _11, + } + } + pub fn _12(&self) -> &T12 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _12, + } + } + pub fn _13(&self) -> &T13 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _13, + } + } + pub fn _14(&self) -> &T14 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _14, + } + } + pub fn _15(&self) -> &T15 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _15, + } + } + pub fn _16(&self) -> &T16 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _16, + } + } + pub fn _17(&self) -> &T17 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _17, + } + } + pub fn _18(&self) -> &T18 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _18, + } + } + pub fn _19(&self) -> &T19 { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => _19, + } + } + } + + impl Debug + for Tuple20 { + fn fmt(&self, f: &mut Formatter) -> Result { + DafnyPrint::fmt_print(self, f, true) + } + } + + impl DafnyPrint + for Tuple20 { + fn fmt_print(&self, _formatter: &mut Formatter, _in_seq: bool) -> std::fmt::Result { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => { + write!(_formatter, "(")?; + DafnyPrint::fmt_print(_0, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_1, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_2, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_3, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_4, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_5, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_6, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_7, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_8, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_9, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_10, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_11, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_12, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_13, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_14, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_15, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_16, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_17, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_18, _formatter, false)?; + write!(_formatter, ", ")?; + DafnyPrint::fmt_print(_19, _formatter, false)?; + write!(_formatter, ")")?; + Ok(()) + }, + } + } + } + + impl Tuple20 { + pub fn coerce(f_0: Rc r#__T0 + 'static>, f_1: Rc r#__T1 + 'static>, f_2: Rc r#__T2 + 'static>, f_3: Rc r#__T3 + 'static>, f_4: Rc r#__T4 + 'static>, f_5: Rc r#__T5 + 'static>, f_6: Rc r#__T6 + 'static>, f_7: Rc r#__T7 + 'static>, f_8: Rc r#__T8 + 'static>, f_9: Rc r#__T9 + 'static>, f_10: Rc r#__T10 + 'static>, f_11: Rc r#__T11 + 'static>, f_12: Rc r#__T12 + 'static>, f_13: Rc r#__T13 + 'static>, f_14: Rc r#__T14 + 'static>, f_15: Rc r#__T15 + 'static>, f_16: Rc r#__T16 + 'static>, f_17: Rc r#__T17 + 'static>, f_18: Rc r#__T18 + 'static>, f_19: Rc r#__T19 + 'static>) -> Rc) -> Tuple20> { + Rc::new(move |this: Self| -> Tuple20{ + match this { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => { + Tuple20::_T20 { + _0: f_0.clone()(_0), + _1: f_1.clone()(_1), + _2: f_2.clone()(_2), + _3: f_3.clone()(_3), + _4: f_4.clone()(_4), + _5: f_5.clone()(_5), + _6: f_6.clone()(_6), + _7: f_7.clone()(_7), + _8: f_8.clone()(_8), + _9: f_9.clone()(_9), + _10: f_10.clone()(_10), + _11: f_11.clone()(_11), + _12: f_12.clone()(_12), + _13: f_13.clone()(_13), + _14: f_14.clone()(_14), + _15: f_15.clone()(_15), + _16: f_16.clone()(_16), + _17: f_17.clone()(_17), + _18: f_18.clone()(_18), + _19: f_19.clone()(_19) + } + }, + } + }) + } + } + + impl Eq + for Tuple20 {} + + impl Hash + for Tuple20 { + fn hash<_H: Hasher>(&self, _state: &mut _H) { + match self { + Tuple20::_T20{_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, } => { + Hash::hash(_0, _state); + Hash::hash(_1, _state); + Hash::hash(_2, _state); + Hash::hash(_3, _state); + Hash::hash(_4, _state); + Hash::hash(_5, _state); + Hash::hash(_6, _state); + Hash::hash(_7, _state); + Hash::hash(_8, _state); + Hash::hash(_9, _state); + Hash::hash(_10, _state); + Hash::hash(_11, _state); + Hash::hash(_12, _state); + Hash::hash(_13, _state); + Hash::hash(_14, _state); + Hash::hash(_15, _state); + Hash::hash(_16, _state); + Hash::hash(_17, _state); + Hash::hash(_18, _state); + Hash::hash(_19, _state) + }, + } + } + } + + impl Default + for Tuple20 { + fn default() -> Tuple20 { + Tuple20::_T20 { + _0: Default::default(), + _1: Default::default(), + _2: Default::default(), + _3: Default::default(), + _4: Default::default(), + _5: Default::default(), + _6: Default::default(), + _7: Default::default(), + _8: Default::default(), + _9: Default::default(), + _10: Default::default(), + _11: Default::default(), + _12: Default::default(), + _13: Default::default(), + _14: Default::default(), + _15: Default::default(), + _16: Default::default(), + _17: Default::default(), + _18: Default::default(), + _19: Default::default() + } + } + } + + impl AsRef> + for &Tuple20 { + fn as_ref(&self) -> Self { + self + } + } +} \ No newline at end of file diff --git a/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/src/tests/mod.rs b/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/src/tests/mod.rs new file mode 100644 index 000000000..2d61e01e6 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/dafny_runtime_rust/src/tests/mod.rs @@ -0,0 +1,859 @@ +// Test module +#[cfg(test)] +mod tests { + use crate::*; + + #[test] + fn test_int() { + let x = int!(37); + assert_eq!(x.to_u8().unwrap(), truncate!(x.clone(), u8)); + assert_eq!(x.to_u16().unwrap(), truncate!(x.clone(), u16)); + assert_eq!(x.to_u32().unwrap(), truncate!(x.clone(), u32)); + assert_eq!(x.to_u64().unwrap(), truncate!(x.clone(), u64)); + assert_eq!(x.to_u128().unwrap(), truncate!(x.clone(), u128)); + assert_eq!(x.to_i8().unwrap(), truncate!(x.clone(), i8)); + assert_eq!(x.to_i16().unwrap(), truncate!(x.clone(), i16)); + assert_eq!(x.to_i32().unwrap(), truncate!(x.clone(), i32)); + assert_eq!(x.to_i64().unwrap(), truncate!(x.clone(), i64)); + assert_eq!(x.to_i128().unwrap(), truncate!(x.clone(), i128)); + } + + #[test] + fn test_sequence() { + let values = vec![1, 2, 3]; + let seq = Sequence::::from_array_owned(values.clone()); + assert_eq!(seq.cardinality_usize(), 3); + assert_eq!(seq.to_array(), values.into()); + + // Create a concat array, wrap it into a lazy one, get the i-th element, + // and verify that this operation flattened the array + let left = Sequence::::from_array_owned(vec![1, 2, 3]); + let right = Sequence::::from_array_owned(vec![4, 5, 6]); + let concat = Sequence::::new_concat_sequence(&left, &right); + + assert_eq!(concat.cardinality_usize(), 6); + match &concat { + Sequence::ConcatSequence { + boxed, + length, + left, + .. + } => { + assert_eq!(*length, 6); + assert_eq!(unsafe { &*left.get() }.cardinality_usize(), 3); + // Test that boxed is None + assert!(boxed.as_ref().clone().borrow().as_ref().is_none()); + } + _ => panic!("This should never happen"), + } + let value = concat.get_usize(0); + assert_eq!(value, 1); + match &concat { + crate::Sequence::ConcatSequence { boxed, .. } => { + assert_eq!( + *boxed.as_ref().clone().borrow().as_ref().unwrap().as_ref(), + vec![1, 2, 3, 4, 5, 6] + ); + } + _ => panic!("This should never happen"), + } + } + + #[test] + fn test_dafny_int() { + assert_eq!(int!(0).to_usize(), Some(0)); + assert_eq!(int!(42).to_usize(), Some(42)); + } + + #[test] + fn test_dafny_sequence_print() { + let hello: DafnyString = seq![ + DafnyChar('H'), + DafnyChar('e'), + DafnyChar('l'), + DafnyChar('l'), + DafnyChar('o') + ]; + assert_eq!(DafnyPrintWrapper(&hello).to_string(), "Hello"); + let hello: DafnyStringUTF16 = seq![ + DafnyCharUTF16(0x0048), + DafnyCharUTF16(0x0065), + DafnyCharUTF16(0x006c), + DafnyCharUTF16(0x006c), + DafnyCharUTF16(0x006f) + ]; + assert_eq!(DafnyPrintWrapper(&hello).to_string(), "Hello"); + assert_eq!(DafnyPrintWrapper(&string_of("Hello")).to_string(), "Hello"); + let hello = seq![1, 2, 3]; + assert_eq!(DafnyPrintWrapper(&hello).to_string(), "[1, 2, 3]"); + } + #[test] + fn test_dafny_sequence() { + let s = seq![55, 56, 57]; + assert_eq!(seq![55, 56] != s, true); + assert_eq!(s.cardinality_usize(), 3); + assert_eq!(s.cardinality(), int!(3)); + assert_eq!(s.get(&int!(1)), 56); + assert_eq!(s.slice(&int!(1), &int!(2)), seq![56]); + assert_eq!(s.take(&int!(2)), seq![55, 56]); + assert_eq!(s.drop(&int!(1)), seq![56, 57]); + assert_eq!(s.update_index(&int!(1), &8), seq![55, 8, 57]); + assert_eq!(s.concat(&seq![58, 59]), seq!(55, 56, 57, 58, 59)); + } + + #[test] + fn test_dafny_set() { + let s = set! {55, 56, 57, 56, 58}; + let t = set! {59, 58, 57}; + assert_eq!(s.cardinality_usize(), 4); + assert_eq!(s.cardinality(), int!(4)); + assert_eq!(s.contains(&55), true); + assert_eq!(s.contains(&54), false); + assert_eq!(s.merge(&set! {}), s); + assert_eq!(set! {}.merge(&s), s); + assert_eq!(s.merge(&t), set! {59, 58, 57, 56, 55}); + assert_eq!(s.intersect(&t), set! {57, 58}); + assert_eq!(s.subtract(&set! {}), s); + assert_eq!(set! {}.subtract(&s), set! {}); + let smt = s.subtract(&t); + assert_eq!(smt, set! {55, 56}); + assert_eq!(t.subtract(&s), set! {59}); + assert_eq!(s.disjoint(&set! {}), true); + assert_eq!(set! {}.disjoint(&s), true); + assert_eq!(s.disjoint(&t), false); + assert_eq!(t.disjoint(&s), false); + assert_eq!(smt.disjoint(&t), true); + assert_eq!(t.disjoint(&smt), true); + assert_eq!(s.elements(), s); + assert_eq!( + s.as_dafny_multiset(), + Multiset::from_array(&vec!(55, 56, 57, 58)) + ); + } + + #[test] + fn test_dafny_multisubset() { + let s = multiset! {55, 56, 57, 58, 59}; + let t = multiset! {55, 56, 57, 58}; + assert!(t < s); + assert!(t <= s); + assert!(s > t); + assert!(s >= t); + assert!(s != t); + assert!(t != s); + + assert!(!(t > s)); + assert!(!(t >= s)); + assert!(!(s < t)); + assert!(!(s <= t)); + assert!(!(s == t)); + assert!(!(t == s)); + + let s = multiset! {55, 56, 57, 58, 58}; + let t = multiset! {55, 56, 57, 58}; + assert!(t < s); + assert!(t <= s); + assert!(s > t); + assert!(s >= t); + assert!(s != t); + assert!(t != s); + + assert!(!(t > s)); + assert!(!(t >= s)); + assert!(!(s < t)); + assert!(!(s <= t)); + assert!(!(s == t)); + assert!(!(t == s)); + + let s = multiset! {55, 56, 57, 58, 59}; + let t = multiset! {55, 56, 57, 58, 59}; + assert!(t <= s); + assert!(s <= t); + assert!(s >= t); + assert!(t >= s); + assert!(s == t); + assert!(t == s); + + assert!(!(t < s)); + assert!(!(t > s)); + assert!(!(s > t)); + assert!(!(s < t)); + assert!(!(s != t)); + assert!(!(t != s)); + + let s = multiset! {55, 56, 57, 58, 59}; + let t = multiset! {55, 56, 57, 58, 58}; + assert!(s != t); + assert!(t != s); + + assert!(!(t < s)); + assert!(!(t <= s)); + assert!(!(t > s)); + assert!(!(t >= s)); + assert!(!(s < t)); + assert!(!(s <= t)); + assert!(!(s > t)); + assert!(!(s >= t)); + assert!(!(s == t)); + assert!(!(t == s)); + } + + #[test] + fn test_dafny_multiset() { + let s = multiset! {55, 56, 57, 56, 58}; + let t = multiset! {59, 58, 57, 56}; + assert_eq!(s.cardinality_usize(), 5); + assert_eq!(s.cardinality(), int!(5)); + assert_eq!(s.contains(&55), true); + assert_eq!(s.contains(&54), false); + assert_eq!(s.get(&54), int!(0)); + assert_eq!(s.get(&55), int!(1)); + assert_eq!(s.get(&56), int!(2)); + assert_eq!(s.get(&57), int!(1)); + assert_eq!(s.merge(&multiset! {}), s); + assert_eq!(multiset! {}.merge(&s), s); + let merged = multiset! {59, 58, 58, 57, 57, 56, 56, 56, 55}; + assert_eq!(s.merge(&t), merged); + assert_eq!(merged.cardinality_usize(), 9); + assert_eq!(s.intersect(&t), multiset! {57, 58, 56}); + assert_eq!(s.subtract(&multiset! {}), s); + assert_eq!(multiset! {}.subtract(&s), multiset! {}); + let smt = s.subtract(&t); + assert_eq!(smt, multiset! {55, 56}); + let tms = t.subtract(&s); + assert_eq!(tms, multiset! {59}); + assert_eq!(s.disjoint(&multiset! {}), true); + assert_eq!(multiset! {}.disjoint(&s), true); + assert_eq!(s.disjoint(&t), false); + assert_eq!(t.disjoint(&s), false); + assert_eq!(smt.disjoint(&t), false); + assert_eq!(t.disjoint(&smt), false); + assert_eq!(tms.disjoint(&s), true); + assert_eq!(s.disjoint(&tms), true); + assert_eq!(s.as_dafny_multiset(), s); + } + + #[test] + fn test_dafny_map() { + let m_empty: Map = map![]; + let m_3 = map![1 => 2, 3 => 6, 5 => 10]; + let k_3 = set! {1, 3, 5}; + let v_3 = set! {2, 6, 10}; + assert_eq!(m_empty.cardinality_usize(), 0); + assert_eq!(m_empty.cardinality(), int!(0)); + assert_eq!(m_3.cardinality_usize(), 3); + assert_eq!(m_3.cardinality(), int!(3)); + assert_eq!(m_3.contains(&1), true); + assert_eq!(m_3.contains(&0), false); + assert_eq!(m_3.keys(), k_3); + assert_eq!(m_3.values(), v_3); + assert_eq!(m_3.get(&1), 2); + assert_eq!(m_3.get_or_none(&1), Some(2)); + assert_eq!(m_3.get_or_none(&0), None); + let mut m_4 = m_3.update_index(&0, &2); + assert_eq!(m_3.contains(&0), false); + assert_eq!(m_4.contains(&0), true); + m_4 = m_4.update_index_owned(0, 7); + assert_eq!(m_4.contains(&0), true); + + assert_eq!(m_4.get(&0), 7); + assert_eq!(m_4.cardinality_usize(), 4); + assert_eq!(m_4.merge(&map![]), m_4); + assert_eq!(map![].merge(&m_4), m_4); + let m_5 = m_4.merge(&map![3 => 9, 6 => 12]); + assert_eq!(m_5.cardinality_usize(), 5); + println!("m_4 is {:?}", m_4); + assert_eq!(m_4.get(&3), 6); + assert_eq!(m_5.get(&3), 9); + assert_eq!(m_5.subtract(&set! {}), m_5); + let m_4b = m_5.subtract(&set! {3}); + assert_eq!(m_4b.cardinality_usize(), 4); + assert_eq!(m_4b.contains(&3), false) + } + + #[test] + fn test_dafny_array() { + let a = array![1, 2, 3]; + assert_eq!(crate::array::length_usize(a), 3); + assert_eq!(crate::array::length(a), int!(3)); + assert_eq!(array::get_usize(a, 0), 1); + assert_eq!(array::get_usize(a, 1), 2); + assert_eq!(array::get_usize(a, 2), 3); + array::update_usize(a, 0, 4); + array::update_usize(a, 1, 5); + array::update_usize(a, 2, 6); + assert_eq!(array::get_usize(a, 0), 4); + assert_eq!(array::get_usize(a, 1), 5); + assert_eq!(array::get_usize(a, 2), 6); + deallocate(a); + } + + #[test] + fn test_dafny_array_init() { + // test from_vec, and initialize + let mut v = Vec::new(); + v.push(1); + v.push(2); + v.push(3); + let a = array::from_vec(v); + assert_eq!(array::length_usize(a), 3); + assert_eq!(array::get_usize(a, 0), 1); + let v2 = array::initialize_usize(3, Rc::new(|i| i + 1)); + assert_eq!(array::length_usize(v2), 3); + assert_eq!(array::get_usize(v2, 0), 1); + assert_eq!(array::get_usize(v2, 1), 2); + assert_eq!(array::get_usize(v2, 2), 3); + array::update_usize(v2, 1, 10); + assert_eq!(array::get_usize(v2, 1), 10); + + + let v3 = array::initialize(&int!(3), Rc::new(|i| i.clone() + int!(1))); + assert_eq!(array::length_usize(v3), 3); + assert_eq!(array::get_usize(v3, 0), int!(1)); + assert_eq!(array::get_usize(v3, 1), int!(2)); + assert_eq!(array::get_usize(v3, 2), int!(3)); + array::update(v3, &int!(1), int!(10)); + assert_eq!(array::get_usize(v3, 1), int!(10)); + + deallocate(a); + deallocate(v2); + deallocate(v3); + } + + #[test] + fn test_array2() { + let p = Array2::::placebos(&int!(3), &int!(4)); + for i in 0..3 { + for j in 0..4 { + modify!(p).data[i][j] = MaybeUninit::new(int!(i + j)); + } + } + let p = Array2::construct(p); + assert_eq!(read!(p).length0_usize(), 3); + assert_eq!(read!(p).length1_usize(), 4); + let v = read!(p).to_vec(); + assert_eq!(v.len(), 3); + assert_eq!(v, vec![ + vec![int!(0), int!(1), int!(2), int!(3)], + vec![int!(1), int!(2), int!(3), int!(4)], + vec![int!(2), int!(3), int!(4), int!(5)]]); + + deallocate(p); + // Allocate an array whose first dimension is zero + let p = Array2::::placebos(&int!(0), &int!(4)); + let p = Array2::construct(p); + assert_eq!(read!(p).length0_usize(), 0); + assert_eq!(read!(p).length1_usize(), 4); + deallocate(p); + } + + #[test] + fn test_array3() { + let a = Array3::::placebos(&int!(3), &int!(2), &int!(4)); + for i in 0..3 { + for j in 0..2 { + for k in 0..4 { + modify!(a).data[i][j][k] = MaybeUninit::new(DafnyInt::from(i * j + k)); + } + } + } + let a = Array3::construct(a); + assert_eq!(read!(a).length0(), int!(3)); + assert_eq!(read!(a).length1(), int!(2)); + assert_eq!(read!(a).length2(), int!(4)); + let v = read!(a).to_vec(); + assert_eq!(v.len(), 3); + for i in 0..3 { + for j in 0..2 { + for k in 0..4 { + assert_eq!(read!(a).data[i][j][k], DafnyInt::from(i * j + k)); + assert_eq!(v[i][j][k], DafnyInt::from(i * j + k)); + } + } + } + deallocate(a); + // Even if the first two dimensions are zero, the third dimension should not be zero + let a = Array3::::placebos(&int!(0), &int!(0), &int!(4)); + let a = Array3::construct(a); + assert_eq!(read!(a).length0(), int!(0)); + assert_eq!(read!(a).length1(), int!(0)); + assert_eq!(read!(a).length2(), int!(4)); + deallocate(a); + } + + struct ClassWrapper { + /*var*/ t: T, + /*var*/ x: crate::DafnyInt, + /*const*/ next: Ptr>, + /*const*/ constant: crate::DafnyInt, + } + impl ClassWrapper { + fn constant_plus_x(&self) -> crate::DafnyInt { + self.constant.clone() + self.x.clone() + } + fn increment_x(&mut self) { + self.x = self.x.clone() + int!(1); + } + } + + impl ClassWrapper { + fn constructor(t: T) -> Ptr> { + let this = crate::allocate::>(); + update_field_nodrop!(this, t, t); + update_field_nodrop!(this, next, this); + // If x is assigned twice, we need to keep track of whether it's assigned + // like in methods. + let mut x_assigned = false; + update_field_uninit!(this, x, x_assigned, int!(2)); + update_field_uninit!(this, x, x_assigned, int!(1)); + // If we can prove that x is assigned, we can even write this + modify!(this).x = int!(0); + update_field_nodrop!(this, constant, int!(42)); + this + } + } + + impl Upcast for ClassWrapper { + UpcastFn!(dyn Any); + } + impl UpcastObject for ClassWrapper { + UpcastObjectFn!(dyn Any); + } + + #[test] + #[allow(unused_unsafe)] + fn test_class_wrapper() { + let c: Ptr> = ClassWrapper::constructor(53); + assert_eq!(read!(c).constant, int!(42)); + assert_eq!(read!(c).t, 53); + assert_eq!(read!(c).x, int!(0)); + assert_eq!(read!(read!(c).next).t, 53); + assert_eq!(read!(c).constant_plus_x(), int!(42)); + modify!(c).increment_x(); + assert_eq!(read!(c).constant_plus_x(), int!(43)); + modify!(c).x = int!(40); + assert_eq!(read!(c).constant_plus_x(), int!(82)); + modify!(c).t = 54; + assert_eq!(read!(c).t, 54); + let x_copy = read!(c).x.clone(); + assert_eq!(Rc::strong_count(&x_copy.data), 2); + deallocate(c); + assert_eq!(Rc::strong_count(&x_copy.data), 1); + } + + // Requires test1 || test2 + // We will not do that as it enables the compiler to assume that t contains a valid Rc when it does not. + // Prefer MaybePlacebo + fn assign_lazy_in_method(test1: bool, test2: bool) -> Rc { + let mut t = MaybePlacebo::>::new(); + if test1 { + t = MaybePlacebo::from(Rc::new(5 as i32)); + } + if test2 { + t = MaybePlacebo::from(Rc::new( + 7 as i32 + if test1 { *MaybePlacebo::read(&t) } else { 0 }, + )); + } + println!("{}", MaybePlacebo::read(&t)); + MaybePlacebo::read(&t) + } + + #[test] + fn assign_lazy_in_method_test() { + let mut t = assign_lazy_in_method(true, false); + assert_eq!(*t, 5); + t = assign_lazy_in_method(false, true); + assert_eq!(*t, 7); + t = assign_lazy_in_method(true, true); + assert_eq!(*t, 12); + } + + fn override_placebo(o: T, do_override: bool) { + let mut _x: MaybePlacebo = MaybePlacebo::::new(); + if do_override { + _x = MaybePlacebo::from(o.clone()); + } + } + + #[test] + fn test_placebo() { + override_placebo::>(Rc::new(BigInt::from(1)), false); + override_placebo::>(Rc::new(BigInt::from(1)), true); + override_placebo::(int!(1), false); + override_placebo::(int!(1), true); + let _x: MaybePlacebo>> = + MaybePlacebo::>>::new(); + //let mut f: Rc Class> = Class> as Placebo>::new(); + } + + #[test] + fn test_maybe_placebos_from() { + let x = (1, 2, 3, 4); + let (a, b, c, d) = maybe_placebos_from!(x, 0, 1, 2, 3); + assert_eq!(a.read(), 1); + assert_eq!(b.read(), 2); + assert_eq!(c.read(), 3); + assert_eq!(d.read(), 4); + } + + #[test] + fn test_coercion_immutable() { + let o = ClassWrapper::::constructor(1); + let a: Ptr = Upcast::::upcast(read!(o)); + assert_eq!(cast!(a, ClassWrapper), o); + let seq_o = seq![o]; + let seq_a = Sequence::>>::coerce(upcast::, dyn Any>())(seq_o); + assert_eq!(cast!(seq_a.get_usize(0), ClassWrapper), o); + let set_o = set! {o}; + let set_a = Set::>>::coerce(upcast::, dyn Any>())(set_o); + assert_eq!(cast!(set_a.peek(), ClassWrapper), o); + let multiset_o = multiset! {o, o}; + let multiset_a = Multiset::>>::coerce(upcast::, dyn Any>())(multiset_o); + assert_eq!(cast!(multiset_a.peek(), ClassWrapper), o); + let map_o = map![1 => o, 2 => o]; + let map_a = Map::>>::coerce(upcast::, dyn Any>())(map_o); + assert_eq!(cast!(map_a.get(&1), ClassWrapper), o); + deallocate(o); + } + + #[test] + fn test_defaults() { + let set_i32 = as Default>::default(); + let seq_i32 = as Default>::default(); + let map_i32 = as Default>::default(); + let multiset_i32 = as Default>::default(); + assert_eq!(set_i32.cardinality_usize(), 0); + assert_eq!(seq_i32.cardinality_usize(), 0); + assert_eq!(map_i32.cardinality_usize(), 0); + assert_eq!(multiset_i32.cardinality_usize(), 0); + } + + #[test] + fn test_nontrivial_defaults() { + let set_i32 = as NontrivialDefault>::nontrivial_default(); + let seq_i32 = as NontrivialDefault>::nontrivial_default(); + let map_i32 = as NontrivialDefault>::nontrivial_default(); + let multiset_i32 = as NontrivialDefault>::nontrivial_default(); + assert_eq!(set_i32.cardinality_usize(), 0); + assert_eq!(seq_i32.cardinality_usize(), 0); + assert_eq!(map_i32.cardinality_usize(), 0); + assert_eq!(multiset_i32.cardinality_usize(), 0); + let ptr_i32 = as NontrivialDefault>::nontrivial_default(); + assert_eq!(ptr_i32, Ptr::::null()); + } + + #[test] + fn test_function_wrappers() { + let f: Rc i32> = Rc::new(|i: i32| i + 1); + let g = f.clone(); + let _h = seq![g]; + } + + #[test] + fn test_forall_exists() { + assert!(integer_range(int!(0), int!(10)) + .all(Rc::new(|i: DafnyInt| i.clone() < int!(10)).as_ref())); + assert!(!integer_range(int!(0), int!(11)) + .all(Rc::new(|i: DafnyInt| i.clone() < int!(10)).as_ref())); + assert!(!integer_range(int!(0), int!(10)) + .any(Rc::new(|i: DafnyInt| i.clone() >= int!(10)).as_ref())); + assert!(integer_range(int!(0), int!(11)) + .any(Rc::new(|i: DafnyInt| i.clone() >= int!(10)).as_ref())); + + assert!(integer_range(int!(0), int!(10)).all( + Rc::new(|i: DafnyInt| !(i.clone() % int!(4) == int!(0)) + || i.clone() < int!(10) && i.clone() % int!(2) == int!(0)) + .as_ref() + )); + assert!(integer_range(int!(0), int!(11)).all( + Rc::new(|i: DafnyInt| !(i.clone() % int!(4) == int!(0)) + || i.clone() < int!(10) && i.clone() % int!(2) == int!(0)) + .as_ref() + )); + assert!(!integer_range(int!(0), int!(10)).any( + Rc::new(|i: DafnyInt| i.clone() % int!(4) == int!(0) + && i.clone() >= int!(10) + && i.clone() % int!(2) == int!(0)) + .as_ref() + )); + assert!(!integer_range(int!(0), int!(11)).any( + Rc::new(|i: DafnyInt| i.clone() % int!(4) == int!(0) + && i.clone() >= int!(10) + && i.clone() % int!(2) == int!(0)) + .as_ref() + )); + + assert!(exact_range(10).all(Rc::new(|i: i32| i == 10).as_ref())); + assert!(exact_range(10).any(Rc::new(|i: i32| i == 10).as_ref())); + assert!(!exact_range(10).all(Rc::new(|i: i32| i != 10).as_ref())); + assert!(!exact_range(10).any(Rc::new(|i: i32| i != 10).as_ref())); + + assert!(seq![1, 3, 5, 7] + .iter() + .all(Rc::new(|i: u32| i % 2 == 1).as_ref())); + assert!(!seq![1, 3, 5, 7] + .iter() + .any(Rc::new(|i: u32| i % 2 == 0).as_ref())); + assert!(!seq![1, 3, 5, 7, 8] + .iter() + .all(Rc::new(|i: u32| i % 2 == 1).as_ref())); + assert!(seq![1, 3, 5, 7, 8] + .iter() + .any(Rc::new(|i: u32| i % 2 == 0).as_ref())); + + assert!(set! {1, 3, 5, 7} + .iter() + .cloned() + .all(Rc::new(|i: u32| i % 2 == 1).as_ref())); + assert!(!set! {1, 3, 5, 7} + .iter() + .cloned() + .any(Rc::new(|i: u32| i % 2 == 0).as_ref())); + assert!(!set! {1, 3, 5, 7, 8} + .iter() + .cloned() + .all(Rc::new(|i: u32| i % 2 == 1).as_ref())); + assert!(set! {1, 3, 5, 7, 8} + .iter() + .cloned() + .any(Rc::new(|i: u32| i % 2 == 0).as_ref())); + + for i in set! {1, 3, 5, 7}.iter() { + println!("{}", i); + } + + assert!(multiset! {1, 1, 5, 7} + .iter() + .all(Rc::new(|i: u32| i % 2 == 1).as_ref())); + assert!(!multiset! {1, 1, 5, 7} + .iter() + .any(Rc::new(|i: u32| i % 2 == 0).as_ref())); + assert!(!multiset! {1, 1, 5, 7, 8} + .iter() + .all(Rc::new(|i: u32| i % 2 == 1).as_ref())); + assert!(multiset! {1, 1, 5, 7, 8} + .iter() + .any(Rc::new(|i: u32| i % 2 == 0).as_ref())); + + let m = map![1 => 4, 3 => 6, 5 => 8]; + let m2 = m.clone(); + let m3 = m.clone(); + assert!(m + .clone() + .iter() + .all(Rc::new(move |i: u32| i + 3 == m2.get(&i)).as_ref())); + assert!(!m + .iter() + .any(Rc::new(move |i: u32| i + 2 == m3.get(&i)).as_ref())); + let m = map![1 => 4, 3 => 7, 5 => 7]; + let m2 = m.clone(); + let m3 = m.clone(); + assert!(!m + .clone() + .iter() + .all(Rc::new(move |i: u32| i + 3 == m2.get(&i)).as_ref())); + assert!(m + .iter() + .any(Rc::new(move |i: u32| i + 2 == m3.get(&i)).as_ref())); + } + + #[test] + fn test_for_loops() { + let mut sum: i32 = 0; + for i in integer_range(1, 11) { + sum += i; + } + assert_eq!(sum, 55); + + let mut sum: i32 = 0; + for i in integer_range_down(11, 1) { + sum += i; + } + assert_eq!(sum, 55); + + let mut sum: i32 = 0; + for i in integer_range_unbounded(1) { + sum += i; + if i == 10 { + break; + } + } + assert_eq!(sum, 55); + + let mut sum: i32 = 0; + for i in integer_range_down_unbounded(11) { + sum += i; + if i == 1 { + break; + } + } + assert_eq!(sum, 55); + } + + trait SuperTrait: Upcast + UpcastObject { + } + + trait NodeRcMutTrait: SuperTrait + Upcast + UpcastObject{ + } + + pub struct NodeRcMut { + val: DafnyInt, + next: Object, + } + impl NodeRcMut { + fn _ctor(this: Object, val: DafnyInt) { + let mut val_assign = false; + let mut next_assign = false; + update_field_uninit_object!(this.clone(), val, val_assign, val); + update_field_if_uninit_object!(this.clone(), next, next_assign, Object(None)); + } + } + impl SuperTrait for NodeRcMut {} + impl UpcastObject for NodeRcMut { + UpcastObjectFn!(dyn Any); + } + impl Upcast for NodeRcMut { + UpcastFn!(dyn Any); + } + impl UpcastObject for NodeRcMut { + UpcastObjectFn!(dyn NodeRcMutTrait); + } + impl Upcast for NodeRcMut { + UpcastFn!(dyn NodeRcMutTrait); + } + impl UpcastObject for NodeRcMut { + UpcastObjectFn!(dyn SuperTrait); + } + impl Upcast for NodeRcMut { + UpcastFn!(dyn SuperTrait); + } + impl NodeRcMutTrait for NodeRcMut {} + + #[test] + fn test_object() { + let mut x: Object = allocate_object::(); + NodeRcMut::_ctor(x.clone(), int!(42)); + assert_eq!(refcount!(x), 1); + assert_eq!(x.as_ref().val, int!(42)); + x.as_mut().next = x.clone(); + assert_eq!(refcount!(x), 2); + assert_eq!(x.as_ref().next.as_ref().val, int!(42)); + md!(rd!(x).next).next = Object(None); + assert_eq!(refcount!(x), 1); + let y: Object = upcast_object::<_, _>()(x.clone()); + assert_eq!(refcount!(x), 2); + let z: Object = upcast_object::<_, _>()(x.clone()); + assert_eq!(refcount!(x), 3); + let a2: Object = cast_object!(y.clone(), NodeRcMut); + assert_eq!(refcount!(x), 4); + assert_eq!(rd!(a2).val, int!(42)); + let a3: Object = cast_object!(z.clone(), NodeRcMut); + assert_eq!(refcount!(x), 5); + assert_eq!(rd!(a3).val, int!(42)); + + // Other way to create objects + let direct: Object = Object::::new(NodeRcMut {val: int!(1), next: Object::::null()}); + assert_eq!(rd!(direct).val, int!(1)); + let tail: Object = Object::::null(); + assert_eq!(tail, rd!(direct).next); + assert!(tail.is_null()); + assert!(!direct.is_null()); + + let a: Object<[i32]> = rcmut::array_object_from_rc(Rc::new([42, 43, 44])); + assert_eq!(rd!(a).len(), 3); + assert_eq!(rd!(a)[0], 42); + assert_eq!(rd!(a)[1], 43); + assert_eq!(rd!(a)[2], 44); + let b: Object<[i32]> = a.clone(); + md!(b)[0] = 45; + assert_eq!(rd!(a)[0], 45); + + let previous_count = refcount!(x); + { + let z = Object::::from_ref(x.as_ref()); + assert_eq!(refcount!(z), previous_count + 1); + assert_eq!(refcount!(x), previous_count + 1); + } + assert_eq!(refcount!(x), previous_count); + + let objects: Set> = crate::set!{y.clone(), cast_any_object!(x.clone())}; + assert_eq!(objects.cardinality_usize(), 1); + } + + pub struct NodeRawMut { + val: DafnyInt, + next: Ptr, + } + impl NodeRawMut { + fn _ctor(this: Ptr, val: DafnyInt) { + let mut val_assign = false; + update_field_uninit!(this, val, val_assign, val); + } + } + impl NodeRcMutTrait for NodeRawMut {} + UpcastDefObject!(NodeRawMut, dyn NodeRcMutTrait, dyn SuperTrait, dyn Any); + UpcastDef!(NodeRawMut, dyn NodeRcMutTrait, dyn SuperTrait, dyn Any); + + impl SuperTrait for NodeRawMut {} + + #[test] + fn test_rawmut() { + let x: Ptr = allocate::(); + NodeRawMut::_ctor(x.clone(), int!(42)); + assert_eq!(read!(x.clone()).val, int!(42)); + modify!(x.clone()).next = x.clone(); + assert_eq!(read!(read!(x.clone()).next.clone()).val, int!(42)); + modify!(read!(x.clone()).next.clone()).next = Ptr::null(); + let y: Ptr = upcast::<_, _>()(x); + assert!(y.is_instance_of::()); + assert!(!y.is_instance_of::()); + let z: Ptr = upcast::<_, _>()(x); + let _a2: Ptr = cast!(y, NodeRawMut); + let _a3: Ptr = cast!(z, NodeRawMut); + deallocate(x); + + let a = array::from_native(Box::new([42, 43, 44])); + assert_eq!(read!(a.clone()).len(), 3); + assert_eq!(read!(a.clone())[0], 42); + assert_eq!(read!(a.clone())[1], 43); + assert_eq!(read!(a.clone())[2], 44); + let b = a.clone(); + modify!(b.clone())[0] = 45; + assert_eq!(read!(a.clone())[0], 45); + + deallocate(a); + } + + // Conversion of any usize-compatible value into usize + #[test] + fn test_usize() { + let a: u128 = 1; + let b: i8 = 1; + let u: usize = 1; + assert_eq!(DafnyUsize::into_usize(int!(a)), u); + assert_eq!(DafnyUsize::into_usize(a), u); + assert_eq!(DafnyUsize::into_usize(b), u); + assert_eq!(DafnyUsize::into_usize(int!(b)), u); + } + + // Tests that we can compose Dafny types, like a sequence of maps + fn _test(_input: Sequence>) { + } + + #[derive(Clone)] + pub struct InternalOpaqueError { + pub message: String, + } + + crate::UpcastDefObject!(InternalOpaqueError, dyn Any); + + #[test] + fn test_native_string_upcast() { + let s = InternalOpaqueError { message: "Hello, World!".to_string() }; + let o: Object = Object::new(s); + let n: Object = upcast_object::()(o); + let x = cast_object!(n, InternalOpaqueError); + let s2 = crate::dafny_runtime_conversions::object::dafny_class_to_struct(x); + assert_eq!(s2.message, "Hello, World!"); + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/README.md b/AwsEncryptionSDK/runtimes/rust/examples/README.md new file mode 100644 index 000000000..fbb76e9c5 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/README.md @@ -0,0 +1 @@ +# TODO: Update for Rust diff --git a/AwsEncryptionSDK/runtimes/rust/examples/client_supplier/client_supplier_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/client_supplier/client_supplier_example.rs new file mode 100644 index 000000000..f7eaa4dd1 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/client_supplier/client_supplier_example.rs @@ -0,0 +1,191 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* + This example sets up an MRK multi-keyring and an MRK discovery + multi-keyring using a custom client supplier. + A custom client supplier grants users access to more granular + configuration aspects of their authentication details and KMS + client. In this example, we create a simple custom client supplier + that authenticates with a different IAM role based on the + region of the KMS key. + + This example creates a MRK multi-keyring configured with a custom + client supplier using a single MRK and encrypts the example_data with it. + Then, it creates a MRK discovery multi-keyring to decrypt the ciphertext. +*/ + +use super::regional_role_client_supplier::RegionalRoleClientSupplier; +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::types::error::Error::AwsCryptographicMaterialProvidersException; +use aws_esdk::aws_cryptography_materialProviders::types::client_supplier::ClientSupplierRef; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use aws_esdk::aws_cryptography_materialProviders::types::DiscoveryFilter; +use std::collections::HashMap; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, + mrk_key_id_encrypt: &str, + aws_account_id: &str, + aws_regions: Vec, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 3. Create a single MRK multi-keyring. + // This can be either a single-region KMS key or an MRK. + // For this example to succeed, the key's region must either + // 1) be in the regions list, or + // 2) the key must be an MRK with a replica defined + // in a region in the regions list, and the client + // must have the correct permissions to access the replica. + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + // Create the multi-keyring using our custom client supplier + // defined in the RegionalRoleClientSupplier class in this directory. + // Note: RegionalRoleClientSupplier will internally use the key_arn's region + // to retrieve the correct IAM role. + let supplier_ref = ClientSupplierRef { + inner: std::rc::Rc::new(std::cell::RefCell::new(RegionalRoleClientSupplier {})), + }; + + let mrk_keyring_with_client_supplier = mpl + .create_aws_kms_mrk_multi_keyring() + .client_supplier(supplier_ref.clone()) + .generator(mrk_key_id_encrypt) + .send() + .await?; + + // 4. Encrypt the data with the encryptionContext using the encrypt_keyring. + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(mrk_keyring_with_client_supplier) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 5. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 6. Create a MRK discovery multi-keyring with a custom client supplier. + // A discovery MRK multi-keyring will be composed of + // multiple discovery MRK keyrings, one for each region. + // Each component keyring has its own KMS client in a particular region. + // When we provide a client supplier to the multi-keyring, all component + // keyrings will use that client supplier configuration. + // In our tests, we make `mrk_key_id_encrypt` an MRK with a replica, and + // provide only the replica region in our discovery filter. + let discovery_filter = DiscoveryFilter::builder() + .account_ids(vec![aws_account_id.to_string()]) + .partition("aws".to_string()) + .build()?; + + let mrk_discovery_client_supplier_keyring = mpl + .create_aws_kms_mrk_discovery_multi_keyring() + .client_supplier(supplier_ref.clone()) + .discovery_filter(discovery_filter.clone()) + .regions(aws_regions) + .send() + .await?; + + // 7. Decrypt your encrypted data using the discovery multi keyring. + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // For each member of the Multi Keyring, every EDK will try to be decrypted until a decryption + // is successful. + // Since every member of the Multi Keyring is a Discovery Keyring: + // Each Keyring will filter the EDKs by the Discovery Filter and the Keyring's region. + // For each filtered EDK, the keyring will attempt decryption with the keyring's client. + // All of this is done serially, until a success occurs or all keyrings have failed + // all (filtered) EDKs. KMS MRK Discovery Keyrings will attempt to decrypt + // Multi Region Keys (MRKs) and regular KMS Keys. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(mrk_discovery_client_supplier_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // 9. Test the Missing Region Exception + // (This is an example for demonstration; you do not need to do this in your own code.) + let mrk_discovery_client_supplier_keyring_missing_region = mpl + .create_aws_kms_mrk_discovery_multi_keyring() + .client_supplier(supplier_ref) + .discovery_filter(discovery_filter) + .regions(vec!["fake-region".to_string()]) + .send() + .await; + + // Swallow the exception + // (This is an example for demonstration; you do not need to do this in your own code.) + match mrk_discovery_client_supplier_keyring_missing_region { + Ok(_) => panic!("Decryption using discovery keyring with missing region MUST \ + raise AwsCryptographicMaterialProvidersException"), + Err(AwsCryptographicMaterialProvidersException { message: _e }) => (), + _ => panic!("Unexpected error type"), + } + + println!("Client Supplier Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the Client Supplier example + use crate::example_utils::utils; + + // Note that we pass in an MRK in us-east-1. The RegionalRoleClientSupplier + // will internally use the key_arn's region (us-east-1) + // to retrieve the correct IAM role. + // and access its replica in eu-west-1 + let aws_regions: Vec = vec!["eu-west-1".to_string()]; + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + utils::TEST_MRK_KEY_ID_US_EAST_1, + utils::TEST_DEFAULT_KMS_KEY_ACCOUNT_ID, + aws_regions, + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/client_supplier/mod.rs b/AwsEncryptionSDK/runtimes/rust/examples/client_supplier/mod.rs new file mode 100644 index 000000000..3f2c50ba9 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/client_supplier/mod.rs @@ -0,0 +1,3 @@ +pub mod client_supplier_example; +pub mod regional_role_client_supplier; +pub mod regional_role_client_supplier_config; diff --git a/AwsEncryptionSDK/runtimes/rust/examples/client_supplier/regional_role_client_supplier.rs b/AwsEncryptionSDK/runtimes/rust/examples/client_supplier/regional_role_client_supplier.rs new file mode 100644 index 000000000..9bcef9427 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/client_supplier/regional_role_client_supplier.rs @@ -0,0 +1,57 @@ +use aws_config::Region; +use aws_esdk::aws_cryptography_materialProviders::types::ClientSupplier; +use aws_esdk::aws_cryptography_materialProviders::operation::get_client::GetClientInput; +use aws_esdk::aws_cryptography_materialProviders::types::error::Error; +use aws_esdk::com_amazonaws_kms::client::Client as kms_client; +use super::regional_role_client_supplier_config; + +/* + Example class demonstrating an implementation of a custom client supplier. + This particular implementation will create KMS clients with different IAM roles, + depending on the region passed. +*/ + +pub struct RegionalRoleClientSupplier {} + +impl ClientSupplier for RegionalRoleClientSupplier { + fn get_client(&mut self, input: GetClientInput) -> Result { + let region = input.region.unwrap(); + + let region_iam_role_map = regional_role_client_supplier_config::region_iam_role_map(); + + if !region_iam_role_map.contains_key(®ion) { + return Err(Error::AwsCryptographicMaterialProvidersException { + message: format!("Region {} is not supported by this client supplier", region).to_string(), + }); + } + + let arn = region_iam_role_map[®ion].clone(); + + use aws_config::sts::AssumeRoleProvider; + + let provider = tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + AssumeRoleProvider::builder(arn) + .region(Region::new(region.clone())) + .session_name("Rust-ESDK-Client-Supplier-Example-Session") + .build() + .await + }) + }); + + let sdk_config = tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + aws_config::load_defaults(aws_config::BehaviorVersion::v2024_03_28()).await + }) + }); + let kms_config = aws_sdk_kms::config::Builder::from(&sdk_config) + .credentials_provider(provider) + .region(Region::new(region)) + .build(); + + let inner_client = aws_sdk_kms::Client::from_conf(kms_config); + Ok(kms_client { + inner: inner_client, + }) + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/client_supplier/regional_role_client_supplier_config.rs b/AwsEncryptionSDK/runtimes/rust/examples/client_supplier/regional_role_client_supplier_config.rs new file mode 100644 index 000000000..cc4d9ec9b --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/client_supplier/regional_role_client_supplier_config.rs @@ -0,0 +1,20 @@ +use std::collections::HashMap; + +/* + Class containing config for the RegionalRoleClientSupplier. + In your own code, this might be hardcoded, or reference + an external source, e.g. environment variables or AWS AppConfig. +*/ + +const US_EAST_1_IAM_ROLE: &str = + "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Dafny-Role-only-us-east-1-KMS-keys"; + +const EU_WEST_1_IAM_ROLE: &str = + "arn:aws:iam::370957321024:role/GitHub-CI-Public-ESDK-Dafny-Role-only-eu-west-1-KMS-keys"; + +pub fn region_iam_role_map() -> HashMap { + HashMap::from([ + ("us-east-1".to_string(), US_EAST_1_IAM_ROLE.to_string()), + ("eu-west-1".to_string(), EU_WEST_1_IAM_ROLE.to_string()), + ]) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/mod.rs b/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/mod.rs new file mode 100644 index 000000000..3c640f858 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/mod.rs @@ -0,0 +1,2 @@ +pub mod required_encryption_context; +pub mod restrict_algorithm_suite; \ No newline at end of file diff --git a/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/required_encryption_context/mod.rs b/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/required_encryption_context/mod.rs new file mode 100644 index 000000000..f1b2fe755 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/required_encryption_context/mod.rs @@ -0,0 +1 @@ +pub mod required_encryption_context_example; \ No newline at end of file diff --git a/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/required_encryption_context/required_encryption_context_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/required_encryption_context/required_encryption_context_example.rs new file mode 100644 index 000000000..a9bb4a3bb --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/required_encryption_context/required_encryption_context_example.rs @@ -0,0 +1,217 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +Demonstrate an encrypt/decrypt cycle using a Required Encryption Context CMM. +A required encryption context CMM asks for required keys in the encryption context field +on encrypt such that they will not be stored on the message, but WILL be included in the header signature. +On decrypt, the client MUST supply the key/value pair(s) that were not stored to successfully decrypt the message. +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use aws_esdk::types::error::Error::AwsCryptographicMaterialProvidersError; +use std::collections::HashMap; +use std::vec::Vec; + +pub async fn encrypt_and_decrypt_with_cmm( + example_data: &str, + kms_key_id: &str, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create a KMS client. + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let kms_client = aws_sdk_kms::Client::new(&sdk_config); + + // 3. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ("requiredKey1".to_string(), "requiredValue1".to_string()), + ("requiredKey2".to_string(), "requiredValue2".to_string()), + ]); + + // 4. Create your required encryption context keys. + // These keys MUST be in your encryption context. + // These keys and their corresponding values WILL NOT be stored on the message but will be used + // for authentication. + let required_encryption_context_keys: Vec = vec![ + "requiredKey1".to_string(), + "requiredKey2".to_string(), + ]; + + // 5. Create a KMS keyring + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + let kms_keyring = mpl + .create_aws_kms_keyring() + .kms_key_id(kms_key_id) + .kms_client(kms_client) + .send() + .await?; + + // 6. Create the required encryption context CMM. + let underlying_cmm = mpl + .create_default_cryptographic_materials_manager() + .keyring(kms_keyring) + .send() + .await?; + + let required_ec_cmm = mpl + .create_required_encryption_context_cmm() + .underlying_cmm(underlying_cmm.clone()) + .required_encryption_context_keys(required_encryption_context_keys) + .send() + .await?; + + // 7. Encrypt the data with the encryption_context + // NOTE: the keys "requiredKey1", and "requiredKey2" + // WILL NOT be stored in the message header, but "encryption", "is not", + // "but adds", "that can help you", and "the data you are handling" WILL be stored. + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .materials_manager(required_ec_cmm.clone()) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 8. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 9. Decrypt your encrypted data using the same keyring you used on encrypt. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext.clone()) + .materials_manager(required_ec_cmm.clone()) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 10. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // 11. Attempt to decrypt your encrypted data using the same cryptographic material manager + // you used on encrypt, but we won't pass the encryption context we DID NOT store on the message. + // This will fail + let decryption_response_without_ec = esdk_client.decrypt() + .ciphertext(ciphertext.clone()) + .materials_manager(required_ec_cmm.clone()) + .send() + .await; + + match decryption_response_without_ec { + Ok(_) => panic!("Decrypt without encryption context MUST \ + raise AwsCryptographicMaterialProvidersError"), + Err(AwsCryptographicMaterialProvidersError { error: _e }) => (), + _ => panic!("Unexpected error type"), + } + + // 12. Decrypt your encrypted data using the same cryptographic material manager + // you used to encrypt, but supply encryption context that contains ONLY the encryption context that + // was NOT stored. This will pass. + let reproduced_encryption_context = HashMap::from([ + ("requiredKey1".to_string(), "requiredValue1".to_string()), + ("requiredKey2".to_string(), "requiredValue2".to_string()), + ]); + + let decryption_response_with_reproduced_ec = esdk_client.decrypt() + .ciphertext(ciphertext.clone()) + .materials_manager(required_ec_cmm) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(reproduced_encryption_context) + .send() + .await?; + + let decrypted_plaintext_with_reproduced_ec = + decryption_response_with_reproduced_ec + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext_with_reproduced_ec, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // 13. You can decrypt the ciphertext using the underlying cmm, but not providing the + // encryption context with the request will result in an AwsCryptographicMaterialProvidersError + + // This will pass + let decryption_response_with_ec_underlying_cmm = esdk_client.decrypt() + .ciphertext(ciphertext.clone()) + .materials_manager(underlying_cmm.clone()) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let decrypted_plaintext_with_ec_underlying_cmm = + decryption_response_with_ec_underlying_cmm + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext_with_ec_underlying_cmm, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // This will fail + let decryption_response_without_ec_underlying_cmm = esdk_client.decrypt() + .ciphertext(ciphertext) + .materials_manager(underlying_cmm) + .send() + .await; + + match decryption_response_without_ec_underlying_cmm { + Ok(_) => panic!("Decrypt without encryption context MUST \ + raise AwsCryptographicMaterialProvidersError"), + Err(AwsCryptographicMaterialProvidersError { error: _e }) => (), + _ => panic!("Unexpected error type"), + } + + println!("Required Encryption Context CMM Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_cmm() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the Required Encryption Context CMM example + use crate::example_utils::utils; + + encrypt_and_decrypt_with_cmm( + utils::TEST_EXAMPLE_DATA, + utils::TEST_DEFAULT_KMS_KEY_ID + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/restrict_algorithm_suite/mod.rs b/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/restrict_algorithm_suite/mod.rs new file mode 100644 index 000000000..dd135d17a --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/restrict_algorithm_suite/mod.rs @@ -0,0 +1,2 @@ +pub mod signing_only_example; +pub mod signing_suite_only_cmm; \ No newline at end of file diff --git a/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/restrict_algorithm_suite/signing_only_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/restrict_algorithm_suite/signing_only_example.rs new file mode 100644 index 000000000..e825f50da --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/restrict_algorithm_suite/signing_only_example.rs @@ -0,0 +1,135 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* + Demonstrate an encrypt/decrypt cycle using a Custom Cryptographic Materials Manager (CMM). + `SigningSuiteOnlyCMM.cs` demonstrates creating a custom CMM to reject Non-Signing Algorithms. +*/ + +use super::signing_suite_only_cmm::SigningSuiteOnlyCMM; +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::types::error::Error::AwsCryptographicMaterialProvidersError; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use aws_esdk::aws_cryptography_materialProviders::types::cryptographic_materials_manager::CryptographicMaterialsManagerRef; +use aws_esdk::aws_cryptography_materialProviders::types::EsdkAlgorithmSuiteId; +use std::collections::HashMap; + +pub async fn encrypt_and_decrypt_with_cmm( + example_data: &str, + kms_key_id: &str, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create a KMS client. + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let kms_client = aws_sdk_kms::Client::new(&sdk_config); + + // 3. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 4. Create a custom SigningSuiteOnlyCMM + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + let kms_keyring = mpl + .create_aws_kms_keyring() + .kms_key_id(kms_key_id) + .kms_client(kms_client) + .send() + .await?; + + let signing_suite_only_cmm = SigningSuiteOnlyCMM::new(kms_keyring); + + let signing_suite_only_cmm_ref: CryptographicMaterialsManagerRef = CryptographicMaterialsManagerRef { + inner: ::std::rc::Rc::new(std::cell::RefCell::new(signing_suite_only_cmm)), + }; + + // 5. Encrypt the data with the encryption_context + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .materials_manager(signing_suite_only_cmm_ref.clone()) + .encryption_context(encryption_context.clone()) + .algorithm_suite_id(EsdkAlgorithmSuiteId::AlgAes256GcmHkdfSha512CommitKeyEcdsaP384) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 6. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 7. Decrypt your encrypted data using the same keyring you used on encrypt. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext) + .materials_manager(signing_suite_only_cmm_ref.clone()) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // 9. Demonstrate that a Non Signing Algorithm Suite will be rejected + // by the CMM. + let encryption_response_non_signing = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .materials_manager(signing_suite_only_cmm_ref) + .encryption_context(encryption_context.clone()) + .algorithm_suite_id(EsdkAlgorithmSuiteId::AlgAes256GcmHkdfSha512CommitKey) + .send() + .await; + + match encryption_response_non_signing { + Ok(_) => panic!("Encrypt using non signing algorithm suite MUST \ + raise AwsCryptographicMaterialProvidersError"), + Err(AwsCryptographicMaterialProvidersError { error: _e }) => (), + _ => panic!("Unexpected error type"), + } + + println!("SigningSuiteOnlyCMM Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_cmm() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the SigningSuiteOnlyCMM example + use crate::example_utils::utils; + + encrypt_and_decrypt_with_cmm( + utils::TEST_EXAMPLE_DATA, + utils::TEST_DEFAULT_KMS_KEY_ID + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/restrict_algorithm_suite/signing_suite_only_cmm.rs b/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/restrict_algorithm_suite/signing_suite_only_cmm.rs new file mode 100644 index 000000000..e47c01842 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/cryptographic_materials_manager/restrict_algorithm_suite/signing_suite_only_cmm.rs @@ -0,0 +1,122 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use aws_esdk::aws_cryptography_materialProviders::operation::get_encryption_materials::GetEncryptionMaterialsInput; +use aws_esdk::aws_cryptography_materialProviders::operation::get_encryption_materials::GetEncryptionMaterialsOutput; +use aws_esdk::aws_cryptography_materialProviders::operation::decrypt_materials::DecryptMaterialsInput; +use aws_esdk::aws_cryptography_materialProviders::operation::decrypt_materials::DecryptMaterialsOutput; +use aws_esdk::aws_cryptography_materialProviders::types::error::Error; +use aws_esdk::aws_cryptography_materialProviders::types::cryptographic_materials_manager::CryptographicMaterialsManager; +use aws_esdk::aws_cryptography_materialProviders::types::cryptographic_materials_manager::CryptographicMaterialsManagerRef; +use aws_esdk::aws_cryptography_materialProviders::types::keyring::KeyringRef; +use aws_esdk::aws_cryptography_materialProviders::types::EsdkAlgorithmSuiteId; +use aws_esdk::aws_cryptography_materialProviders::types::AlgorithmSuiteId; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use std::vec::Vec; + +/* + Demonstrates creating a custom Cryptographic Materials Manager (CMM). + The SigningSuiteOnlyCMM ensures that callers use an Algorithm Suite with + signing. This is a best practice. Read more about Digital Signing: + https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#digital-sigs + Read more about Cryptographic Materials Managers (CMMs): + https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#crypt-materials-manager +*/ + +pub struct SigningSuiteOnlyCMM { + approved_algos: Vec, + cmm: CryptographicMaterialsManagerRef, +} + +impl SigningSuiteOnlyCMM { + pub fn new(keyring: KeyringRef) -> Self { + let mpl_config = MaterialProvidersConfig::builder().build().unwrap(); + let mpl = mpl_client::Client::from_conf(mpl_config).unwrap(); + + Self { + approved_algos: vec![ + EsdkAlgorithmSuiteId::AlgAes128GcmIv12Tag16HkdfSha256EcdsaP256, + EsdkAlgorithmSuiteId::AlgAes192GcmIv12Tag16HkdfSha384EcdsaP384, + EsdkAlgorithmSuiteId::AlgAes256GcmIv12Tag16HkdfSha384EcdsaP384, + EsdkAlgorithmSuiteId::AlgAes256GcmHkdfSha512CommitKeyEcdsaP384, + ], + // Create a DefaultCryptographicMaterialsManager to facilitate + // GetEncryptionMaterials and DecryptionMaterials + // after this CMM approves the Algorithm Suite. + cmm: tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + mpl.create_default_cryptographic_materials_manager() + .keyring(keyring) + .send() + .await + }) + }).unwrap(), + } + } +} + +impl CryptographicMaterialsManager for SigningSuiteOnlyCMM { + fn get_encryption_materials( + &mut self, + input: GetEncryptionMaterialsInput, + ) -> Result { + let algorithm_suite_id: AlgorithmSuiteId = input.algorithm_suite_id.clone().unwrap(); + let esdk_algorithm_suite_id: EsdkAlgorithmSuiteId = if let AlgorithmSuiteId::Esdk(esdk_id) = algorithm_suite_id { + esdk_id + } + else { + panic!("Algorithm Suite ID is not an EsdkAlgorithmSuiteId"); + }; + + if !self.approved_algos.contains(&esdk_algorithm_suite_id) { + return Err(Error::AwsCryptographicMaterialProvidersException { + message: "Algorithm Suite must use Signing".to_string(), + }); + } + + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + self.cmm.get_encryption_materials() + .algorithm_suite_id(input.algorithm_suite_id.unwrap()) + .commitment_policy(input.commitment_policy.unwrap()) + .encryption_context(input.encryption_context.unwrap()) + .max_plaintext_length(input.max_plaintext_length.unwrap()) + .send() + .await + }) + }) + + } + + fn decrypt_materials( + &mut self, + input: DecryptMaterialsInput, + ) -> Result { + let algorithm_suite_id: AlgorithmSuiteId = input.algorithm_suite_id.clone().unwrap(); + let esdk_algorithm_suite_id: EsdkAlgorithmSuiteId = if let AlgorithmSuiteId::Esdk(esdk_id) = algorithm_suite_id { + esdk_id + } + else { + panic!("Algorithm Suite ID is not an EsdkAlgorithmSuiteId"); + }; + + if !self.approved_algos.contains(&esdk_algorithm_suite_id) { + return Err(Error::AwsCryptographicMaterialProvidersException { + message: "Algorithm Suite must use Signing".to_string(), + }); + } + + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + self.cmm.decrypt_materials() + .algorithm_suite_id(input.algorithm_suite_id.unwrap()) + .commitment_policy(input.commitment_policy.unwrap()) + .encryption_context(input.encryption_context.unwrap()) + .encrypted_data_keys(input.encrypted_data_keys.unwrap()) + .send() + .await + }) + }) + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/example_utils/mod.rs b/AwsEncryptionSDK/runtimes/rust/examples/example_utils/mod.rs new file mode 100644 index 000000000..fab870e3e --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/example_utils/mod.rs @@ -0,0 +1 @@ +pub mod utils; \ No newline at end of file diff --git a/AwsEncryptionSDK/runtimes/rust/examples/example_utils/utils.rs b/AwsEncryptionSDK/runtimes/rust/examples/example_utils/utils.rs new file mode 100644 index 000000000..dd942d664 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/example_utils/utils.rs @@ -0,0 +1,38 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +pub const TEST_EXAMPLE_DATA: &str = "Hello World!"; + +pub const TEST_DEFAULT_KMS_KEY_ID: &str = + "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f"; + +pub const TEST_DEFAULT_KMS_KEY_ACCOUNT_ID: &str = "658956600833"; + +pub const TEST_SECOND_REGION_KMS_KEY_ID: &str = + "arn:aws:kms:eu-central-1:658956600833:key/75414c93-5285-4b57-99c9-30c1cf0a22c2"; + +pub const TEST_KMS_RSA_KEY_ID: &str = + "arn:aws:kms:us-west-2:370957321024:key/mrk-63d386cb70614ea59b32ad65c9315297"; + +pub const TEST_KMS_RSA_PUBLIC_KEY: &str = "-----BEGIN PUBLIC KEY-----\n\ + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA27Uc/fBaMVhxCE/SpCMQ\ + oSBRSzQJw+o2hBaA+FiPGtiJ/aPy7sn18aCkelaSj4kwoC79b/arNHlkjc7OJFsN\ + /GoFKgNvaiY4lOeJqEiWQGSSgHtsJLdbO2u4OOSxh8qIRAMKbMgQDVX4FR/PLKeK\ + fc2aCDvcNSpAM++8NlNmv7+xQBJydr5ce91eISbHkFRkK3/bAM+1iddupoRw4Wo2\ + r3avzrg5xBHmzR7u1FTab22Op3Hgb2dBLZH43wNKAceVwKqKA8UNAxashFON7xK9\ + yy4kfOL0Z/nhxRKe4jRZ/5v508qIzgzCksYy7Y3QbMejAtiYnr7s5/d5KWw0swou\ + twIDAQAB\n\ + -----END PUBLIC KEY-----"; + +pub const TEST_MRK_KEY_ID_US_EAST_1: &str = + "arn:aws:kms:us-east-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7"; + +pub const TEST_MRK_KEY_ID_EU_WEST_1: &str = + "arn:aws:kms:eu-west-1:658956600833:key/mrk-80bd8ecdcd4342aebd84b7dc9da498a7"; + +pub const TEST_KEY_STORE_NAME: &str = "KeyStoreDdbTable"; + +pub const TEST_LOGICAL_KEY_STORE_NAME: &str = "KeyStoreDdbTable"; + +pub const TEST_KEY_STORE_KMS_KEY_ID: &str = + "arn:aws:kms:us-west-2:370957321024:key/9d989aa2-2f9c-438c-a745-cc57d3ad0126"; diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_discovery_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_discovery_keyring_example.rs new file mode 100644 index 000000000..a196b8151 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_discovery_keyring_example.rs @@ -0,0 +1,204 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the AWS KMS Discovery Keyring + +AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. + +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. For information about using multi-Region keys with the +AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks + +Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. +If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt +operation fails. + +When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS key. + +This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This encrypted ciphertext is then decrypted using the Discovery keyring. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +3. Decryption is only possible if the Discovery Keyring contains the correct AWS Account ID's to + which the KMS key used for encryption belongs +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use aws_esdk::aws_cryptography_materialProviders::types::DiscoveryFilter; +use aws_esdk::types::error::Error::AwsCryptographicMaterialProvidersError; +use std::collections::HashMap; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, + kms_key_id: &str, + aws_account_id: &str, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create a KMS client. + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let kms_client = aws_sdk_kms::Client::new(&sdk_config); + + // 3. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 4. Create the keyring that determines how your data keys are protected. + // Although this example highlights Discovery keyrings, Discovery keyrings cannot + // be used to encrypt, so for encryption we create a KMS keyring without discovery mode. + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + let encrypt_kms_keyring = mpl + .create_aws_kms_keyring() + .kms_key_id(kms_key_id) + .kms_client(kms_client.clone()) + .send() + .await?; + + // 5. Encrypt the data with the encryption_context + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(encrypt_kms_keyring) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 6. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 7. Now create a Discovery keyring to use for decryption. We'll add a discovery filter + // so that we limit the set of ciphertexts we are willing to decrypt to only ones + // created by KMS keys in our account and partition. + let discovery_filter = DiscoveryFilter::builder() + .account_ids(vec![aws_account_id.to_string()]) + .partition("aws".to_string()) + .build()?; + + let discovery_keyring = mpl + .create_aws_kms_discovery_keyring() + .kms_client(kms_client.clone()) + .discovery_filter(discovery_filter) + .send() + .await?; + + // 8. Decrypt your encrypted data using the discovery keyring. + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // The Discovery Keyring filters these EDKs for + // EDKs encrypted by Single Region OR Multi Region KMS Keys. + // If a Discovery Filter is present, these KMS Keys must belong + // to an AWS Account ID in the discovery filter's AccountIds and + // must be from the discovery filter's partition. + // Finally, KMS is called to decrypt each filtered EDK until an EDK is + // successfully decrypted. The resulting data key is used to decrypt the + // ciphertext's message. + // If all calls to KMS fail, the decryption fails. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext.clone()) + .keyring(discovery_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 9. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // 10. Demonstrate that if a different discovery keyring (Bob's) doesn't have the correct + // AWS Account ID's, the decrypt will fail with an error message + // Note that this assumes Account ID used here ('888888888888') is different than the one used + // during encryption + let discovery_filter_bob = DiscoveryFilter::builder() + .account_ids(vec!["888888888888".to_string()]) + .partition("aws".to_string()) + .build()?; + + let discovery_keyring_bob = mpl + .create_aws_kms_discovery_keyring() + .kms_client(kms_client) + .discovery_filter(discovery_filter_bob) + .send() + .await?; + + // Decrypt the ciphertext using Bob's discovery keyring which doesn't contain the required + // Account ID's for the KMS keyring used for encryption. + // This should throw an AwsCryptographicMaterialProvidersError exception + let decryption_response_bob = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(discovery_keyring_bob) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await; + + match decryption_response_bob { + Ok(_) => panic!("Decrypt using discovery keyring with wrong AWS Account ID MUST \ + raise AwsCryptographicMaterialProvidersError"), + Err(AwsCryptographicMaterialProvidersError { error: _e }) => (), + _ => panic!("Unexpected error type"), + } + + println!("KMS Discovery Keyring Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the AWS KMS Discovery Keyring example + use crate::example_utils::utils; + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + utils::TEST_DEFAULT_KMS_KEY_ID, + utils::TEST_DEFAULT_KMS_KEY_ACCOUNT_ID + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_discovery_multi_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_discovery_multi_keyring_example.rs new file mode 100644 index 000000000..bb15dc653 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_discovery_multi_keyring_example.rs @@ -0,0 +1,173 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the AWS KMS Discovery Multi Keyring and demonstrates decryption +using a Multi-Keyring containing multiple AWS KMS Discovery Keyrings. + +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. For information about using multi-Region keys with the +AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks + +Because it doesn't specify any wrapping keys, a discovery keyring can't encrypt data. +If you use a discovery keyring to encrypt data, alone or in a multi-keyring, the encrypt +operation fails. + +When decrypting, a discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS key that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS key. + +This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This encrypted ciphertext is then decrypted using the Discovery Multi +keyring. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use aws_esdk::aws_cryptography_materialProviders::types::DiscoveryFilter; +use std::collections::HashMap; +use std::vec::Vec; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, + kms_key_id: &str, + aws_account_id: &str, + aws_regions: Vec, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create a KMS client. + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let kms_client = aws_sdk_kms::Client::new(&sdk_config); + + // 3. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 4. Create the keyring that determines how your data keys are protected. + // Although this example highlights Discovery keyrings, Discovery keyrings cannot + // be used to encrypt, so for encryption we create a KMS keyring without discovery mode. + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + let encrypt_kms_keyring = mpl + .create_aws_kms_keyring() + .kms_key_id(kms_key_id) + .kms_client(kms_client.clone()) + .send() + .await?; + + // 5. Encrypt the data with the encryption_context + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(encrypt_kms_keyring) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 6. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 7. Now create a Discovery Multi keyring to use for decryption. We'll add a discovery filter + // so that we limit the set of ciphertexts we are willing to decrypt to only ones + // created by KMS keys in our account and partition. + let discovery_filter = DiscoveryFilter::builder() + .account_ids(vec![aws_account_id.to_string()]) + .partition("aws".to_string()) + .build()?; + + // This is a Multi Keyring composed of Discovery Keyrings. + // There is a keyring for every region in `regions`. + // All the keyrings have the same Discovery Filter. + // Each keyring has its own KMS Client, which is created for the keyring's region. + let discovery_multi_keyring = mpl + .create_aws_kms_discovery_multi_keyring() + .regions(aws_regions) + .discovery_filter(discovery_filter) + .send() + .await?; + + // 8. On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // For each member of the Multi Keyring, every EDK will try to be decrypted until a decryption + // is successful. + // Since every member of the Multi Keyring is a Discovery Keyring: + // Each Keyring will filter the EDKs by the Discovery Filter + // For the filtered EDKs, the keyring will try to decrypt it with the keyring's client. + // All of this is done serially, until a success occurs or all keyrings have + // failed all (filtered) EDKs. + // KMS Discovery Keyrings will attempt to decrypt Multi Region Keys (MRKs) and regular KMS Keys. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(discovery_multi_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 9. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + println!("KMS Discovery Multi Keyring Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the AWS KMS Discovery Multi Keyring example + use crate::example_utils::utils; + + let aws_regions: Vec = vec!["us-east-1".to_string(), "us-west-2".to_string()]; + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + utils::TEST_DEFAULT_KMS_KEY_ID, + utils::TEST_DEFAULT_KMS_KEY_ACCOUNT_ID, + aws_regions + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_hierarchical/aws_kms_hierarchical_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_hierarchical/aws_kms_hierarchical_keyring_example.rs new file mode 100644 index 000000000..da945cc39 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_hierarchical/aws_kms_hierarchical_keyring_example.rs @@ -0,0 +1,290 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* + This example sets up the Hierarchical Keyring, which establishes a key hierarchy where "branch" + keys are persisted in DynamoDb. These branch keys are used to protect your data keys, and these + branch keys are themselves protected by a KMS Key. + + Establishing a key hierarchy like this has two benefits: + First, by caching the branch key material, and only calling KMS to re-establish authentication + regularly according to your configured TTL, you limit how often you need to call KMS to protect + your data. This is a performance security tradeoff, where your authentication, audit, and logging + from KMS is no longer one-to-one with every encrypt or decrypt call. Additionally, KMS Cloudtrail + cannot be used to distinguish Encrypt and Decrypt calls, and you cannot restrict who has + Encryption rights from who has Decryption rights since they both ONLY need KMS:Decrypt. However, + the benefit is that you no longer have to make a network call to KMS for every encrypt or + decrypt. + + Second, this key hierarchy facilitates cryptographic isolation of a tenant's data in a + multi-tenant data store. Each tenant can have a unique Branch Key, that is only used to protect + the tenant's data. You can either statically configure a single branch key to ensure you are + restricting access to a single tenant, or you can implement an interface that selects the Branch + Key based on the Encryption Context. + + This example demonstrates configuring a Hierarchical Keyring with a Branch Key ID Supplier to + encrypt and decrypt data for two separate tenants. + + This example requires access to the DDB Table where you are storing the Branch Keys. This + table must be configured with the following primary key configuration: - Partition key is named + "partition_key" with type (S) - Sort key is named "sort_key" with type (S). + + This example also requires using a KMS Key. You need the following access on this key: + - GenerateDataKeyWithoutPlaintext + - Decrypt + + For more information on how to use Hierarchical Keyrings, see + https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-hierarchical-keyring.html +*/ + +use super::create_branch_key_id::create_branch_key_id; +use super::example_branch_key_id_supplier::ExampleBranchKeyIdSupplier; +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::types::branch_key_id_supplier::BranchKeyIdSupplierRef; +use aws_esdk::types::error::Error::AwsCryptographicMaterialProvidersError; +use aws_esdk::aws_cryptography_keyStore::types::KmsConfiguration; +use aws_esdk::aws_cryptography_keyStore::types::key_store_config::KeyStoreConfig; +use aws_esdk::aws_cryptography_keyStore::client as keystore_client; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use std::collections::HashMap; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, + key_store_table_name: &str, + logical_key_store_name: &str, + key_store_kms_key_id: &str, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create a KMS client and DynamoDB client. + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let kms_client = aws_sdk_kms::Client::new(&sdk_config); + let ddb_client = aws_sdk_dynamodb::Client::new(&sdk_config); + + // 3. Configure your KeyStore resource. + // This SHOULD be the same configuration that you used + // to initially create and populate your KeyStore. + let key_store_config = KeyStoreConfig::builder() + .kms_client(kms_client) + .ddb_client(ddb_client) + .ddb_table_name(key_store_table_name) + .logical_key_store_name(logical_key_store_name) + .kms_configuration(KmsConfiguration::KmsKeyArn(key_store_kms_key_id.to_string())) + .build()?; + + let key_store = keystore_client::Client::from_conf(key_store_config)?; + + // 4. Call CreateKey to create two new active branch keys + let branch_key_id_a: String = create_branch_key_id( + key_store_table_name, + logical_key_store_name, + key_store_kms_key_id + ).await?; + let branch_key_id_b: String = create_branch_key_id( + key_store_table_name, + logical_key_store_name, + key_store_kms_key_id + ).await?; + + // 5. Create a branch key supplier that maps the branch key id to a more readable format + let branch_key_id_supplier = ExampleBranchKeyIdSupplier::new( + &branch_key_id_a, + &branch_key_id_b + ); + let branch_key_id_supplier_ref: BranchKeyIdSupplierRef = BranchKeyIdSupplierRef { + inner: ::std::rc::Rc::new(std::cell::RefCell::new(branch_key_id_supplier)), + }; + + // 6. Create the Hierarchical Keyring. + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + let hierarchical_keyring = mpl + .create_aws_kms_hierarchical_keyring() + .key_store(key_store.clone()) + .branch_key_id_supplier(branch_key_id_supplier_ref) + .ttl_seconds(600) + .send() + .await?; + + // 7. Create encryption context for both tenants. + // The Branch Key Id supplier uses the encryption context to determine which branch key id will + // be used to encrypt data. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + + // Create encryption context for TenantA + let encryption_context_a = HashMap::from([ + ("tenant".to_string(), "TenantA".to_string()), + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // Create encryption context for TenantB + let encryption_context_b = HashMap::from([ + ("tenant".to_string(), "TenantB".to_string()), + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 8. Encrypt the data with encryptionContextA & encryptionContextB + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response_a = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(hierarchical_keyring.clone()) + .encryption_context(encryption_context_a.clone()) + .send() + .await?; + + let ciphertext_a = encryption_response_a + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + let encryption_response_b = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(hierarchical_keyring.clone()) + .encryption_context(encryption_context_b.clone()) + .send() + .await?; + + let ciphertext_b = encryption_response_b + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 9. Demonstrate that the ciphertexts and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext_a, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + assert_ne!(ciphertext_b, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 10. To attest that TenantKeyB cannot decrypt a message written by TenantKeyA, + // and vice versa, let's construct more restrictive hierarchical keyrings. + let hierarchical_keyring_a = mpl + .create_aws_kms_hierarchical_keyring() + .key_store(key_store.clone()) + .branch_key_id(branch_key_id_a) + .ttl_seconds(600) + .send() + .await?; + + let hierarchical_keyring_b = mpl + .create_aws_kms_hierarchical_keyring() + .key_store(key_store) + .branch_key_id(branch_key_id_b) + .ttl_seconds(600) + .send() + .await?; + + // 11. Demonstrate that data encrypted by one tenant's key + // cannot be decrypted with by a keyring specific to another tenant. + + // Keyring with tenant B's branch key cannot decrypt data encrypted with tenant A's branch key + // This will fail and raise a AwsCryptographicMaterialProvidersError, + // which we swallow ONLY for demonstration purposes. + let decryption_response_mismatch_1 = esdk_client.decrypt() + .ciphertext(ciphertext_a.clone()) + .keyring(hierarchical_keyring_b.clone()) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context_a.clone()) + .send() + .await; + + match decryption_response_mismatch_1 { + Ok(_) => panic!("Decrypt with wrong tenant's hierarchical keyring MUST \ + raise AwsCryptographicMaterialProvidersError"), + Err(AwsCryptographicMaterialProvidersError { error: _e }) => (), + _ => panic!("Unexpected error type"), + } + + // Keyring with tenant A's branch key cannot decrypt data encrypted with tenant B's branch key. + // This will fail and raise a AwsCryptographicMaterialProvidersError, + // which we swallow ONLY for demonstration purposes. + let decryption_response_mismatch_2 = esdk_client.decrypt() + .ciphertext(ciphertext_b.clone()) + .keyring(hierarchical_keyring_a.clone()) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context_b.clone()) + .send() + .await; + + match decryption_response_mismatch_2 { + Ok(_) => panic!("Decrypt with wrong tenant's hierarchical keyring MUST \ + raise AwsCryptographicMaterialProvidersError"), + Err(AwsCryptographicMaterialProvidersError { error: _e }) => (), + _ => panic!("Unexpected error type"), + } + + // 12. Demonstrate that data encrypted by one tenant's branch key can be decrypted by that tenant, + // and that the decrypted data matches the input data. + let decryption_response_a = esdk_client.decrypt() + .ciphertext(ciphertext_a) + .keyring(hierarchical_keyring_a) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context_a) + .send() + .await?; + + let decrypted_plaintext_a = decryption_response_a + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext_a, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // Similarly for TenantB + let decryption_response_b = esdk_client.decrypt() + .ciphertext(ciphertext_b) + .keyring(hierarchical_keyring_b) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context_b) + .send() + .await?; + + let decrypted_plaintext_b = decryption_response_b + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext_b, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + println!("Hierarchical Keyring Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the Hierarchical Keyring example + use crate::example_utils::utils; + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + utils::TEST_KEY_STORE_NAME, + utils::TEST_LOGICAL_KEY_STORE_NAME, + utils::TEST_KEY_STORE_KMS_KEY_ID + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_hierarchical/create_branch_key_id.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_hierarchical/create_branch_key_id.rs new file mode 100644 index 000000000..b4fb6a525 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_hierarchical/create_branch_key_id.rs @@ -0,0 +1,37 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use aws_esdk::aws_cryptography_keyStore::client as keystore_client; +use aws_esdk::aws_cryptography_keyStore::types::key_store_config::KeyStoreConfig; +use aws_esdk::aws_cryptography_keyStore::types::KmsConfiguration; + +/* + The Hierarchical Keyring Example relies on the existence + of a DDB-backed key store with pre-existing + branch key material. + + This example demonstrates configuring a KeyStore and then + using a helper method to create a branch key. +*/ +pub async fn create_branch_key_id( + key_store_table_name: &str, + logical_key_store_name: &str, + kms_key_arn: &str +) -> Result { + // Create a Key Store + // The KMS Configuration you use in the KeyStore MUST have the right access to the resources in the KeyStore. + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let key_store_config = KeyStoreConfig::builder() + .kms_client(aws_sdk_kms::Client::new(&sdk_config)) + .ddb_client(aws_sdk_dynamodb::Client::new(&sdk_config)) + .ddb_table_name(key_store_table_name) + .logical_key_store_name(logical_key_store_name) + .kms_configuration(KmsConfiguration::KmsKeyArn(kms_key_arn.to_string())) + .build()?; + + let keystore = keystore_client::Client::from_conf(key_store_config)?; + + // Create a branch key identifier with the AWS KMS Key configured in the KeyStore Configuration. + let new_key = keystore.create_key().send().await?; + Ok(new_key.branch_key_identifier.unwrap()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_hierarchical/example_branch_key_id_supplier.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_hierarchical/example_branch_key_id_supplier.rs new file mode 100644 index 000000000..a60eec44f --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_hierarchical/example_branch_key_id_supplier.rs @@ -0,0 +1,74 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use aws_esdk::aws_cryptography_materialProviders::operation::get_branch_key_id::GetBranchKeyIdInput; +use aws_esdk::aws_cryptography_materialProviders::operation::get_branch_key_id::GetBranchKeyIdOutput; +use aws_esdk::aws_cryptography_materialProviders::types::error::Error; +use aws_esdk::aws_cryptography_materialProviders::types::branch_key_id_supplier::BranchKeyIdSupplier; +use std::collections::HashMap; + +/* + Demonstrates how to create a BranchKeyIdSupplier. + + The BranchKeyIdSupplier determines which Branch Key is used + to protect or access data. + It is an important component in a Multi-tenant solution, + where each tenant is cryptographically isolated. + The Branch Key ID Supplier uses the Encryption Context + provided at Encrypt or Decrypt + to determine what "shared secret" (Branch Key) + is used. +*/ + +pub struct ExampleBranchKeyIdSupplier { + branch_key_id_for_tenant_a: String, + branch_key_id_for_tenant_b: String, +} + +impl ExampleBranchKeyIdSupplier { + pub fn new(tenant_a_id: &str, tenant_b_id: &str) -> Self { + Self { + branch_key_id_for_tenant_a: tenant_a_id.to_string(), + branch_key_id_for_tenant_b: tenant_b_id.to_string(), + } + } +} + +// We MUST use the encryption context to determine +// the Branch Key ID. +impl BranchKeyIdSupplier for ExampleBranchKeyIdSupplier { + fn get_branch_key_id( + &mut self, + input: GetBranchKeyIdInput, + ) -> Result { + let encryption_context: HashMap = input.encryption_context.unwrap(); + + if !encryption_context.contains_key("tenant") { + return Err( Error::AwsCryptographicMaterialProvidersException { + message: + "EncryptionContext invalid, does not contain expected tenant key value pair." + .to_string() + } + ); + } + let tenant_key_id: &str = encryption_context["tenant"].as_str(); + + if tenant_key_id == "TenantA" { + Ok(GetBranchKeyIdOutput::builder() + .branch_key_id(self.branch_key_id_for_tenant_a.clone()) + .build() + .unwrap()) + } else if tenant_key_id == "TenantB" { + Ok(GetBranchKeyIdOutput::builder() + .branch_key_id(self.branch_key_id_for_tenant_b.clone()) + .build() + .unwrap()) + } else { + Err( Error::AwsCryptographicMaterialProvidersException { + message: + "Item does not contain valid tenant ID.".to_string() + } + ) + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_hierarchical/mod.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_hierarchical/mod.rs new file mode 100644 index 000000000..df131ec3f --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_hierarchical/mod.rs @@ -0,0 +1,3 @@ +pub mod aws_kms_hierarchical_keyring_example; +pub mod create_branch_key_id; +pub mod example_branch_key_id_supplier; diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_keyring_example.rs new file mode 100644 index 000000000..a0d08e5c3 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_keyring_example.rs @@ -0,0 +1,122 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the AWS KMS Keyring + +The AWS KMS keyring uses symmetric encryption KMS keys to generate, encrypt and +decrypt data keys. This example creates a KMS Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +AWS KMS keyrings can be used independently or in a multi-keyring with other keyrings +of the same or a different type. + +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use std::collections::HashMap; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, + kms_key_id: &str, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create a KMS client. + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let kms_client = aws_sdk_kms::Client::new(&sdk_config); + + // 3. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 4. Create a KMS keyring + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + let kms_keyring = mpl + .create_aws_kms_keyring() + .kms_key_id(kms_key_id) + .kms_client(kms_client) + .send() + .await?; + + // 5. Encrypt the data with the encryption_context + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(kms_keyring.clone()) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 6. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 7. Decrypt your encrypted data using the same keyring you used on encrypt. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(kms_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + println!("KMS Keyring Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the AWS KMS Keyring example + use crate::example_utils::utils; + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + utils::TEST_DEFAULT_KMS_KEY_ID + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_mrk_discovery_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_mrk_discovery_keyring_example.rs new file mode 100644 index 000000000..840f952c8 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_mrk_discovery_keyring_example.rs @@ -0,0 +1,181 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the AWS KMS MRK (multi-region key) Discovery Keyring + +The AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. + +When decrypting, an MRK discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS MRK that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS MRK. + +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. Because it doesn't specify any wrapping keys, a discovery keyring +can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, +the encrypt operation fails. + +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). +This example creates a KMS MRK Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This encrypted ciphertext is then decrypted using an +MRK Discovery keyring. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For information about using multi-Region keys with the AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks + +For more info on KMS MRKs (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html + +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use aws_esdk::aws_cryptography_materialProviders::types::DiscoveryFilter; +use std::collections::HashMap; +use aws_config::Region; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, + mrk_key_id_encrypt: &str, + aws_account_id: &str, + mrk_encrypt_region: String, + mrk_replica_decrypt_region: String, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 3. Create the keyring that determines how your data keys are protected. + // Although this example highlights Discovery keyrings, Discovery keyrings cannot + // be used to encrypt, so for encryption we create an MRK keyring without discovery mode. + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + // Create a KMS client in the first region + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let encrypt_kms_config = aws_sdk_kms::config::Builder::from(&sdk_config) + .region(Region::new(mrk_encrypt_region)) + .build(); + let encrypt_kms_client = aws_sdk_kms::Client::from_conf(encrypt_kms_config); + + // Create a keyring that will encrypt your data, using a KMS MRK in the first region. + let encrypt_kms_keyring = mpl + .create_aws_kms_mrk_keyring() + .kms_key_id(mrk_key_id_encrypt) + .kms_client(encrypt_kms_client) + .send() + .await?; + + // 4. Encrypt the data with the encryptionContext using the encrypt_keyring. + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(encrypt_kms_keyring) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 5. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 6. Now create a Discovery keyring to use for decryption. + // In order to illustrate the MRK behavior of this keyring, we configure + // the keyring to use the second KMS region where the MRK (mrk_key_id_encrypt) is replicated to. + // This example assumes you have already replicated your key, but since we + // are using a discovery keyring, we don't need to provide the mrk replica key id + + // Create a KMS Client in the second region. + let decrypt_kms_config = aws_sdk_kms::config::Builder::from(&sdk_config) + .region(Region::new(mrk_replica_decrypt_region.clone())) + .build(); + let decrypt_kms_client = aws_sdk_kms::Client::from_conf(decrypt_kms_config); + + let discovery_filter = DiscoveryFilter::builder() + .account_ids(vec![aws_account_id.to_string()]) + .partition("aws".to_string()) + .build()?; + + let discovery_keyring = mpl + .create_aws_kms_mrk_discovery_keyring() + .kms_client(decrypt_kms_client) + .region(mrk_replica_decrypt_region) + .discovery_filter(discovery_filter) + .send() + .await?; + + // 7. Decrypt your encrypted data using the discovery keyring. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(discovery_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + println!("KMS MRK Discovery Keyring Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the AWS KMS MRK Discovery Keyring example + use crate::example_utils::utils; + + let mrk_encrypt_region: String = "us-east-1".to_string(); + let mrk_replica_decrypt_region: String = "eu-west-1".to_string(); + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + utils::TEST_MRK_KEY_ID_US_EAST_1, + utils::TEST_DEFAULT_KMS_KEY_ACCOUNT_ID, + mrk_encrypt_region, + mrk_replica_decrypt_region, + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_mrk_discovery_multi_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_mrk_discovery_multi_keyring_example.rs new file mode 100644 index 000000000..f3e8acd01 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_mrk_discovery_multi_keyring_example.rs @@ -0,0 +1,191 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the AWS KMS MRK (multi-region key) Discovery Multi Keyring + +AWS KMS MRK Discovery Multi Keyring is composed of multiple MRK discovery keyrings. + +The AWS KMS discovery keyring is an AWS KMS keyring that doesn't specify any wrapping keys. + +When decrypting, an MRK discovery keyring allows the AWS Encryption SDK to ask AWS KMS to decrypt +any encrypted data key by using the AWS KMS MRK that encrypted it, regardless of who owns or +has access to that AWS KMS key. The call succeeds only when the caller has kms:Decrypt +permission on the AWS KMS MRK. + +The AWS Encryption SDK provides a standard AWS KMS discovery keyring and a discovery keyring +for AWS KMS multi-Region keys. Because it doesn't specify any wrapping keys, a discovery keyring +can't encrypt data. If you use a discovery keyring to encrypt data, alone or in a multi-keyring, +the encrypt operation fails. + +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). +This example creates a KMS MRK Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This encrypted ciphertext is then decrypted using an +MRK Discovery Multi keyring. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For information about using multi-Region keys with the AWS Encryption SDK, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/configure.html#config-mrks + +For more info on KMS MRKs (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html + +For more information on how to use KMS Discovery keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html#kms-keyring-discovery + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use aws_esdk::aws_cryptography_materialProviders::types::DiscoveryFilter; +use std::collections::HashMap; +use aws_config::Region; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, + mrk_key_id_encrypt: &str, + mrk_encrypt_region: String, + aws_account_id: &str, + aws_regions: Vec, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 3. Create the keyring that determines how your data keys are protected. + // Although this example highlights Discovery keyrings, Discovery keyrings cannot + // be used to encrypt, so for encryption we create an MRK keyring without discovery mode. + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + // Create a KMS client in the first region + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let encrypt_kms_config = aws_sdk_kms::config::Builder::from(&sdk_config) + .region(Region::new(mrk_encrypt_region)) + .build(); + let encrypt_kms_client = aws_sdk_kms::Client::from_conf(encrypt_kms_config); + + // Create a keyring that will encrypt your data, using a KMS MRK in the first region. + let encrypt_kms_keyring = mpl + .create_aws_kms_mrk_keyring() + .kms_key_id(mrk_key_id_encrypt) + .kms_client(encrypt_kms_client) + .send() + .await?; + + // 4. Encrypt the data with the encryptionContext using the encrypt_keyring. + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(encrypt_kms_keyring) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 5. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 6. Now create a MRK Discovery Multi Keyring to use for decryption. + // We'll add a discovery filter to limit the set of encrypted data keys + // we are willing to decrypt to only ones created by KMS keys in select + // accounts and the partition `aws`. + // MRK Discovery keyrings also filter encrypted data keys by the region + // the keyring is created with. + let discovery_filter = DiscoveryFilter::builder() + .account_ids(vec![aws_account_id.to_string()]) + .partition("aws".to_string()) + .build()?; + + // This is a Multi Keyring composed of Discovery Keyrings. + // There is a keyring for every region in `regions`. + // All the keyrings have the same Discovery Filter. + // Each keyring has its own KMS Client, which is created for the keyring's region. + let discovery_multi_keyring = mpl + .create_aws_kms_mrk_discovery_multi_keyring() + .regions(aws_regions) + .discovery_filter(discovery_filter) + .send() + .await?; + + // 7. Decrypt your encrypted data using the discovery multi keyring. + // On Decrypt, the header of the encrypted message (ciphertext) will be parsed. + // The header contains the Encrypted Data Keys (EDKs), which, if the EDK + // was encrypted by a KMS Keyring, includes the KMS Key ARN. + // For each member of the Multi Keyring, every EDK will try to be decrypted until a decryption + // is successful. + // Since every member of the Multi Keyring is a Discovery Keyring: + // Each Keyring will filter the EDKs by the Discovery Filter and the Keyring's region. + // For each filtered EDK, the keyring will attempt decryption with the keyring's client. + // All of this is done serially, until a success occurs or all keyrings have failed + // all (filtered) EDKs. KMS MRK Discovery Keyrings will attempt to decrypt + // Multi Region Keys (MRKs) and regular KMS Keys. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(discovery_multi_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + println!("KMS MRK Discovery Multi Keyring Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the AWS KMS MRK Discovery Multi Keyring example + use crate::example_utils::utils; + + let mrk_encrypt_region: String = "us-east-1".to_string(); + let aws_regions: Vec = vec!["us-east-1".to_string(), "us-west-2".to_string()]; + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + utils::TEST_MRK_KEY_ID_US_EAST_1, + mrk_encrypt_region, + utils::TEST_DEFAULT_KMS_KEY_ACCOUNT_ID, + aws_regions, + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_mrk_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_mrk_keyring_example.rs new file mode 100644 index 000000000..eff2d0e93 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_mrk_keyring_example.rs @@ -0,0 +1,156 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the AWS KMS MRK (multi-region key) Keyring + +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with multi-region AWS KMS keys (MRKs). +This example creates a KMS MRK Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +AWS KMS MRK keyrings can be used independently or in a multi-keyring with other keyrings +of the same or a different type. + +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html + +For more info on KMS MRK (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use std::collections::HashMap; +use aws_config::Region; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, + mrk_key_id_encrypt: &str, + mrk_replica_key_id_decrypt: &str, + mrk_encrypt_region: String, + mrk_replica_decrypt_region: String, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 3. Create a keyring that will encrypt your data, using a KMS MRK in the first region. + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + // Create a KMS client in the first region + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let encrypt_kms_config = aws_sdk_kms::config::Builder::from(&sdk_config) + .region(Region::new(mrk_encrypt_region)) + .build(); + let encrypt_kms_client = aws_sdk_kms::Client::from_conf(encrypt_kms_config); + + // Create the keyring that determines how your data keys are protected. + let encrypt_kms_keyring = mpl + .create_aws_kms_mrk_keyring() + .kms_key_id(mrk_key_id_encrypt) + .kms_client(encrypt_kms_client) + .send() + .await?; + + // 4. Encrypt the data with the encryptionContext using the encrypt_keyring. + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(encrypt_kms_keyring) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 5. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 6. Create a keyring that will decrypt your data, using the same KMS MRK replicated + // to the second region. This example assumes you have already replicated your key + + // Create a KMS Client in the second region. + let decrypt_kms_config = aws_sdk_kms::config::Builder::from(&sdk_config) + .region(Region::new(mrk_replica_decrypt_region)) + .build(); + let decrypt_kms_client = aws_sdk_kms::Client::from_conf(decrypt_kms_config); + + let decrypt_kms_keyring = mpl + .create_aws_kms_mrk_keyring() + .kms_key_id(mrk_replica_key_id_decrypt) + .kms_client(decrypt_kms_client) + .send() + .await?; + + // 7. Decrypt your encrypted data using the decrypt keyring. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(decrypt_kms_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + println!("KMS MRK Keyring Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the AWS KMS MRK Keyring example + use crate::example_utils::utils; + + let mrk_encrypt_region: String = "us-east-1".to_string(); + let mrk_replica_decrypt_region: String = "eu-west-1".to_string(); + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + utils::TEST_MRK_KEY_ID_US_EAST_1, + utils::TEST_MRK_KEY_ID_EU_WEST_1, + mrk_encrypt_region, + mrk_replica_decrypt_region, + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_mrk_multi_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_mrk_multi_keyring_example.rs new file mode 100644 index 000000000..cbd0ae88c --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_mrk_multi_keyring_example.rs @@ -0,0 +1,182 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the AWS KMS MRK Multi Keyring + +The AWS Key Management Service (AWS KMS) MRK keyring interacts with AWS KMS to +create, encrypt, and decrypt data keys with AWS KMS MRK keys. +The KMS MRK multi-keyring consists of one or more individual keyrings of the +same or different type. The keys can either be regular KMS keys or MRKs. +The effect is like using several keyrings in a series. + +This example creates a AwsKmsMrkMultiKeyring using an mrk_key_id (generator) and a kms_key_id +as a child key, and then encrypts a custom input EXAMPLE_DATA with an encryption context. +Either KMS Key individually is capable of decrypting data encrypted under this keyring. +This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +3. Ciphertext can be decrypted using an AwsKmsMrkKeyring containing a replica of the + MRK (from the multi-keyring used for encryption) copied from the first region into + the second region +These sanity checks are for demonstration in the example only. You do not need these in your code. + +For more information on how to use KMS keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html + +For more info on KMS MRK (multi-region keys), see the KMS documentation: +https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use std::collections::HashMap; +use aws_config::Region; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, + mrk_key_id: &str, + kms_key_id: &str, + mrk_replica_key_id: &str, + mrk_replica_decrypt_region: String, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 3. Create an AwsKmsMrkMultiKeyring that protects your data under two different KMS Keys. + // The Keys can either be regular KMS keys or MRKs. + // Either KMS Key individually is capable of decrypting data encrypted under this keyring. + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + let kms_mrk_multi_keyring = mpl + .create_aws_kms_mrk_multi_keyring() + .generator(mrk_key_id) + .kms_key_ids(vec![kms_key_id.to_string()]) + .send() + .await?; + + // 4. Encrypt the data with the encryptionContext using the kms_mrk_multi_keyring. + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(kms_mrk_multi_keyring.clone()) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 5. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 6. Decrypt your encrypted data using the same AwsKmsMrkMultiKeyring you used on encrypt. + // It will decrypt the data using the generator key (in this case, the MRK), since that is + // the first available KMS key on the keyring that is capable of decrypting the data. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext.clone()) + .keyring(kms_mrk_multi_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 7. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // Demonstrate that a single AwsKmsMrkKeyring configured with a replica of the MRK from the + // multi-keyring used to encrypt the data is also capable of decrypting the data. + // (This is an example for demonstration; you do not need to do this in your own code.) + + // 8. Create a single AwsKmsMrkKeyring with the replica KMS MRK from the second region. + + // Create a client for KMS in the second region which is the region for mrk_replica_key_id. + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let second_region_kms_config = aws_sdk_kms::config::Builder::from(&sdk_config) + .region(Region::new(mrk_replica_decrypt_region)) + .build(); + let second_region_kms_client = aws_sdk_kms::Client::from_conf(second_region_kms_config); + + let second_region_mrk_keyring = mpl + .create_aws_kms_mrk_keyring() + .kms_key_id(mrk_replica_key_id) + .kms_client(second_region_kms_client) + .send() + .await?; + + // 9. Decrypt your encrypted data using the second region AwsKmsMrkKeyring + let second_region_decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(second_region_mrk_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let second_region_decrypted_plaintext = second_region_decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 10. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(second_region_decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // Not shown in this example: A KMS Keyring created with `kms_key_id` could also + // decrypt this message. + + println!("KMS MRK Multi Keyring Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the AWS KMS MRK Multi Keyring example + use crate::example_utils::utils; + + let mrk_replica_decrypt_region: String = "eu-west-1".to_string(); + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + utils::TEST_MRK_KEY_ID_US_EAST_1, + utils::TEST_DEFAULT_KMS_KEY_ID, + utils::TEST_MRK_KEY_ID_EU_WEST_1, + mrk_replica_decrypt_region, + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_multi_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_multi_keyring_example.rs new file mode 100644 index 000000000..245d21c18 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_multi_keyring_example.rs @@ -0,0 +1,228 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the AWS KMS Multi Keyring made up of multiple AWS KMS Keyrings. + +A multi-keyring is a keyring that consists of one or more individual keyrings of the +same or a different type. The effect is like using several keyrings in a series. +When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its +keyrings can decrypt that data. + +When you create a multi-keyring to encrypt data, you designate one of the keyrings as +the generator keyring. All other keyrings are known as child keyrings. The generator keyring +generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the +child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext +key and one encrypted data key for each wrapping key in the multi-keyring. If you create a +multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt. +If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates +and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, +and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. + +When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted +data keys. The keyrings are called in the order that they are specified in the multi-keyring. +Processing stops as soon as any key in any keyring can decrypt an encrypted data key. + +This example creates a Multi Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decryption of ciphertext is possible using the multi_keyring, + and every one of the keyrings from the multi_keyring separately +3. All decrypted plaintext value match EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +This example creates a multi_keyring using a KMS keyring as generator keyring and +another KMS keyring as a child keyring. + +For more information on how to use Multi keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use std::collections::HashMap; +use aws_config::Region; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, + default_region_kms_key_id: &str, + second_region_kms_key_id: &str, + default_region: String, + second_region: String, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 3. Create an AwsKmsMultiKeyring that protects your data under two different KMS Keys. + // Either KMS Key individually is capable of decrypting data encrypted under this Multi Keyring. + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + let kms_multi_keyring = mpl + .create_aws_kms_multi_keyring() + .generator(default_region_kms_key_id) + .kms_key_ids(vec![second_region_kms_key_id.to_string()]) + .send() + .await?; + + // 4. Encrypt the data with the encryptionContext + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(kms_multi_keyring.clone()) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 5. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 6a. Decrypt your encrypted data using the same multi_keyring you used on encrypt. + let decryption_response_multi_keyring = esdk_client.decrypt() + .ciphertext(ciphertext.clone()) + .keyring(kms_multi_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let decrypted_plaintext_multi_keyring = + decryption_response_multi_keyring + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 6b. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext_multi_keyring, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // Because you used a multi_keyring on Encrypt, you can use either of the two + // kms keyrings individually to decrypt the data. + + // 7. Demonstrate that you can successfully decrypt data using a KMS keyring with just the + // `default_region_kms_key_id` directly. + // (This is an example for demonstration; you do not need to do this in your own code.) + + // 7a. Create a client for KMS for the default region. + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let default_region_kms_config = aws_sdk_kms::config::Builder::from(&sdk_config) + .region(Region::new(default_region)) + .build(); + let default_region_kms_client = aws_sdk_kms::Client::from_conf(default_region_kms_config); + + // 7b. Create KMS keyring + let default_region_kms_keyring = mpl + .create_aws_kms_keyring() + .kms_key_id(default_region_kms_key_id) + .kms_client(default_region_kms_client) + .send() + .await?; + + // 7c. Decrypt your encrypted data using the default_region_kms_keyring. + let decryption_response_default_region_kms_keyring = esdk_client.decrypt() + .ciphertext(ciphertext.clone()) + .keyring(default_region_kms_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let decrypted_plaintext_default_region_kms_keyring = + decryption_response_default_region_kms_keyring + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 7d. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext_default_region_kms_keyring, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // 8. Demonstrate that you can also successfully decrypt data using a KMS keyring with just the + // `second_region_kms_key_id` directly. + // (This is an example for demonstration; you do not need to do this in your own code.) + + // 8a. Create a client for KMS for the second region. + let second_region_kms_config = aws_sdk_kms::config::Builder::from(&sdk_config) + .region(Region::new(second_region)) + .build(); + let second_region_kms_client = aws_sdk_kms::Client::from_conf(second_region_kms_config); + + // 8b. Create KMS keyring + let second_region_kms_keyring = mpl + .create_aws_kms_keyring() + .kms_key_id(second_region_kms_key_id) + .kms_client(second_region_kms_client) + .send() + .await?; + + // 8c. Decrypt your encrypted data using the second_region_kms_keyring. + let decryption_response_second_region_kms_keyring = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(second_region_kms_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let decrypted_plaintext_second_region_kms_keyring = + decryption_response_second_region_kms_keyring + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 8d. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext_second_region_kms_keyring, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + println!("KMS Multi Keyring Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the AWS KMS Multi Keyring example + use crate::example_utils::utils; + + let default_region: String = "us-west-2".to_string(); + let second_region: String = "eu-central-1".to_string(); + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + utils::TEST_DEFAULT_KMS_KEY_ID, + utils::TEST_SECOND_REGION_KMS_KEY_ID, + default_region, + second_region, + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_rsa_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_rsa_keyring_example.rs new file mode 100644 index 000000000..a456338e6 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/aws_kms_rsa_keyring_example.rs @@ -0,0 +1,127 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the AWS KMS RSA Keyring + +This example creates a KMS RSA Keyring and then encrypts a custom input +example_data with an encryption context. This example also includes some sanity checks for +demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +# For more information on how to use KMS keyrings, see +# https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::EsdkAlgorithmSuiteId; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use std::collections::HashMap; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, + kms_rsa_key_id: &str, + kms_rsa_public_key: &str, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create a KMS client. + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let kms_client = aws_sdk_kms::Client::new(&sdk_config); + + // 3. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 4. Create a KMS RSA keyring + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + // For more information on the allowed encryption algorithms, please see + // https://docs.aws.amazon.com/kms/latest/developerguide/asymmetric-key-specs.html#key-spec-rsa + let kms_rsa_keyring = mpl + .create_aws_kms_rsa_keyring() + .kms_key_id(kms_rsa_key_id) + .public_key(aws_smithy_types::Blob::new(kms_rsa_public_key)) + .encryption_algorithm(aws_sdk_kms::types::EncryptionAlgorithmSpec::RsaesOaepSha256) + .kms_client(kms_client) + .send() + .await?; + + // 5. Encrypt the data with the encryption_context + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(kms_rsa_keyring.clone()) + .encryption_context(encryption_context.clone()) + .algorithm_suite_id(EsdkAlgorithmSuiteId::AlgAes256GcmHkdfSha512CommitKey) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 6. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 7. Decrypt your encrypted data using the same keyring you used on encrypt. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(kms_rsa_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + println!("KMS RSA Keyring Example Completed Successfully"); + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the AWS KMS RSA Keyring example + use crate::example_utils::utils; + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + utils::TEST_KMS_RSA_KEY_ID, + utils::TEST_KMS_RSA_PUBLIC_KEY + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/mod.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/mod.rs new file mode 100644 index 000000000..a1f8f84f6 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/mod.rs @@ -0,0 +1,13 @@ +pub mod aws_kms_discovery_keyring_example; +pub mod aws_kms_discovery_multi_keyring_example; +pub mod aws_kms_keyring_example; +pub mod aws_kms_mrk_discovery_keyring_example; +pub mod aws_kms_mrk_discovery_multi_keyring_example; +pub mod aws_kms_mrk_keyring_example; +pub mod aws_kms_mrk_multi_keyring_example; +pub mod aws_kms_multi_keyring_example; +pub mod aws_kms_rsa_keyring_example; +pub mod multi_keyring_example; +pub mod raw_aes_keyring_example; +pub mod raw_rsa_keyring_example; +pub mod aws_kms_hierarchical; diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/multi_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/multi_keyring_example.rs new file mode 100644 index 000000000..8a9a227ee --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/multi_keyring_example.rs @@ -0,0 +1,237 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the Multi Keyring + +A multi-keyring is a keyring that consists of one or more individual keyrings of the +same or a different type. The effect is like using several keyrings in a series. +When you use a multi-keyring to encrypt data, any of the wrapping keys in any of its +keyrings can decrypt that data. + +When you create a multi-keyring to encrypt data, you designate one of the keyrings as +the generator keyring. All other keyrings are known as child keyrings. The generator keyring +generates and encrypts the plaintext data key. Then, all of the wrapping keys in all of the +child keyrings encrypt the same plaintext data key. The multi-keyring returns the plaintext +key and one encrypted data key for each wrapping key in the multi-keyring. If you create a +multi-keyring with no generator keyring, you can use it to decrypt data, but not to encrypt. +If the generator keyring is a KMS keyring, the generator key in the AWS KMS keyring generates +and encrypts the plaintext key. Then, all additional AWS KMS keys in the AWS KMS keyring, +and all wrapping keys in all child keyrings in the multi-keyring, encrypt the same plaintext key. + +When decrypting, the AWS Encryption SDK uses the keyrings to try to decrypt one of the encrypted +data keys. The keyrings are called in the order that they are specified in the multi-keyring. +Processing stops as soon as any key in any keyring can decrypt an encrypted data key. + +This example creates a Multi Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decryption of ciphertext is possible using the multi_keyring, +and every one of the keyrings from the multi_keyring separately +3. All decrypted plaintext value match EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +This example creates a multi_keyring using a KMS keyring as generator keyring and a raw AES keyring +as a child keyring. You can use different combinations of keyrings in the multi_keyring. + +For more information on how to use Multi keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-multi-keyring.html + +For more information on KMS Key identifiers, see +https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-id +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use aws_esdk::aws_cryptography_materialProviders::types::AesWrappingAlg; +use std::collections::HashMap; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, + kms_key_id: &str, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create a KMS client. + let sdk_config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let kms_client = aws_sdk_kms::Client::new(&sdk_config); + + // 3. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 4. Create a KMS keyring + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + let kms_keyring = mpl + .create_aws_kms_keyring() + .kms_key_id(kms_key_id) + .kms_client(kms_client) + .send() + .await?; + + // 5. Create a raw AES keyring to additionally encrypt under as child_keyring + + // The key namespace and key name are defined by you. + // and are used by the Raw AES keyring to determine + // whether it should attempt to decrypt an encrypted data key. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html + let key_namespace: &str = "my-key-namespace"; + let key_name: &str = "my-aes-key-name"; + + // Generate a 256-bit AES key to use with your raw AES keyring. + // In practice, you should get this key from a secure key management system such as an HSM. + let aes_key_bytes = generate_aes_key_bytes(); + + let raw_aes_keyring = mpl + .create_raw_aes_keyring() + .key_name(key_name) + .key_namespace(key_namespace) + .wrapping_key(aws_smithy_types::Blob::new(aes_key_bytes)) + .wrapping_alg(AesWrappingAlg::AlgAes256GcmIv12Tag16) + .send() + .await?; + + // 6. Create a multi_keyring that consists of the previously created keyrings. + // When using this multi_keyring to encrypt data, either `kms_keyring` or + // `raw_aes_keyring` (or a multi_keyring containing either) may be used to decrypt the data. + let multi_keyring = mpl + .create_multi_keyring() + .generator(kms_keyring.clone()) + .child_keyrings(vec![raw_aes_keyring.clone()]) + .send() + .await?; + + // 7. Encrypt the data with the encryptionContext + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(multi_keyring.clone()) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 8. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 9a. Decrypt your encrypted data using the same multi_keyring you used on encrypt. + let decryption_response_multi_keyring = esdk_client.decrypt() + .ciphertext(ciphertext.clone()) + .keyring(multi_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let decrypted_plaintext_multi_keyring = + decryption_response_multi_keyring + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 9b. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext_multi_keyring, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // Because you used a multi_keyring on Encrypt, you can use either the + // `kms_keyring` or `raw_aes_keyring` individually to decrypt the data. + + // 10. Demonstrate that you can successfully decrypt data using just the `kms_keyring` + // directly. + // (This is an example for demonstration; you do not need to do this in your own code.) + + // 10a. Decrypt your encrypted data using the kms_keyring. + let decryption_response_kms_keyring = esdk_client.decrypt() + .ciphertext(ciphertext.clone()) + .keyring(kms_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let decrypted_plaintext_kms_keyring = + decryption_response_kms_keyring + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 10b. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext_kms_keyring, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + // 11. Demonstrate that you can also successfully decrypt data using the `raw_aes_keyring` + // directly. + // (This is an example for demonstration; you do not need to do this in your own code.) + + // 11a. Decrypt your encrypted data using the raw_aes_keyring. + let decryption_response_raw_aes_keyring = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(raw_aes_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let decrypted_plaintext_raw_aes_keyring = + decryption_response_raw_aes_keyring + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 11b. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext_raw_aes_keyring, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + println!("Multi Keyring Example Completed Successfully"); + + Ok(()) +} + +fn generate_aes_key_bytes() -> Vec { + // This example returns a static key. + // In practice, you should not generate this key in your code, and should instead + // retrieve this key from a secure key management system (e.g. HSM). + // This key is created here for example purposes only and should not be used for any other purpose. + vec![ + 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, + ] +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the Multi Keyring example + use crate::example_utils::utils; + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + utils::TEST_DEFAULT_KMS_KEY_ID, + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/raw_aes_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/raw_aes_keyring_example.rs new file mode 100644 index 000000000..760028c69 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/raw_aes_keyring_example.rs @@ -0,0 +1,144 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the Raw AES Keyring + +The Raw AES keyring lets you use an AES symmetric key that you provide as a wrapping key that +protects your data key. You need to generate, store, and protect the key material, +preferably in a hardware security module (HSM) or key management system. Use a Raw AES keyring +when you need to provide the wrapping key and encrypt the data keys locally or offline. + +This example creates a Raw AES Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +These sanity checks are for demonstration in the example only. You do not need these in your code. + +The Raw AES keyring encrypts data by using the AES-GCM algorithm and a wrapping key that +you specify as a byte array. You can specify only one wrapping key in each Raw AES keyring, +but you can include multiple Raw AES keyrings, alone or with other keyrings, in a multi-keyring. + +For more information on how to use Raw AES keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use aws_esdk::aws_cryptography_materialProviders::types::AesWrappingAlg; +use std::collections::HashMap; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. The key namespace and key name are defined by you. + // and are used by the Raw AES keyring to determine + // whether it should attempt to decrypt an encrypted data key. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-aes-keyring.html + let key_namespace: &str = "my-key-namespace"; + let key_name: &str = "my-aes-key-name"; + + // 3. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 4. Generate a 256-bit AES key to use with your keyring. + // In practice, you should get this key from a secure key management system such as an HSM. + let aes_key_bytes = generate_aes_key_bytes(); + + // 5. Create a Raw AES Keyring + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + let raw_aes_keyring = mpl + .create_raw_aes_keyring() + .key_name(key_name) + .key_namespace(key_namespace) + .wrapping_key(aws_smithy_types::Blob::new(aes_key_bytes)) + .wrapping_alg(AesWrappingAlg::AlgAes256GcmIv12Tag16) + .send() + .await?; + + // 6. Encrypt the data with the encryptionContext + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(raw_aes_keyring.clone()) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 7. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 8. Decrypt your encrypted data using the same keyring you used on encrypt. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(raw_aes_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 8. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + println!("Raw AES Keyring Example Completed Successfully"); + + Ok(()) +} + +fn generate_aes_key_bytes() -> Vec { + // This example returns a static key. + // In practice, you should not generate this key in your code, and should instead + // retrieve this key from a secure key management system (e.g. HSM). + // This key is created here for example purposes only and should not be used for any other purpose. + vec![ + 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, + ] +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the Raw AES Keyring example + use crate::example_utils::utils; + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/keyring/raw_rsa_keyring_example.rs b/AwsEncryptionSDK/runtimes/rust/examples/keyring/raw_rsa_keyring_example.rs new file mode 100644 index 000000000..2bf7453ab --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/keyring/raw_rsa_keyring_example.rs @@ -0,0 +1,253 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* +This example sets up the Raw RSA Keyring + +The Raw RSA keyring performs asymmetric encryption and decryption of data keys in local memory +with RSA public and private keys that you provide. + +This keyring accepts PEM encodings of the key pair as UTF-8 interpreted bytes. +The encryption function encrypts the data key under the RSA public key. The decryption function +decrypts the data key using the private key. + +This example loads a key pair from PEM files with paths defined in + - EXAMPLE_RSA_PRIVATE_KEY_FILENAME + - EXAMPLE_RSA_PUBLIC_KEY_FILENAME +If you do not provide these files, running this example through this +class' main method will generate these files for you. These files will +be generated in the directory where the example is run. +In practice, users of this library should not generate new key pairs +like this, and should instead retrieve an existing key from a secure +key management system (e.g. an HSM). +You may also provide your own key pair by placing PEM files in the +directory where the example is run or modifying the paths in the code +below. These files must be valid PEM encodings of the key pair as UTF-8 +encoded bytes. If you do provide your own key pair, or if a key pair +already exists, this class' main method will not generate a new key pair. + +This example creates a Raw RSA Keyring and then encrypts a custom input EXAMPLE_DATA +with an encryption context. This example also includes some sanity checks for demonstration: +1. Ciphertext and plaintext data are not the same +2. Decrypted plaintext value matches EXAMPLE_DATA +3. The original ciphertext is not decryptable using a keyring with a different RSA key pair +These sanity checks are for demonstration in the example only. You do not need these in your code. + +A Raw RSA keyring that encrypts and decrypts must include an asymmetric public key and private +key pair. However, you can encrypt data with a Raw RSA keyring that has only a public key, +and you can decrypt data with a Raw RSA keyring that has only a private key. This example requires +the user to either provide both private and public keys, or not provide any keys and the example +generates both to test encryption and decryption. If you configure a Raw RSA keyring with a +public and private key, be sure that they are part of the same key pair. Some language +implementations of the AWS Encryption SDK will not construct a Raw RSA keyring with keys +from different pairs. Others rely on you to verify that your keys are from the same key pair. +You can include any Raw RSA keyring in a multi-keyring. + +For more information on how to use Raw RSA keyrings, see +https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-rsa-keyring.html +*/ + +use aws_esdk::client as esdk_client; +use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig; +use aws_esdk::aws_cryptography_materialProviders::client as mpl_client; +use aws_esdk::aws_cryptography_materialProviders::types::material_providers_config::MaterialProvidersConfig; +use aws_esdk::aws_cryptography_materialProviders::types::PaddingScheme; +use std::collections::HashMap; +use std::fs::File; +use std::io::Read; +use std::io::Write; +use std::path::Path; + +const EXAMPLE_RSA_PRIVATE_KEY_FILENAME: &str = "RawRsaKeyringExamplePrivateKey.pem"; +const EXAMPLE_RSA_PUBLIC_KEY_FILENAME: &str = "RawRsaKeyringExamplePublicKey.pem"; + +pub async fn encrypt_and_decrypt_with_keyring( + example_data: &str, +) -> Result<(), crate::BoxError> { + // 1. Instantiate the encryption SDK client. + // This builds the default client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy, + // which enforces that this client only encrypts using committing algorithm suites and enforces + // that this client will only decrypt encrypted messages that were created with a committing + // algorithm suite. + let esdk_config = AwsEncryptionSdkConfig::builder().build()?; + let esdk_client = esdk_client::Client::from_conf(esdk_config)?; + + // 2. Create encryption context. + // Remember that your encryption context is NOT SECRET. + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context + let encryption_context = HashMap::from([ + ("encryption".to_string(), "context".to_string()), + ("is not".to_string(), "secret".to_string()), + ("but adds".to_string(), "useful metadata".to_string()), + ("that can help you".to_string(), "be confident that".to_string()), + ("the data you are handling".to_string(), "is what you think it is".to_string()), + ]); + + // 3. You may provide your own RSA key pair in the files located at + // - EXAMPLE_RSA_PRIVATE_KEY_FILENAME + // - EXAMPLE_RSA_PUBLIC_KEY_FILENAME + // If these files are not present, this will generate a pair for you + if should_generate_new_rsa_key_pair()? { + generate_rsa_key_pair()?; + } + + // 4. Load key pair from UTF-8 encoded PEM files. + // You may provide your own PEM files to use here. + // If you do not, the main method in this class will generate PEM + // files for example use. Do not use these files for any other purpose. + let mut file = File::open(Path::new(EXAMPLE_RSA_PUBLIC_KEY_FILENAME))?; + let mut public_key_utf8_bytes = Vec::new(); + file.read_to_end(&mut public_key_utf8_bytes)?; + + let mut file = File::open(Path::new(EXAMPLE_RSA_PRIVATE_KEY_FILENAME))?; + let mut private_key_utf8_bytes = Vec::new(); + file.read_to_end(&mut private_key_utf8_bytes)?; + + // 5. The key namespace and key name are defined by you. + // and are used by the Raw RSA keyring + // For more information, see + // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-raw-rsa-keyring.html + let key_namespace: &str = "my-key-namespace"; + let key_name: &str = "my-rsa-key-name"; + + // 6. Create the Raw RSA keyring. + let mpl_config = MaterialProvidersConfig::builder().build()?; + let mpl = mpl_client::Client::from_conf(mpl_config)?; + + let raw_rsa_keyring = mpl + .create_raw_rsa_keyring() + .key_name(key_name) + .key_namespace(key_namespace) + .padding_scheme(PaddingScheme::OaepSha256Mgf1) + .public_key(aws_smithy_types::Blob::new(public_key_utf8_bytes)) + .private_key(aws_smithy_types::Blob::new(private_key_utf8_bytes)) + .send() + .await?; + + // 7. Encrypt the data with the encryptionContext + let plaintext = aws_smithy_types::Blob::new(example_data); + + let encryption_response = esdk_client.encrypt() + .plaintext(plaintext.clone()) + .keyring(raw_rsa_keyring.clone()) + .encryption_context(encryption_context.clone()) + .send() + .await?; + + let ciphertext = encryption_response + .ciphertext + .expect("Unable to unwrap ciphertext from encryption response"); + + // 8. Demonstrate that the ciphertext and plaintext are different. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_ne!(ciphertext, plaintext, + "Ciphertext and plaintext data are the same. Invalid encryption"); + + // 9. Decrypt your encrypted data using the same keyring you used on encrypt. + let decryption_response = esdk_client.decrypt() + .ciphertext(ciphertext) + .keyring(raw_rsa_keyring) + // Provide the encryption context that was supplied to the encrypt method + .encryption_context(encryption_context) + .send() + .await?; + + let decrypted_plaintext = decryption_response + .plaintext + .expect("Unable to unwrap plaintext from decryption response"); + + // 10. Demonstrate that the decrypted plaintext is identical to the original plaintext. + // (This is an example for demonstration; you do not need to do this in your own code.) + assert_eq!(decrypted_plaintext, plaintext, + "Decrypted plaintext should be identical to the original plaintext. Invalid decryption"); + + println!("Raw RSA Keyring Example Completed Successfully"); + + Ok(()) +} + +fn exists(f: &str) -> bool { + Path::new(f).exists() +} +fn should_generate_new_rsa_key_pair() -> Result { + // If a key pair already exists: do not overwrite existing key pair + if exists(EXAMPLE_RSA_PRIVATE_KEY_FILENAME) && exists(EXAMPLE_RSA_PUBLIC_KEY_FILENAME) { + Ok(false) + } + // If only one file is present: throw exception + else if exists(EXAMPLE_RSA_PRIVATE_KEY_FILENAME) && !exists(EXAMPLE_RSA_PUBLIC_KEY_FILENAME) { + Err("Missing public key file at ".to_string() + EXAMPLE_RSA_PUBLIC_KEY_FILENAME) + } + // If a key pair already exists: do not overwrite existing key pair + else if exists(EXAMPLE_RSA_PRIVATE_KEY_FILENAME) && !exists(EXAMPLE_RSA_PUBLIC_KEY_FILENAME) { + Err("Missing private key file at ".to_string() + EXAMPLE_RSA_PRIVATE_KEY_FILENAME) + } + // If neither file is present, generate a new key pair + else { + Ok(true) + } +} + +fn generate_rsa_key_pair() -> Result<(), crate::BoxError> { + use aws_lc_rs::encoding::AsDer; + use aws_lc_rs::encoding::Pkcs8V1Der; + use aws_lc_rs::encoding::PublicKeyX509Der; + use aws_lc_rs::rsa::KeySize; + use aws_lc_rs::rsa::PrivateDecryptingKey; + + // Safety check: Validate neither file is present + if exists(EXAMPLE_RSA_PRIVATE_KEY_FILENAME) || exists(EXAMPLE_RSA_PUBLIC_KEY_FILENAME) { + return Err(crate::BoxError( + "generate_rsa_key_pair will not overwrite existing PEM files".to_string(), + )); + } + + // This code will generate a new RSA key pair for example use. + // The public and private key will be written to the files: + // - public: EXAMPLE_RSA_PUBLIC_KEY_FILENAME + // - private: EXAMPLE_RSA_PRIVATE_KEY_FILENAME + // This example uses aws-lc-rs's KeyPairGenerator to generate the key pair. + // In practice, you should not generate this in your code, and should instead + // retrieve this key from a secure key management system (e.g. HSM) + // This key is created here for example purposes only. + + let private_key = PrivateDecryptingKey::generate(KeySize::Rsa2048)?; + let public_key = private_key.public_key(); + + let public_key = AsDer::::as_der(&public_key)?; + let public_key = pem::Pem::new("RSA PUBLIC KEY", public_key.as_ref()); + let public_key = pem::encode(&public_key); + + let private_key = AsDer::::as_der(&private_key)?; + let private_key = pem::Pem::new("RSA PRIVATE KEY", private_key.as_ref()); + let private_key = pem::encode(&private_key); + + std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(Path::new(EXAMPLE_RSA_PRIVATE_KEY_FILENAME))? + .write_all(private_key.as_bytes())?; + + std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(Path::new(EXAMPLE_RSA_PUBLIC_KEY_FILENAME))? + .write_all(public_key.as_bytes())?; + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +pub async fn test_encrypt_and_decrypt_with_keyring() -> Result<(), crate::BoxError2> { + // Test function for encrypt and decrypt using the Raw RSA Keyring example + use crate::example_utils::utils; + + encrypt_and_decrypt_with_keyring( + utils::TEST_EXAMPLE_DATA, + ).await?; + + Ok(()) +} diff --git a/AwsEncryptionSDK/runtimes/rust/examples/main.rs b/AwsEncryptionSDK/runtimes/rust/examples/main.rs new file mode 100644 index 000000000..79651c556 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/examples/main.rs @@ -0,0 +1,46 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +pub mod client_supplier; +pub mod cryptographic_materials_manager; +pub mod keyring; +pub mod example_utils; + +use std::convert::From; + +// Why two types? +// return type from main must impl Debug +// but if impl Debug for BoxError +// then I can't impl From for BoxError +// because there's already a impl From for T; + +pub struct BoxError(String); +pub struct BoxError2(String); + +impl std::fmt::Debug for BoxError2 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for BoxError2 { + fn from(error: BoxError) -> Self { + BoxError2(error.0) + } +} + +impl From for BoxError { + fn from(error: T) -> Self { + let my_str = format!("{:?}", error); + BoxError(my_str) + } +} + +#[tokio::main] +pub async fn main() -> Result<(), BoxError2> { + Ok(()) +} \ No newline at end of file diff --git a/AwsEncryptionSDK/runtimes/rust/src/aes_gcm.rs b/AwsEncryptionSDK/runtimes/rust/src/aes_gcm.rs new file mode 100644 index 000000000..cbf0a8f78 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/aes_gcm.rs @@ -0,0 +1,255 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +use crate::software::amazon::cryptography::primitives::internaldafny::types::AESEncryptOutput; +use crate::software::amazon::cryptography::primitives::internaldafny::types::Error as DafnyError; +use crate::software::amazon::cryptography::primitives::internaldafny::types::AES_GCM; +use crate::*; +use aws_lc_rs::aead::{Aad, LessSafeKey, Nonce, UnboundKey}; +use std::rc::Rc; + +struct DoAESEncryptOutput { + cipher_text: Vec, + auth_tag: Vec, +} + +fn error(s: &str) -> Rc { + Rc::new(DafnyError::AwsCryptographicPrimitivesError { + message: + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string(s), + }) +} + +fn enc_result(s: &str) -> Rc<_Wrappers_Compile::Result, Rc>> { + Rc::new(_Wrappers_Compile::Result::Failure { error: error(s) }) +} + +fn dec_result( + s: &str, +) -> Rc<_Wrappers_Compile::Result<::dafny_runtime::Sequence, Rc>> { + Rc::new(_Wrappers_Compile::Result::Failure { error: error(s) }) +} + +#[allow(non_snake_case)] +pub mod AESEncryption { + pub use crate::software::amazon::cryptography::primitives::internaldafny::types::*; +} +impl AES_GCM { + fn get_alg(&self) -> Result<&'static aws_lc_rs::aead::Algorithm, String> { + if *self.tagLength() != 16i32 { + Err(format!( + "Tag length of {} not supported in Rust. Tag length must be 16.", + self.tagLength() + )) + } else if *self.ivLength() != 12i32 { + Err(format!( + "IV length of {} not supported in Rust. IV length must be 12.", + self.ivLength() + )) + } else if *self.keyLength() == 32i32 { + Ok(&aws_lc_rs::aead::AES_256_GCM) + } else if *self.keyLength() == 16i32 { + Ok(&aws_lc_rs::aead::AES_128_GCM) + } else { + Err(format!( + "Key length of {} not supported in Rust. Tag length must be 16 or 32.", + self.keyLength() + )) + } + } + + fn do_aes_encrypt( + &self, + iv: &[u8], + key: &[u8], + msg: &[u8], + aad: &[u8], + ) -> Result { + let alg = self.get_alg()?; + let mut in_out_buffer = Vec::from(msg); + let key = UnboundKey::new(alg, key).map_err(|e| format!("new {:?}", e))?; + let nonce = Nonce::assume_unique_for_key(iv.try_into().unwrap()); + let key = LessSafeKey::new(key); + let aad = Aad::from(aad); + let tag = key + .seal_in_place_separate_tag(nonce, aad, &mut in_out_buffer) + .map_err(|e| format!("Seal {:?}", e))?; + Ok(DoAESEncryptOutput { + cipher_text: in_out_buffer, + auth_tag: Vec::from(tag.as_ref()), + }) + } + + fn do_aes_decrypt( + &self, + key: &[u8], + cipher_text: &[u8], + auth_tag: &[u8], + iv: &[u8], + aad: &[u8], + ) -> Result, String> { + let alg = self.get_alg()?; + let mut out_buffer = Vec::from(cipher_text); + let key = UnboundKey::new(alg, key).map_err(|e| format!("new {:?}", e))?; + let nonce = Nonce::assume_unique_for_key(iv.try_into().unwrap()); + let key = LessSafeKey::new(key); + let aad = Aad::from(aad); + key.open_separate_gather(nonce, aad, cipher_text, auth_tag, &mut out_buffer) + .map_err(|e| format!("gather {:?}", e))?; + Ok(out_buffer) + } + + #[allow(non_snake_case)] + pub fn AESEncryptExtern( + &self, + iv: &::dafny_runtime::Sequence, + key: &::dafny_runtime::Sequence, + msg: &::dafny_runtime::Sequence, + aad: &::dafny_runtime::Sequence, + ) -> Rc<_Wrappers_Compile::Result, Rc>> { + let iv: Vec = iv.iter().collect(); + let key: Vec = key.iter().collect(); + let msg: Vec = msg.iter().collect(); + let aad: Vec = aad.iter().collect(); + + if *self.keyLength() as usize != key.len() { + let msg = format!( + "AESEncrypt : algorithm key length was {} but actual key length was {}.", + self.keyLength(), + key.len() + ); + return enc_result(&msg); + } + if *self.ivLength() as usize != iv.len() { + let msg = format!( + "AESEncrypt : algorithm nonce length was {} but actual nonce length was {}.", + self.ivLength(), + iv.len() + ); + return enc_result(&msg); + } + + match self.do_aes_encrypt(&iv, &key, &msg, &aad) { + Ok(x) => Rc::new(_Wrappers_Compile::Result::Success { + value: Rc::new(AESEncryptOutput::AESEncryptOutput { + cipherText: x.cipher_text.iter().cloned().collect(), + authTag: x.auth_tag.iter().cloned().collect(), + }), + }), + Err(e) => { + let msg = format!("AES Encrypt : {}", e); + enc_result(&msg) + } + } + } + + #[allow(non_snake_case)] + pub fn AESDecryptExtern( + &self, + key: &::dafny_runtime::Sequence, + cipher_text: &::dafny_runtime::Sequence, + auth_tag: &::dafny_runtime::Sequence, + iv: &::dafny_runtime::Sequence, + aad: &::dafny_runtime::Sequence, + ) -> Rc<_Wrappers_Compile::Result<::dafny_runtime::Sequence, Rc>> { + let key: Vec = key.iter().collect(); + let cipher_text: Vec = cipher_text.iter().collect(); + let auth_tag: Vec = auth_tag.iter().collect(); + let iv: Vec = iv.iter().collect(); + let aad: Vec = aad.iter().collect(); + + if *self.keyLength() as usize != key.len() { + let msg = format!( + "AESEncrypt : algorithm key length was {} but actual key length was {}.", + self.keyLength(), + key.len() + ); + return dec_result(&msg); + } + + if *self.ivLength() as usize != iv.len() { + let msg = format!( + "AESEncrypt : algorithm nonce length was {} but actual nonce length was {}.", + self.ivLength(), + iv.len() + ); + return dec_result(&msg); + } + + if *self.tagLength() as usize != auth_tag.len() { + let msg = format!( + "AESEncrypt : algorithm auth tag length was {} but actual auth tag length was {}.", + self.tagLength(), + auth_tag.len() + ); + return dec_result(&msg); + } + + match self.do_aes_decrypt(&key, &cipher_text, &auth_tag, &iv, &aad) { + Ok(x) => Rc::new(_Wrappers_Compile::Result::Success { + value: x.iter().cloned().collect(), + }), + Err(e) => { + let msg = format!("AES Decrypt : {}", e); + dec_result(&msg) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_generate() { + let iv: ::dafny_runtime::Sequence = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + .iter() + .cloned() + .collect(); + let key: ::dafny_runtime::Sequence = [ + 2u8, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, + ] + .iter() + .cloned() + .collect(); + let msg: ::dafny_runtime::Sequence = [2u8, 4, 6, 8, 10, 12].iter().cloned().collect(); + let aad: ::dafny_runtime::Sequence = + [3u8, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] + .iter() + .cloned() + .collect(); + + let alg = AES_GCM::AES_GCM { + keyLength: 32, + tagLength: 16, + ivLength: 12, + }; + let cipher = match &*alg.AESEncryptExtern(&iv, &key, &msg, &aad) { + _Wrappers_Compile::Result::Success { value } => value.clone(), + _Wrappers_Compile::Result::Failure { error } => { + panic!("AESEncryptExtern Failed : {:?}", error); + } + }; + + let (cipher_text, auth_tag) = match &*cipher { + AESEncryptOutput::AESEncryptOutput { + cipherText, + authTag, + } => (cipherText, authTag), + }; + + let output = match &*alg.AESDecryptExtern(&key, &cipher_text, &auth_tag, &iv, &aad) { + _Wrappers_Compile::Result::Success { value } => value.clone(), + _Wrappers_Compile::Result::Failure { error } => { + panic!("AESEncryptExtern Failed : {:?}", error); + } + }; + + assert_eq!(output, msg); + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/aes_kdf_ctr.rs b/AwsEncryptionSDK/runtimes/rust/src/aes_kdf_ctr.rs new file mode 100644 index 000000000..e7798d322 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/aes_kdf_ctr.rs @@ -0,0 +1,76 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +#[allow(non_snake_case)] +pub mod AesKdfCtr { + use crate::software::amazon::cryptography::primitives::internaldafny::types::Error as DafnyError; + use crate::*; + use aws_lc_rs::cipher::{EncryptingKey, EncryptionContext, UnboundCipherKey, AES_256}; + use dafny_runtime::Sequence; + use std::rc::Rc; + + #[allow(non_camel_case_types)] + // pub struct _default {} + + fn error(s: &str) -> Rc { + Rc::new(DafnyError::AwsCryptographicPrimitivesError { + message: + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string(s), + }) + } + + fn as_array(nonce: &[u8]) -> &[u8; aws_lc_rs::iv::IV_LEN_128_BIT] { + nonce.try_into().unwrap() + } + + fn ctr_stream(nonce: &[u8], key: &[u8], length: u32) -> Result, String> { + if nonce.len() != aws_lc_rs::iv::IV_LEN_128_BIT { + return Err(format!( + "Nonce length of {} not supported in AesKdfCtrStream. Nonce length must be {}.", + nonce.len(), + aws_lc_rs::iv::IV_LEN_128_BIT + )); + } + + let mut in_out_buffer = vec![0; length as usize]; + + let key = UnboundCipherKey::new(&AES_256, key).map_err(|e| format!("new {:?}", e))?; + let encrypting_key = EncryptingKey::ctr(key).map_err(|e| format!("new {:?}", e))?; + let nonce = aws_lc_rs::iv::FixedLength::<16>::from(as_array(nonce)); + let context = EncryptionContext::Iv128(nonce); + encrypting_key + .less_safe_encrypt(&mut in_out_buffer, context) + .map_err(|e| format!("new {:?}", e))?; + Ok(in_out_buffer) + } + + impl crate::AesKdfCtr::_default { + #[allow(non_snake_case)] + pub fn AesKdfCtrStream( + nonce: &Sequence, + key: &Sequence, + length: u32, + ) -> Rc< + _Wrappers_Compile::Result< + Sequence, + Rc, + >, + > { + let nonce: Vec = nonce.iter().collect(); + let key: Vec = key.iter().collect(); + match ctr_stream(&nonce, &key, length) { + Ok(x) => Rc::new(_Wrappers_Compile::Result::Success { + value: x.iter().cloned().collect(), + }), + Err(e) => { + let msg = format!("Aes Kdf Ctr : {}", e); + Rc::new(_Wrappers_Compile::Result::Failure { error: error(&msg) }) + } + } + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/concurrent_call.rs b/AwsEncryptionSDK/runtimes/rust/src/concurrent_call.rs new file mode 100644 index 000000000..6bafd0c6a --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/concurrent_call.rs @@ -0,0 +1,61 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] +#![allow(dead_code)] + +#[allow(non_snake_case)] +pub mod ConcurrentCall { + + fn de_const( + p: *const dafny_runtime::Object<(dyn Callee + 'static)>, + ) -> *mut dafny_runtime::Object<(dyn Callee + 'static)> { + p as _ + } + + pub struct FakeCallee { + callee: *const dafny_runtime::Object<(dyn Callee + 'static)>, + } + impl FakeCallee { + fn new(callee: &dafny_runtime::Object<(dyn Callee + 'static)>) -> Self { + Self { + callee: std::ptr::from_ref(callee), + } + } + fn call(&self, x: u32, y: u32) { + let mptr = de_const(self.callee); + let value: &mut dafny_runtime::Object<(dyn Callee + 'static)> = unsafe { &mut *mptr }; + value.as_mut().call(x, y); + } + } + unsafe impl Send for FakeCallee {} + + #[allow(nonstandard_style)] + pub struct _default {} + use crate::ConcurrentCall::Callee; + impl _default { + pub fn ConcurrentCall( + callee: &dafny_runtime::Object<(dyn Callee + 'static)>, + serial_iters: u32, + concurrent_iters: u32, + ) { + let mut children = vec![]; + + for i in 0..concurrent_iters { + // Spin up another thread + let fake = FakeCallee::new(callee); + children.push(std::thread::spawn(move || { + for j in 0..serial_iters { + fake.call(j, i); + } + })); + } + + for child in children { + let _ = child.join(); + } + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/dafny_libraries.rs b/AwsEncryptionSDK/runtimes/rust/src/dafny_libraries.rs new file mode 100644 index 000000000..04ab86415 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/dafny_libraries.rs @@ -0,0 +1,150 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +#[allow(non_snake_case)] +pub mod DafnyLibraries { + use dashmap::DashMap; + use std::collections::HashMap; + use std::collections::HashSet; + + pub struct MutableMap { + map: DashMap, + } + + impl MutableMap { + pub fn _allocate_object() -> ::dafny_runtime::Object { + ::dafny_runtime::Object::new(MutableMap { + map: DashMap::new(), + }) + } + } + + impl + crate::DafnyLibraries::MutableMapTrait for MutableMap + { + fn content(&self) -> ::dafny_runtime::Map { + let mut new_map = HashMap::new(); + for entry in self.map.iter() { + new_map.insert(entry.key().clone(), entry.value().clone()); + } + dafny_runtime::Map::from_hashmap_owned(new_map) + } + fn Put(&mut self, k: &K, v: &V) { + self.map.insert(k.clone(), v.clone()); + } + fn Keys(&self) -> ::dafny_runtime::Set { + let mut new_set = HashSet::new(); + for entry in self.map.iter() { + new_set.insert(entry.key().clone()); + } + dafny_runtime::Set::from_hashset_owned(new_set) + } + fn HasKey(&self, k: &K) -> bool { + self.map.contains_key(k) + } + fn Values(&self) -> ::dafny_runtime::Set { + let mut new_set = HashSet::new(); + for entry in self.map.iter() { + new_set.insert(entry.value().clone()); + } + dafny_runtime::Set::from_hashset_owned(new_set) + } + fn Items(&self) -> ::dafny_runtime::Set<(K, V)> { + let mut new_set = HashSet::new(); + for entry in self.map.iter() { + new_set.insert((entry.key().clone(), entry.value().clone())); + } + dafny_runtime::Set::from_hashset_owned(new_set) + } + fn Select(&self, k: &K) -> V { + self.map.get(k).unwrap().clone() + } + fn Remove(&mut self, k: &K) { + self.map.remove(k); + } + fn Size(&self) -> ::dafny_runtime::DafnyInt { + self.map.len().into() + } + } + + pub mod FileIO { + use std::fs::File; + use std::io::Read; + use std::io::Write; + use std::path::Path; + + pub fn INTERNAL_ReadBytesFromFile( + file: &::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + ) -> ( + bool, + ::dafny_runtime::Sequence, + ::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + ) { + let file_name = dafny_runtime::dafny_runtime_conversions::unicode_chars_false::dafny_string_to_string(file); + let path = Path::new(&file_name); + + let mut file = match File::open(path) { + Err(why) => { + let err_msg = format!("couldn't open {} for reading: {}", path.display(), why); + let err_msg = dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string(&err_msg); + return (true, dafny_runtime::Sequence::default(), err_msg); + } + Ok(file) => file, + }; + + let mut result = Vec::new(); + match file.read_to_end(&mut result) { + Err(why) => { + let err_msg = format!("couldn't read from {}: {}", path.display(), why); + let err_msg = dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string(&err_msg); + (true, dafny_runtime::Sequence::default(), err_msg) + } + Ok(_) => ( + false, + dafny_runtime::Sequence::from_array_owned(result), + dafny_runtime::Sequence::default(), + ), + } + } + + pub fn INTERNAL_WriteBytesToFile( + path: &::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + bytes: &::dafny_runtime::Sequence, + ) -> ( + bool, + ::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + ) { + let file_name = dafny_runtime::dafny_runtime_conversions::unicode_chars_false::dafny_string_to_string(path); + let path = Path::new(&file_name); + + let maybe_file = std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(path); + let mut file = match maybe_file { + Err(why) => { + let err_msg = format!("couldn't open {} for writing: {}", path.display(), why); + let err_msg = dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string(&err_msg); + return (true, err_msg); + } + Ok(file) => file, + }; + + let bytes = bytes.to_array(); + match file.write_all(&bytes) { + Err(why) => { + let err_msg = + format!("couldn't write all bytes to {}: {}", path.display(), why); + let err_msg = dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string(&err_msg); + (true, err_msg) + } + Ok(_) => (false, dafny_runtime::Sequence::default()), + } + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/ddb.rs b/AwsEncryptionSDK/runtimes/rust/src/ddb.rs new file mode 100644 index 000000000..2032916b5 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/ddb.rs @@ -0,0 +1,75 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +use aws_config::Region; +use std::sync::LazyLock; + +static DAFNY_TOKIO_RUNTIME: LazyLock = LazyLock::new(|| { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() +}); + +#[allow(non_snake_case)] +impl crate::r#software::amazon::cryptography::services::dynamodb::internaldafny::_default { + pub fn DDBClientForRegion(region: &::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>) -> ::std::rc::Rc< + crate::r#_Wrappers_Compile::Result< + ::dafny_runtime::Object, + ::std::rc::Rc + > + >{ + let region = + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::dafny_string_to_string( + region, + ); + let shared_config = match tokio::runtime::Handle::try_current() { + Ok(curr) => tokio::task::block_in_place(|| { + curr.block_on(async { + aws_config::load_defaults(aws_config::BehaviorVersion::v2024_03_28()).await + }) + }), + Err(_) => DAFNY_TOKIO_RUNTIME.block_on(aws_config::load_defaults( + aws_config::BehaviorVersion::v2024_03_28(), + )), + }; + let shared_config = shared_config + .to_builder() + .region(Region::new(region)) + .build(); + let inner = aws_sdk_dynamodb::Client::new(&shared_config); + let client = crate::deps::com_amazonaws_dynamodb::client::Client { inner }; + let dafny_client = ::dafny_runtime::upcast_object()(::dafny_runtime::object::new(client)); + std::rc::Rc::new(crate::r#_Wrappers_Compile::Result::Success { + value: dafny_client, + }) + } + + pub fn DynamoDBClient() -> ::std::rc::Rc< + crate::r#_Wrappers_Compile::Result< + ::dafny_runtime::Object, + ::std::rc::Rc + > + >{ + let shared_config = match tokio::runtime::Handle::try_current() { + Ok(curr) => tokio::task::block_in_place(|| { + curr.block_on(async { + aws_config::load_defaults(aws_config::BehaviorVersion::v2024_03_28()).await + }) + }), + Err(_) => DAFNY_TOKIO_RUNTIME.block_on(aws_config::load_defaults( + aws_config::BehaviorVersion::v2024_03_28(), + )), + }; + let inner = aws_sdk_dynamodb::Client::new(&shared_config); + let client = crate::deps::com_amazonaws_dynamodb::client::Client { inner }; + let dafny_client = ::dafny_runtime::upcast_object()(::dafny_runtime::object::new(client)); + std::rc::Rc::new(crate::r#_Wrappers_Compile::Result::Success { + value: dafny_client, + }) + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/digest.rs b/AwsEncryptionSDK/runtimes/rust/src/digest.rs new file mode 100644 index 000000000..f3a01ce67 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/digest.rs @@ -0,0 +1,34 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +use crate::*; +use aws_lc_rs::digest; +use software::amazon::cryptography::primitives::internaldafny::types::DigestAlgorithm; + +impl crate::ExternDigest::_default { + #[allow(non_snake_case)] + pub fn Digest( + digest_algorithm: &::std::rc::Rc, + message: &::dafny_runtime::Sequence, + ) -> ::std::rc::Rc< + _Wrappers_Compile::Result< + ::dafny_runtime::Sequence, + ::std::rc::Rc, + >, + > { + let algorithm = match **digest_algorithm { + DigestAlgorithm::SHA_512 {} => &digest::SHA512, + DigestAlgorithm::SHA_384 {} => &digest::SHA384, + DigestAlgorithm::SHA_256 {} => &digest::SHA256, + }; + let message_vec: Vec = message.iter().collect(); + let result = digest::digest(algorithm, &message_vec); + ::std::rc::Rc::new(_Wrappers_Compile::Result::Success { + value: result.as_ref().iter().cloned().collect(), + }) + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/ecdh.rs b/AwsEncryptionSDK/runtimes/rust/src/ecdh.rs new file mode 100644 index 000000000..15e179505 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/ecdh.rs @@ -0,0 +1,513 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] +#![allow(dead_code)] + +#[allow(non_snake_case)] +pub mod ECDH { + use crate::software::amazon::cryptography::primitives::internaldafny::types::Error as DafnyError; + use std::rc::Rc; + + fn error(s: &str) -> Rc { + Rc::new(DafnyError::AwsCryptographicPrimitivesError { + message: + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string(s), + }) + } + + pub mod ECCUtils { + use crate::software::amazon::cryptography::primitives::internaldafny::types::ECDHCurveSpec; + use crate::software::amazon::cryptography::primitives::internaldafny::types::Error as DafnyError; + use crate::*; + use aws_lc_sys; + use std::rc::Rc; + + fn get_nid(x: &ECDHCurveSpec) -> i32 { + match x { + ECDHCurveSpec::ECC_NIST_P256 {} => aws_lc_sys::NID_X9_62_prime256v1, + ECDHCurveSpec::ECC_NIST_P384 {} => aws_lc_sys::NID_secp384r1, + ECDHCurveSpec::ECC_NIST_P521 {} => aws_lc_sys::NID_secp521r1, + ECDHCurveSpec::SM2 {} => panic!("No SM2 in Rust"), + } + } + // NID_secp224r1 (NIST P-224), + // NID_secp256k1 (SEC/ANSI P-256 K1) + + pub(crate) fn get_alg(x: &ECDHCurveSpec) -> &'static aws_lc_rs::agreement::Algorithm { + match x { + ECDHCurveSpec::ECC_NIST_P256 {} => &aws_lc_rs::agreement::ECDH_P256, + ECDHCurveSpec::ECC_NIST_P384 {} => &aws_lc_rs::agreement::ECDH_P384, + ECDHCurveSpec::ECC_NIST_P521 {} => &aws_lc_rs::agreement::ECDH_P521, + ECDHCurveSpec::SM2 {} => panic!("No SM2 in Rust"), + } + } + + use aws_lc_sys::CBB_finish; + use aws_lc_sys::CBB_init; + use aws_lc_sys::EC_GROUP_get_curve_name; + use aws_lc_sys::EC_GROUP_new_by_curve_name; + use aws_lc_sys::EC_KEY_get0_group; + use aws_lc_sys::EC_KEY_get0_public_key; + use aws_lc_sys::EC_KEY_new_by_curve_name; + use aws_lc_sys::EC_KEY_set_public_key; + use aws_lc_sys::EC_POINT_free; + use aws_lc_sys::EC_POINT_new; + use aws_lc_sys::EC_POINT_oct2point; + use aws_lc_sys::EC_POINT_point2oct; + use aws_lc_sys::EVP_PKEY_assign_EC_KEY; + use aws_lc_sys::EVP_PKEY_free; + use aws_lc_sys::EVP_PKEY_get0_EC_KEY; + use aws_lc_sys::EVP_PKEY_new; + use aws_lc_sys::EVP_PKEY_size; + use aws_lc_sys::EVP_marshal_public_key; + use aws_lc_sys::EVP_parse_public_key; + use aws_lc_sys::OPENSSL_free; + use aws_lc_sys::CBB; + use aws_lc_sys::CBS; + use aws_lc_sys::EVP_PKEY_EC; + use std::ptr::null_mut; + + const ELEM_MAX_BITS: usize = 521; + const ELEM_MAX_BYTES: usize = (ELEM_MAX_BITS + 7) / 8; + const PUBLIC_KEY_MAX_LEN: usize = 1 + (2 * ELEM_MAX_BYTES); + + pub(crate) fn X509_to_X962( + public_key: &[u8], + compress: bool, + nid: Option, + ) -> Result, String> { + let mut cbs = CBS { + data: public_key.as_ptr(), + len: public_key.len(), + }; + + let evp_pkey = unsafe { EVP_parse_public_key(&mut cbs) }; + if evp_pkey.is_null() { + return Err("Invalid X509 Public Key.".to_string()); + } + let ec_key = unsafe { EVP_PKEY_get0_EC_KEY(evp_pkey) }; + + let ec_group = unsafe { EC_KEY_get0_group(ec_key) }; + if ec_group.is_null() { + return Err("Error in EC_KEY_get0_group in X509_to_X962.".to_string()); + } + if nid.is_some() && nid.unwrap() != unsafe { EC_GROUP_get_curve_name(ec_group) } { + return Err("Curve type mismatch in X509_to_X962.".to_string()); + } + let ec_point = unsafe { EC_KEY_get0_public_key(ec_key) }; + if ec_point.is_null() { + return Err("Error in EC_KEY_get0_public_key in X509_to_X962.".to_string()); + } + + let comp = if compress { + aws_lc_sys::point_conversion_form_t::POINT_CONVERSION_COMPRESSED + } else { + aws_lc_sys::point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED + }; + + let mut out_buf = [0u8; PUBLIC_KEY_MAX_LEN]; + let new_size = unsafe { + EC_POINT_point2oct( + ec_group, + ec_point, + comp, + out_buf.as_mut_ptr(), + PUBLIC_KEY_MAX_LEN, + null_mut(), + ) + }; + unsafe { EVP_PKEY_free(evp_pkey) }; + Ok(out_buf[..new_size].to_vec()) + } + + pub(crate) fn X962_to_X509( + public_key: &[u8], + alg: &ECDHCurveSpec, + ) -> Result, String> { + let ec_group = unsafe { EC_GROUP_new_by_curve_name(get_nid(alg)) }; + let ec_point = unsafe { EC_POINT_new(ec_group) }; + + if 1 != unsafe { + EC_POINT_oct2point( + ec_group, + ec_point, + public_key.as_ptr(), + public_key.len(), + null_mut(), + ) + } { + return Err("Error in EC_POINT_oct2point.".to_string()); + } + + let ec_key = unsafe { EC_KEY_new_by_curve_name(get_nid(alg)) }; + if 1 != unsafe { EC_KEY_set_public_key(ec_key, ec_point) } { + return Err("Error in EC_KEY_set_public_key.".to_string()); + } + + let evp_pkey = unsafe { EVP_PKEY_new() }; + if 1 != unsafe { EVP_PKEY_assign_EC_KEY(evp_pkey, ec_key) } { + return Err("Error in EVP_PKEY_assign_EC_KEY.".to_string()); + } + + let key_size_bytes: usize = unsafe { EVP_PKEY_size(evp_pkey) }.try_into().unwrap(); + let mut cbb: CBB = Default::default(); + unsafe { CBB_init(&mut cbb as *mut CBB, key_size_bytes * 5) }; + + if 1 != unsafe { EVP_marshal_public_key(&mut cbb, evp_pkey) } { + return Err("Error in EVP_marshal_public_key in GetPublicKey.".to_string()); + }; + + let mut out_data = null_mut::(); + let mut out_len: usize = 0; + + if 1 != unsafe { CBB_finish(&mut cbb, &mut out_data, &mut out_len) } { + return Err("Error in CBB_finish in GetPublicKey.".to_string()); + }; + let slice = unsafe { std::slice::from_raw_parts(out_data, out_len) }; + let slice = slice.to_vec(); + + unsafe { OPENSSL_free(out_data as *mut ::std::os::raw::c_void) }; + unsafe { EVP_PKEY_free(evp_pkey) }; + unsafe { EC_POINT_free(ec_point) }; + Ok(slice) + } + + fn inner_get_public_key( + key_bytes: &[u8], + expected_curve_nid: i32, + ) -> Result, String> { + let mut out = null_mut(); + let evp_pkey = unsafe { + aws_lc_sys::d2i_PrivateKey( + EVP_PKEY_EC, + &mut out, + &mut key_bytes.as_ptr(), + key_bytes + .len() + .try_into() + .map_err(|_| "Key too long".to_string())?, + ) + }; + if evp_pkey.is_null() { + return Err("Error in d2i_PrivateKey in GetPublicKey.".to_string()); + } + + let ec_key = unsafe { EVP_PKEY_get0_EC_KEY(evp_pkey) }; + if ec_key.is_null() { + return Err("Error in EVP_PKEY_get0_EC_KEY in GetPublicKey.".to_string()); + } + let ec_group = unsafe { EC_KEY_get0_group(ec_key) }; + if ec_group.is_null() { + return Err("Error in EC_KEY_get0_group in GetPublicKey.".to_string()); + } + let key_nid = unsafe { EC_GROUP_get_curve_name(ec_group) }; + + if key_nid != expected_curve_nid { + return Err("Wrong Algorithm".to_string()); + } + + let key_size_bytes: usize = unsafe { EVP_PKEY_size(evp_pkey) }.try_into().unwrap(); + let mut cbb: CBB = Default::default(); + unsafe { CBB_init(&mut cbb as *mut CBB, key_size_bytes * 5) }; + + if 1 != unsafe { EVP_marshal_public_key(&mut cbb, evp_pkey) } { + return Err("Error in EVP_marshal_public_key in GetPublicKey.".to_string()); + }; + + let mut out_data = null_mut::(); + let mut out_len: usize = 0; + + if 1 != unsafe { CBB_finish(&mut cbb, &mut out_data, &mut out_len) } { + return Err("Error in CBB_finish in GetPublicKey.".to_string()); + }; + let slice = unsafe { std::slice::from_raw_parts(out_data, out_len) }; + let slice = slice.to_vec(); + + unsafe { OPENSSL_free(out_data as *mut ::std::os::raw::c_void) }; + unsafe { EVP_PKEY_free(evp_pkey) }; + Ok(slice) + } + fn get_public_key(alg: &ECDHCurveSpec, pem: &[u8]) -> Result, String> { + let pem = std::str::from_utf8(pem).map_err(|e| format!("{:?}", e))?; + let private_key = pem::parse(pem).map_err(|e| format!("{:?}", e))?; + inner_get_public_key(private_key.contents(), get_nid(alg)) + } + + fn get_out_of_bounds(curve: &ECDHCurveSpec) -> Vec { + match curve { + ECDHCurveSpec::ECC_NIST_P256 {} => vec![ + 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, + 3, 1, 7, 3, 66, 0, 4, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, + ], + ECDHCurveSpec::ECC_NIST_P384 {} => vec![ + 48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, + 98, 0, 4, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, + ], + ECDHCurveSpec::ECC_NIST_P521 {} => vec![ + 48, 129, 155, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, + 35, 3, 129, 134, 0, 4, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, + ], + ECDHCurveSpec::SM2 {} => vec![], + } + } + pub fn GetOutOfBoundsPublicKey( + curve_algorithm: &Rc, + ) -> Rc<_Wrappers_Compile::Result<::dafny_runtime::Sequence, Rc>> { + let result = get_out_of_bounds(curve_algorithm); + Rc::new(_Wrappers_Compile::Result::Success { + value: result.iter().cloned().collect(), + }) + } + + fn get_infinity(curve: &ECDHCurveSpec) -> Vec { + match curve { + ECDHCurveSpec::ECC_NIST_P256 {} => vec![ + 48, 25, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, + 3, 1, 7, 3, 2, 0, 0, + ], + ECDHCurveSpec::ECC_NIST_P384 {} => vec![ + 48, 22, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, + 2, 0, 0, + ], + ECDHCurveSpec::ECC_NIST_P521 {} => vec![ + 48, 22, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, + 2, 0, 0, + ], + ECDHCurveSpec::SM2 {} => vec![], + } + } + + pub fn GetInfinityPublicKey( + curve_algorithm: &Rc, + ) -> Rc<_Wrappers_Compile::Result<::dafny_runtime::Sequence, Rc>> { + let result = get_infinity(curve_algorithm); + Rc::new(_Wrappers_Compile::Result::Success { + value: result.iter().cloned().collect(), + }) + } + pub fn GetPublicKey( + curve_algorithm: &Rc, + private_key: &Rc, + ) -> Rc<_Wrappers_Compile::Result<::dafny_runtime::Sequence, Rc>> { + let private_key: Vec = private_key.pem().iter().collect(); + match get_public_key(curve_algorithm, &private_key) { + Ok(x) => Rc::new(_Wrappers_Compile::Result::Success { + value: x.iter().cloned().collect(), + }), + Err(e) => { + let msg = format!("ECDH Get Public Key : {}", e); + Rc::new(_Wrappers_Compile::Result::Failure { + error: super::error(&msg), + }) + } + } + } + + // for the moment, it's valid if we can use it to generate a shared secret + fn valid_public_key(alg: &ECDHCurveSpec, public_key: &[u8]) -> Result<(), String> { + X509_to_X962(public_key, false, Some(get_nid(alg)))?; + Ok(()) + } + + pub fn ValidatePublicKey( + curve_algorithm: &Rc, + public_key: &::dafny_runtime::Sequence, + ) -> Rc<_Wrappers_Compile::Result>> { + let public_key: Vec = public_key.iter().collect(); + match valid_public_key(curve_algorithm, &public_key) { + Ok(_) => Rc::new(_Wrappers_Compile::Result::Success { value: true }), + Err(e) => Rc::new(_Wrappers_Compile::Result::Failure { + error: super::error(&e), + }), + } + } + + pub fn CompressPublicKey( + public_key: &::dafny_runtime::Sequence, + _curve_algorithm: &Rc, + ) -> Rc<_Wrappers_Compile::Result<::dafny_runtime::Sequence, Rc>> { + let public_key: Vec = public_key.iter().collect(); + match X509_to_X962(&public_key, true, None) { + Ok(v) => Rc::new(_Wrappers_Compile::Result::Success { + value: v.iter().cloned().collect(), + }), + Err(e) => { + let msg = format!("ECDH Compress Public Key {}", e); + Rc::new(_Wrappers_Compile::Result::Failure { + error: super::error(&msg), + }) + } + } + } + + pub fn DecompressPublicKey( + public_key: &::dafny_runtime::Sequence, + curve_algorithm: &Rc, + ) -> Rc<_Wrappers_Compile::Result<::dafny_runtime::Sequence, Rc>> { + let public_key: Vec = public_key.iter().collect(); + match X962_to_X509(&public_key, curve_algorithm) { + Ok(v) => Rc::new(_Wrappers_Compile::Result::Success { + value: v.iter().cloned().collect(), + }), + Err(e) => { + let msg = format!("ECDH Decompress Public Key {}", e); + Rc::new(_Wrappers_Compile::Result::Failure { + error: super::error(&msg), + }) + } + } + } + + pub fn ParsePublicKey( + publicKey: &::dafny_runtime::Sequence, + ) -> Rc<_Wrappers_Compile::Result<::dafny_runtime::Sequence, Rc>> { + let public_key: Vec = publicKey.iter().collect(); + match X509_to_X962(&public_key, false, None) { + Ok(_) => Rc::new(_Wrappers_Compile::Result::Success { + value: publicKey.clone(), + }), + Err(e) => Rc::new(_Wrappers_Compile::Result::Failure { + error: super::error(&e), + }), + } + } + } + pub mod DeriveSharedSecret { + use crate::software::amazon::cryptography::primitives::internaldafny::types::ECDHCurveSpec; + use crate::software::amazon::cryptography::primitives::internaldafny::types::Error as DafnyError; + use crate::*; + use std::rc::Rc; + + pub fn agree( + curve_algorithm: &ECDHCurveSpec, + private_key_pem: &[u8], + public_key_der: &[u8], + ) -> Result, String> { + let pem = std::str::from_utf8(private_key_pem).map_err(|e| format!("{:?}", e))?; + let private_key = pem::parse(pem).map_err(|e| format!("{:?}", e))?; + let private_key = aws_lc_rs::agreement::PrivateKey::from_private_key_der( + super::ECCUtils::get_alg(curve_algorithm), + private_key.contents(), + ) + .map_err(|e| format!("{:?}", e))?; + let public_key = super::ECCUtils::X509_to_X962(public_key_der, false, None)?; + let public_key = aws_lc_rs::agreement::UnparsedPublicKey::new( + super::ECCUtils::get_alg(curve_algorithm), + &public_key, + ); + let shared: Vec = + aws_lc_rs::agreement::agree(&private_key, &public_key, "foo", |x| Ok(x.to_vec())) + .map_err(|_e| "Failure in aws_lc_rs::agreement::agree.".to_string())?; + Ok(shared) + } + pub fn CalculateSharedSecret( + curve_algorithm: &Rc, + private_key: &Rc, + public_key: &Rc, + ) -> Rc<_Wrappers_Compile::Result<::dafny_runtime::Sequence, Rc>> { + let private_key: Vec = private_key.pem().iter().collect(); + let public_key: Vec = public_key.der().iter().collect(); + match agree(curve_algorithm, &private_key, &public_key) { + Ok(v) => Rc::new(_Wrappers_Compile::Result::Success { + value: v.iter().cloned().collect(), + }), + Err(e) => { + let msg = format!("ECDH Calculate Shared Secret : {}", e); + Rc::new(_Wrappers_Compile::Result::Failure { + error: super::error(&msg), + }) + } + } + } + } + pub mod KeyGeneration { + use crate::software::amazon::cryptography::primitives::internaldafny::types::ECDHCurveSpec; + use crate::software::amazon::cryptography::primitives::internaldafny::types::Error as DafnyError; + use crate::*; + use aws_lc_rs::encoding::AsDer; + use aws_lc_rs::encoding::EcPrivateKeyRfc5915Der; + use std::rc::Rc; + + fn ecdsa_key_gen(alg: &ECDHCurveSpec) -> Result<(Vec, Vec), String> { + let private_key = + aws_lc_rs::agreement::PrivateKey::generate(super::ECCUtils::get_alg(alg)) + .map_err(|e| format!("{:?}", e))?; + + let public_key = private_key + .compute_public_key() + .map_err(|e| format!("{:?}", e))?; + + let public_key: Vec = super::ECCUtils::X962_to_X509(public_key.as_ref(), alg)?; + + let private_key_der = AsDer::::as_der(&private_key) + .map_err(|e| format!("{:?}", e))?; + let private_key = pem::Pem::new("PRIVATE KEY", private_key_der.as_ref()); + let private_key = pem::encode(&private_key); + let private_key: Vec = private_key.into_bytes(); + + Ok((public_key, private_key)) + } + + pub fn GenerateKeyPair( + s: &Rc, + ) -> Rc<_Wrappers_Compile::Result, Rc>> { + match ecdsa_key_gen(s) { + Ok(x) => Rc::new(_Wrappers_Compile::Result::Success { + value: Rc::new(crate::ECDH::EccKeyPair::EccKeyPair { + publicKey: x.0.iter().cloned().collect(), + privateKey: x.1.iter().cloned().collect(), + }), + }), + Err(e) => { + let msg = format!("ECDH Generate Key Pair : {}", e); + Rc::new(_Wrappers_Compile::Result::Failure { + error: super::error(&msg), + }) + } + } + } + } + #[cfg(test)] + mod tests { + use super::*; + use crate::software::amazon::cryptography::primitives::internaldafny::types::ECDHCurveSpec; + use crate::*; + use std::rc::Rc; + + #[test] + fn test_generate() { + let alg = Rc::new(ECDHCurveSpec::ECC_NIST_P256 {}); + + let pair: crate::ECDH::EccKeyPair = match &*KeyGeneration::GenerateKeyPair(&alg) { + _Wrappers_Compile::Result::Success { value } => (**value).clone(), + _Wrappers_Compile::Result::Failure { error } => panic!("{:?}", error), + }; + + match &*ECCUtils::ValidatePublicKey(&alg, pair.publicKey()) { + _Wrappers_Compile::Result::Success { .. } => {} + _Wrappers_Compile::Result::Failure { error } => panic!("{:?}", error), + }; + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/ecdsa.rs b/AwsEncryptionSDK/runtimes/rust/src/ecdsa.rs new file mode 100644 index 000000000..5daf10d8f --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/ecdsa.rs @@ -0,0 +1,276 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +#[allow(non_snake_case)] +pub mod Signature { + pub mod ECDSA { + use crate::software::amazon::cryptography::primitives::internaldafny::types::ECDSASignatureAlgorithm; + use crate::software::amazon::cryptography::primitives::internaldafny::types::Error as DafnyError; + use crate::*; + use aws_lc_rs::encoding::AsDer; + use aws_lc_rs::rand::SystemRandom; + use aws_lc_rs::signature::EcdsaKeyPair; + use aws_lc_rs::signature::EcdsaSigningAlgorithm; + use aws_lc_rs::signature::EcdsaVerificationAlgorithm; + use aws_lc_rs::signature::KeyPair; + use aws_lc_rs::signature::UnparsedPublicKey; + use std::rc::Rc; + + fn error(s: &str) -> Rc { + Rc::new(DafnyError::AwsCryptographicPrimitivesError { + message: + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string(s), + }) + } + + fn get_alg(x: &ECDSASignatureAlgorithm) -> &'static EcdsaSigningAlgorithm { + match x { + ECDSASignatureAlgorithm::ECDSA_P256 {} => { + // &aws_lc_rs::signature::ECDSA_P256_SHA256_FIXED_SIGNING + &aws_lc_rs::signature::ECDSA_P256_SHA256_ASN1_SIGNING + } + ECDSASignatureAlgorithm::ECDSA_P384 {} => { + // &aws_lc_rs::signature::ECDSA_P384_SHA384_FIXED_SIGNING + &aws_lc_rs::signature::ECDSA_P384_SHA384_ASN1_SIGNING + } + } + } + + fn get_ver_alg(x: &ECDSASignatureAlgorithm) -> &'static EcdsaVerificationAlgorithm { + match x { + ECDSASignatureAlgorithm::ECDSA_P256 {} => { + // &aws_lc_rs::signature::ECDSA_P256_SHA256_FIXED + &aws_lc_rs::signature::ECDSA_P256_SHA256_ASN1 + } + ECDSASignatureAlgorithm::ECDSA_P384 {} => { + // &aws_lc_rs::signature::ECDSA_P384_SHA384_FIXED + &aws_lc_rs::signature::ECDSA_P384_SHA384_ASN1 + } + } + } + + fn get_nid(x: &ECDSASignatureAlgorithm) -> i32 { + match x { + ECDSASignatureAlgorithm::ECDSA_P256 {} => aws_lc_sys::NID_X9_62_prime256v1, + ECDSASignatureAlgorithm::ECDSA_P384 {} => aws_lc_sys::NID_secp384r1, + } + } + + const ELEM_MAX_BITS: usize = 521; + const ELEM_MAX_BYTES: usize = (ELEM_MAX_BITS + 7) / 8; + const PUBLIC_KEY_MAX_LEN: usize = 1 + (2 * ELEM_MAX_BYTES); + + pub(crate) fn sec1_compress( + data: &[u8], + alg: &ECDSASignatureAlgorithm, + ) -> Result, String> { + sec1_convert( + data, + get_nid(alg), + aws_lc_sys::point_conversion_form_t::POINT_CONVERSION_COMPRESSED, + ) + } + + pub(crate) fn sec1_convert( + data: &[u8], + nid: i32, + form: aws_lc_sys::point_conversion_form_t, + ) -> Result, String> { + use aws_lc_sys::EC_GROUP_new_by_curve_name; + use aws_lc_sys::EC_POINT_free; + use aws_lc_sys::EC_POINT_new; + use aws_lc_sys::EC_POINT_oct2point; + use aws_lc_sys::EC_POINT_point2oct; + use std::ptr::null_mut; + + // no need to free ec_group + let ec_group = unsafe { EC_GROUP_new_by_curve_name(nid) }; + if ec_group.is_null() { + return Err("EC_GROUP_new_by_curve_name returned failure.".to_string()); + } + + let ec_point = unsafe { EC_POINT_new(ec_group) }; + if ec_point.is_null() { + return Err("EC_POINT_new returned failure.".to_string()); + } + let mut out_buf = [0u8; PUBLIC_KEY_MAX_LEN]; + + let ret = unsafe { + EC_POINT_oct2point(ec_group, ec_point, data.as_ptr(), data.len(), null_mut()) + }; + if ret == 0 { + return Err("EC_POINT_oct2point returned failure.".to_string()); + } + let new_size: usize = unsafe { + EC_POINT_point2oct( + ec_group, + ec_point, + form, + out_buf.as_mut_ptr(), + PUBLIC_KEY_MAX_LEN, + null_mut(), + ) + }; + unsafe { EC_POINT_free(ec_point) }; + Ok(out_buf[..new_size].to_vec()) + } + + fn ecdsa_key_gen(alg: &ECDSASignatureAlgorithm) -> Result<(Vec, Vec), String> { + let pair = EcdsaKeyPair::generate(get_alg(alg)).map_err(|e| format!("{:?}", e))?; + + let public_key: Vec = sec1_compress(pair.public_key().as_ref(), alg)?; + let private_key: Vec = pair.private_key().as_der().unwrap().as_ref().to_vec(); + Ok((public_key, private_key)) + } + + pub fn ExternKeyGen( + alg: &Rc, + ) -> Rc<_Wrappers_Compile::Result, Rc>> + { + match ecdsa_key_gen(alg) { + Ok(x) => Rc::new(_Wrappers_Compile::Result::Success { + value: Rc::new(Signature::SignatureKeyPair::SignatureKeyPair { + verificationKey: x.0.iter().cloned().collect(), + signingKey: x.1.iter().cloned().collect(), + }), + }), + Err(e) => { + let msg = format!("ECDSA Key Gen : {}", e); + Rc::new(_Wrappers_Compile::Result::Failure { error: error(&msg) }) + } + } + } + + fn ecdsa_sign_inner( + alg: &ECDSASignatureAlgorithm, + key: &[u8], + msg: &[u8], + ) -> Result, String> { + let private_key = EcdsaKeyPair::from_private_key_der(get_alg(alg), key) + .map_err(|e| format!("{:?}", e))?; + let rng = SystemRandom::new(); + let sig = private_key + .sign(&rng, msg) + .map_err(|e| format!("{:?}", e))?; + Ok(sig.as_ref().to_vec()) + } + fn ecdsa_sign( + alg: &ECDSASignatureAlgorithm, + key: &[u8], + msg: &[u8], + ) -> Result, String> { + loop { + let result = ecdsa_sign_inner(alg, key, msg)?; + if result.len() == 103 { + return Ok(result); + } + } + } + + pub fn Sign( + alg: &Rc, + key: &::dafny_runtime::Sequence, + msg: &::dafny_runtime::Sequence, + ) -> Rc<_Wrappers_Compile::Result<::dafny_runtime::Sequence, Rc>> { + let key: Vec = key.iter().collect(); + let msg: Vec = msg.iter().collect(); + match ecdsa_sign(alg, &key, &msg) { + Ok(x) => Rc::new(_Wrappers_Compile::Result::Success { + value: x.iter().cloned().collect(), + }), + Err(e) => { + let msg = format!("ECDSA Sign : {}", e); + Rc::new(_Wrappers_Compile::Result::Failure { error: error(&msg) }) + } + } + } + + fn ecdsa_verify( + alg: &ECDSASignatureAlgorithm, + key: &[u8], + msg: &[u8], + sig: &[u8], + ) -> Result { + let public_key = UnparsedPublicKey::new(get_ver_alg(alg), key); + match public_key.verify(msg, sig) { + Ok(_) => Ok(true), + Err(_) => Ok(false), + } + } + + pub fn Verify( + alg: &Rc, + key: &::dafny_runtime::Sequence, + msg: &::dafny_runtime::Sequence, + sig: &::dafny_runtime::Sequence, + ) -> Rc<_Wrappers_Compile::Result>> { + let key: Vec = key.iter().collect(); + let msg: Vec = msg.iter().collect(); + let sig: Vec = sig.iter().collect(); + match ecdsa_verify(alg, &key, &msg, &sig) { + Ok(x) => Rc::new(_Wrappers_Compile::Result::Success { value: x }), + Err(e) => { + let msg = format!("ECDSA Verify : {}", e); + Rc::new(_Wrappers_Compile::Result::Failure { error: error(&msg) }) + } + } + } + #[cfg(test)] + mod tests { + use super::*; + use std::rc::Rc; + + #[test] + fn test_generate() { + let alg = Rc::new(ECDSASignatureAlgorithm::ECDSA_P384 {}); + let key_pair = match &*ExternKeyGen(&alg) { + _Wrappers_Compile::Result::Success { value } => value.clone(), + _Wrappers_Compile::Result::Failure { error } => { + panic!("ExternKeyGen Failed : {:?}", error); + } + }; + + let (s_key, v_key) = match &*key_pair { + Signature::SignatureKeyPair::SignatureKeyPair { + signingKey, + verificationKey, + } => (signingKey, verificationKey), + }; + + let message: ::dafny_runtime::Sequence = + [1u8, 2, 3, 4, 5].iter().cloned().collect(); + + let sig = match &*Sign(&alg, &s_key, &message) { + _Wrappers_Compile::Result::Success { value } => value.clone(), + _Wrappers_Compile::Result::Failure { error } => { + panic!("Sign Failed : {:?}", error); + } + }; + + let ver: bool = match &*Verify(&alg, &v_key, &message, &sig) { + _Wrappers_Compile::Result::Success { value } => value.clone(), + _Wrappers_Compile::Result::Failure { error } => { + panic!("Verify Failed : {:?}", error); + } + }; + assert!(ver); + + let mut sig_vec: Vec = sig.iter().collect(); + sig_vec[0] = 42; + let sig2: ::dafny_runtime::Sequence = sig_vec.iter().cloned().collect(); + assert!(sig != sig2); + let ver2: bool = match &*Verify(&alg, &v_key, &message, &sig2) { + _Wrappers_Compile::Result::Success { value } => value.clone(), + _Wrappers_Compile::Result::Failure { error } => { + panic!("Verify Failed : {:?}", error); + } + }; + assert!(!ver2); + } + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/hmac.rs b/AwsEncryptionSDK/runtimes/rust/src/hmac.rs new file mode 100644 index 000000000..561bbf106 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/hmac.rs @@ -0,0 +1,103 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +use crate::software::amazon::cryptography::primitives::internaldafny::types::DigestAlgorithm; +use crate::*; +use aws_lc_rs::hmac; + +fn convert_algorithm(input: &DigestAlgorithm) -> hmac::Algorithm { + match input { + DigestAlgorithm::SHA_512 {} => hmac::HMAC_SHA512, + DigestAlgorithm::SHA_384 {} => hmac::HMAC_SHA384, + DigestAlgorithm::SHA_256 {} => hmac::HMAC_SHA256, + } +} + +// Let's implement HMAC::_default::Digest +impl crate::HMAC::_default { + #[allow(non_snake_case)] + pub fn Digest( + input: &::std::rc::Rc< + crate::software::amazon::cryptography::primitives::internaldafny::types::HMacInput, + >, + ) -> ::std::rc::Rc< + _Wrappers_Compile::Result< + ::dafny_runtime::Sequence, + ::std::rc::Rc, + >, + > { + let key_vec: Vec = input.key().iter().collect(); + let the_key = hmac::Key::new(convert_algorithm(input.digestAlgorithm()), &key_vec); + let message_vec: Vec = input.message().iter().collect(); + let result = hmac::sign(&the_key, &message_vec); + ::std::rc::Rc::new(_Wrappers_Compile::Result::Success { + value: result.as_ref().iter().cloned().collect(), + }) + } +} + +#[allow(non_snake_case)] +pub mod HMAC { + use crate::*; + use aws_lc_rs::hmac; + #[allow(non_camel_case_types)] + pub struct _default {} + + #[derive(Debug)] + pub struct HMac { + algorithm: hmac::Algorithm, + context: Option, + key: Option, + } + impl HMac { + pub fn Init(&mut self, salt: &::dafny_runtime::Sequence) { + let salt: Vec = salt.iter().collect(); + self.key = Some(hmac::Key::new(self.algorithm, &salt)); + self.context = Some(hmac::Context::with_key(self.key.as_ref().unwrap())); + } + pub fn re_init(&mut self) { + self.context = Some(hmac::Context::with_key(self.key.as_ref().unwrap())); + } + pub fn Build( + input: &::std::rc::Rc< + software::amazon::cryptography::primitives::internaldafny::types::DigestAlgorithm, + >, + ) -> ::std::rc::Rc< + _Wrappers_Compile::Result< + ::dafny_runtime::Object, + ::std::rc::Rc< + software::amazon::cryptography::primitives::internaldafny::types::Error, + >, + >, + > { + let inner = dafny_runtime::Object::new(Self { + algorithm: super::convert_algorithm(input), + context: None, + key: None, + }); + + ::std::rc::Rc::new(_Wrappers_Compile::Result::Success { value: inner }) + } + pub fn BlockUpdate(&mut self, block: &::dafny_runtime::Sequence) { + let part: Vec = block.iter().collect(); + self.context.as_mut().unwrap().update(&part); + } + pub fn GetResult(&mut self) -> ::dafny_runtime::Sequence { + let inner = self.context.take(); + match inner { + Some(x) => { + let tag = x.sign(); + // other languages allow you to call BlockUpdate after GetResult + // so we re-initialize to mimic that behavior + self.re_init(); + tag.as_ref().iter().cloned().collect() + } + None => [].iter().cloned().collect(), + } + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/kms.rs b/AwsEncryptionSDK/runtimes/rust/src/kms.rs new file mode 100644 index 000000000..571a3b3fa --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/kms.rs @@ -0,0 +1,88 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +use aws_config::Region; +use std::sync::LazyLock; + +static DAFNY_TOKIO_RUNTIME: LazyLock = LazyLock::new(|| { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() +}); + +impl crate::r#software::amazon::cryptography::services::kms::internaldafny::_default { + #[allow(non_snake_case)] + pub fn KMSClientForRegion(region: &::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>) -> ::std::rc::Rc, ::std::rc::Rc>>{ + let region = + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::dafny_string_to_string( + region, + ); + + let shared_config = match tokio::runtime::Handle::try_current() { + Ok(curr) => tokio::task::block_in_place(|| { + curr.block_on(async { + aws_config::load_defaults(aws_config::BehaviorVersion::v2024_03_28()).await + }) + }), + Err(_) => DAFNY_TOKIO_RUNTIME.block_on(aws_config::load_defaults( + aws_config::BehaviorVersion::v2024_03_28(), + )), + }; + + let shared_config = shared_config + .to_builder() + .region(Region::new(region)) + .build(); + let inner = aws_sdk_kms::Client::new(&shared_config); + let client = crate::deps::com_amazonaws_kms::client::Client { inner }; + let dafny_client = ::dafny_runtime::upcast_object()(::dafny_runtime::object::new(client)); + std::rc::Rc::new(crate::r#_Wrappers_Compile::Result::Success { + value: dafny_client, + }) + } + + #[allow(non_snake_case)] + pub fn KMSClient() -> ::std::rc::Rc, ::std::rc::Rc>>{ + let shared_config = match tokio::runtime::Handle::try_current() { + Ok(curr) => tokio::task::block_in_place(|| { + curr.block_on(async { + aws_config::load_defaults(aws_config::BehaviorVersion::v2024_03_28()).await + }) + }), + Err(_) => DAFNY_TOKIO_RUNTIME.block_on(aws_config::load_defaults( + aws_config::BehaviorVersion::v2024_03_28(), + )), + }; + + let inner = aws_sdk_kms::Client::new(&shared_config); + let client = crate::deps::com_amazonaws_kms::client::Client { inner }; + let dafny_client = ::dafny_runtime::upcast_object()(::dafny_runtime::object::new(client)); + std::rc::Rc::new(crate::r#_Wrappers_Compile::Result::Success { + value: dafny_client, + }) + } + + #[allow(non_snake_case)] + pub fn RegionMatch( + kmsClient: &::dafny_runtime::Object, + region: &::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + ) -> ::std::rc::Rc> { + let region = + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::dafny_string_to_string( + region, + ); + let any = dafny_runtime::cast_any_object!(kmsClient); + let client = + dafny_runtime::cast_object!(any, crate::deps::com_amazonaws_kms::client::Client); + let flag = match client.as_ref().inner.config().region() { + Some(r) => r.as_ref() == region, + None => false, + }; + ::std::rc::Rc::new(crate::r#_Wrappers_Compile::Option::Some { value: flag }) + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/local_cmc.rs b/AwsEncryptionSDK/runtimes/rust/src/local_cmc.rs new file mode 100644 index 000000000..6163d5ec7 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/local_cmc.rs @@ -0,0 +1,53 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +#[allow(non_snake_case)] +pub mod internal_SynchronizedLocalCMC { + use crate::*; + use std::sync::Mutex; + + pub struct SynchronizedLocalCMC { + wrapped: Mutex<::dafny_runtime::Object<_LocalCMC_Compile::LocalCMC>>, + } + + impl SynchronizedLocalCMC { + pub fn _allocate_object( + cmc: ::dafny_runtime::Object<_LocalCMC_Compile::LocalCMC>, + ) -> ::dafny_runtime::Object { + ::dafny_runtime::Object::new(SynchronizedLocalCMC { + wrapped: Mutex::new(cmc), + }) + } + } + + impl ::dafny_runtime::UpcastObject for SynchronizedLocalCMC { + ::dafny_runtime::UpcastObjectFn!(dyn ::std::any::Any); + } + + impl ::dafny_runtime::UpcastObject + for SynchronizedLocalCMC { + ::dafny_runtime::UpcastObjectFn!(dyn software::amazon::cryptography::materialproviders::internaldafny::types::ICryptographicMaterialsCache); + } + + impl software::amazon::cryptography::materialproviders::internaldafny::types::ICryptographicMaterialsCache for SynchronizedLocalCMC { + fn r#_PutCacheEntry_k(&mut self, input: &std::rc::Rc) -> std::rc::Rc>> { + self.wrapped.lock().unwrap().as_mut()._PutCacheEntry_k(input) + } + + fn r#_UpdateUsageMetadata_k(&mut self, input: &std::rc::Rc) -> std::rc::Rc>> { + self.wrapped.lock().unwrap().as_mut()._UpdateUsageMetadata_k(input) + } + + fn r#_GetCacheEntry_k(&mut self, input: &std::rc::Rc) -> std::rc::Rc, std::rc::Rc>> { + self.wrapped.lock().unwrap().as_mut()._GetCacheEntry_k(input) + } + + fn r#_DeleteCacheEntry_k(&mut self, input: &std::rc::Rc) -> std::rc::Rc>> { + self.wrapped.lock().unwrap().as_mut()._DeleteCacheEntry_k(input) + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/random.rs b/AwsEncryptionSDK/runtimes/rust/src/random.rs new file mode 100644 index 000000000..437a22c69 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/random.rs @@ -0,0 +1,38 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +use crate::*; +use aws_lc_rs::rand; + +impl crate::ExternRandom::_default { + #[allow(non_snake_case)] + pub fn GenerateBytes( + num_bytes: i32, + ) -> ::std::rc::Rc< + _Wrappers_Compile::Result< + ::dafny_runtime::Sequence, + ::std::rc::Rc, + >, + > { + let mut rand_bytes: Vec = vec![0; num_bytes as usize]; + match rand::fill(&mut rand_bytes) { + Ok(_) => { + ::std::rc::Rc::new( + _Wrappers_Compile::Result::Success{value : + dafny_runtime::dafny_runtime_conversions::vec_to_dafny_sequence(&rand_bytes, |x| *x) + } + ) + } + Err(_) => { + std::rc::Rc::new(_Wrappers_Compile::Result::Failure{ error : std::rc::Rc::new( + software::amazon::cryptography::primitives::internaldafny::types::Error::AwsCryptographicPrimitivesError{ + message : dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string("Error generating random bytes.") + })}) + } + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/rsa.rs b/AwsEncryptionSDK/runtimes/rust/src/rsa.rs new file mode 100644 index 000000000..9bb76d9cc --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/rsa.rs @@ -0,0 +1,256 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +// Extern methods with a foreign module name +#[allow(non_snake_case)] +pub mod RSAEncryption { + pub mod RSA { + use crate::_Wrappers_Compile as Wrappers; + use crate::software::amazon::cryptography::primitives::internaldafny::types::RSAPaddingMode; + use crate::*; + use ::std::rc::Rc; + use aws_lc_rs::encoding::{AsDer, Pkcs8V1Der, PublicKeyX509Der}; + + use aws_lc_rs::rsa::KeySize; + use aws_lc_rs::rsa::OaepAlgorithm; + use aws_lc_rs::rsa::OaepPrivateDecryptingKey; + use aws_lc_rs::rsa::OaepPublicEncryptingKey; + use aws_lc_rs::rsa::Pkcs1PrivateDecryptingKey; + use aws_lc_rs::rsa::Pkcs1PublicEncryptingKey; + use aws_lc_rs::rsa::PrivateDecryptingKey; + use aws_lc_rs::rsa::PublicEncryptingKey; + use pem; + use software::amazon::cryptography::primitives::internaldafny::types::Error as DafnyError; + + pub fn key_size_from_length(length: i32) -> KeySize { + match length { + 2048 => KeySize::Rsa2048, + 3072 => KeySize::Rsa3072, + 4096 => KeySize::Rsa4096, + 8192 => KeySize::Rsa8192, + _ => panic!("Bad length for GenerateKeyPair"), + } + } + + fn error(s: &str) -> Rc { + Rc::new(DafnyError::AwsCryptographicPrimitivesError { + message: + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string(s), + }) + } + + fn generate_key_pair(length_bits: i32) -> Result<(Vec, Vec), String> { + // Generate an RSA key. + let private_key = PrivateDecryptingKey::generate(key_size_from_length(length_bits)) + .map_err(|e| format!("{:?}", e))?; + + // Serialize the RSA private key to DER encoded PKCS#8 format for later usage. + let private_key_der = + AsDer::::as_der(&private_key).map_err(|e| format!("{:?}", e))?; + + // Retrieve the RSA public key + let public_key = private_key.public_key(); + + // Serialize the RSA public key to DER encoded X.509 SubjectPublicKeyInfo for later usage. + let public_key_der = + AsDer::::as_der(&public_key).map_err(|e| format!("{:?}", e))?; + + let public_key = pem::Pem::new("RSA PUBLIC KEY", public_key_der.as_ref()); + let public_key = pem::encode(&public_key); + let private_key = pem::Pem::new("RSA PRIVATE KEY", private_key_der.as_ref()); + let private_key = pem::encode(&private_key); + + Ok((public_key.into(), private_key.into())) + } + #[allow(non_snake_case)] + pub fn GenerateKeyPairExtern( + length_bits: i32, + ) -> (::dafny_runtime::Sequence, ::dafny_runtime::Sequence) { + match generate_key_pair(length_bits) { + Ok(x) => (x.0.iter().cloned().collect(), x.1.iter().cloned().collect()), + Err(e) => { + panic!("Unexpected error generating RSA Key Pair{}", e); + } + } + } + + fn get_alg_for_padding(mode: &RSAPaddingMode) -> Result<&'static OaepAlgorithm, String> { + match mode { + RSAPaddingMode::PKCS1 {} => { + Err("No support for RSA with PKCS1 in Rust.".to_string()) + } + RSAPaddingMode::OAEP_SHA1 {} => Ok(&aws_lc_rs::rsa::OAEP_SHA1_MGF1SHA1), + RSAPaddingMode::OAEP_SHA256 {} => Ok(&aws_lc_rs::rsa::OAEP_SHA256_MGF1SHA256), + RSAPaddingMode::OAEP_SHA384 {} => Ok(&aws_lc_rs::rsa::OAEP_SHA384_MGF1SHA384), + RSAPaddingMode::OAEP_SHA512 {} => Ok(&aws_lc_rs::rsa::OAEP_SHA512_MGF1SHA512), + } + } + + fn get_modulus(public_key: &[u8]) -> Result { + let public_key = std::str::from_utf8(public_key).map_err(|e| format!("{:?}", e))?; + let public_key = pem::parse(public_key).map_err(|e| format!("{:?}", e))?; + let public_key = PublicEncryptingKey::from_der(public_key.contents()) + .map_err(|e| format!("{:?}", e))?; + Ok(public_key.key_size_bits() as u32) + } + + #[allow(non_snake_case)] + pub fn GetRSAKeyModulusLengthExtern( + public_key: &::dafny_runtime::Sequence, + ) -> Rc>> { + let public_key: Vec = public_key.iter().collect(); + match get_modulus(&public_key) { + Ok(v) => Rc::new(Wrappers::Result::Success { value: v }), + Err(e) => Rc::new(Wrappers::Result::Failure { error: error(&e) }), + } + } + + fn decrypt_extern( + mode: &RSAPaddingMode, + private_key: &[u8], + cipher_text: &[u8], + ) -> Result, String> { + let private_key = + std::str::from_utf8(private_key).map_err(|e| format!("from_utf8 : {:?}", e))?; + let private_key = + pem::parse(private_key).map_err(|e| format!("pem::parse : {:?}", e))?; + if mode == &(RSAPaddingMode::PKCS1 {}) { + return decrypt_pkcs1(private_key.contents(), cipher_text); + } + let mode = get_alg_for_padding(mode)?; + + let private_key = PrivateDecryptingKey::from_pkcs8(private_key.contents()) + .map_err(|e| format!("from_pkcs8 : {:?}", e))?; + let private_key = + OaepPrivateDecryptingKey::new(private_key).map_err(|e| format!("new : {:?}", e))?; + let mut message: Vec = vec![0; cipher_text.len()]; + let message = private_key + .decrypt(mode, cipher_text, &mut message, None) + .map_err(|e| format!("decrypt {:?}", e))?; + Ok(message.to_vec()) + } + + #[allow(non_snake_case)] + pub fn DecryptExtern( + mode: &RSAPaddingMode, + private_key: &::dafny_runtime::Sequence, + cipher_text: &::dafny_runtime::Sequence, + ) -> Rc, Rc>> { + let private_key: Vec = private_key.iter().collect(); + let cipher_text: Vec = cipher_text.iter().collect(); + match decrypt_extern(mode, &private_key, &cipher_text) { + Ok(x) => Rc::new(Wrappers::Result::Success { + value: x.iter().cloned().collect(), + }), + Err(e) => { + let msg = format!("RSA Decrypt : {}", e); + Rc::new(Wrappers::Result::Failure { error: error(&msg) }) + } + } + } + + fn encrypt_extern( + mode: &RSAPaddingMode, + public_key: &[u8], + message: &[u8], + ) -> Result, String> { + let public_key = std::str::from_utf8(public_key).map_err(|e| format!("{:?}", e))?; + let public_key = pem::parse(public_key).map_err(|e| format!("{:?}", e))?; + if mode == &(RSAPaddingMode::PKCS1 {}) { + return encrypt_pkcs1(public_key.contents(), message); + } + let mode = get_alg_for_padding(mode)?; + + let public_key = PublicEncryptingKey::from_der(public_key.contents()) + .map_err(|e| format!("{:?}", e))?; + let public_key = + OaepPublicEncryptingKey::new(public_key).map_err(|e| format!("{:?}", e))?; + let mut ciphertext: Vec = vec![0; message.len() + public_key.key_size_bytes()]; + let cipher_text = public_key + .encrypt(mode, message, &mut ciphertext, None) + .map_err(|e| format!("{:?}", e))?; + Ok(cipher_text.to_vec()) + } + + #[allow(non_snake_case)] + pub fn EncryptExtern( + mode: &RSAPaddingMode, + public_key: &::dafny_runtime::Sequence, + message: &::dafny_runtime::Sequence, + ) -> Rc, Rc>> { + let public_key: Vec = public_key.iter().collect(); + let message: Vec = message.iter().collect(); + match encrypt_extern(mode, &public_key, &message) { + Ok(x) => Rc::new(Wrappers::Result::Success { + value: x.iter().cloned().collect(), + }), + Err(e) => { + let msg = format!("RSA Encrypt : {}", e); + Rc::new(Wrappers::Result::Failure { error: error(&msg) }) + } + } + } + + pub fn encrypt_pkcs1(public_key: &[u8], plain_text: &[u8]) -> Result, String> { + let public_key = + PublicEncryptingKey::from_der(public_key).map_err(|e| format!("{:?}", e))?; + let public_key = + Pkcs1PublicEncryptingKey::new(public_key).map_err(|e| format!("{:?}", e))?; + let mut ciphertext: Vec = vec![0; plain_text.len() + public_key.key_size_bytes()]; + let cipher_text = public_key + .encrypt(plain_text, &mut ciphertext) + .map_err(|e| format!("{:?}", e))?; + Ok(cipher_text.to_vec()) + } + + pub fn decrypt_pkcs1(private_key: &[u8], cipher_text: &[u8]) -> Result, String> { + let private_key = PrivateDecryptingKey::from_pkcs8(private_key) + .map_err(|e| format!("from_pkcs8 : {:?}", e))?; + let private_key = Pkcs1PrivateDecryptingKey::new(private_key) + .map_err(|e| format!("new : {:?}", e))?; + let mut message: Vec = vec![0; cipher_text.len()]; + let message = private_key + .decrypt(cipher_text, &mut message) + .map_err(|e| format!("decrypt {:?}", e))?; + Ok(message.to_vec()) + } + + #[cfg(test)] + mod tests { + use super::*; + #[test] + fn test_generate() { + let (public_key, private_key) = GenerateKeyPairExtern(2048); + + let modulus: u32 = match &*GetRSAKeyModulusLengthExtern(&public_key) { + Wrappers::Result::Success { value } => *value, + Wrappers::Result::Failure { error } => panic!("{:?}", error), + }; + assert_eq!(modulus, 2048); + + let mode = RSAPaddingMode::OAEP_SHA256 {}; + + let plain_text: ::dafny_runtime::Sequence = + [1u8, 2, 3, 4, 5].iter().cloned().collect(); + + let cipher: ::dafny_runtime::Sequence = + match &*EncryptExtern(&mode, &public_key, &plain_text) { + Wrappers::Result::Success { value } => value.clone(), + Wrappers::Result::Failure { error } => panic!("{:?}", error), + }; + + let message: ::dafny_runtime::Sequence = + match &*DecryptExtern(&mode, &private_key, &cipher) { + Wrappers::Result::Success { value } => value.clone(), + Wrappers::Result::Failure { error } => panic!("{:?}", error), + }; + + assert_eq!(plain_text, message); + } + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/sets.rs b/AwsEncryptionSDK/runtimes/rust/src/sets.rs new file mode 100644 index 000000000..755615ddb --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/sets.rs @@ -0,0 +1,63 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +#[allow(non_snake_case)] +#[allow(clippy::type_complexity)] +pub mod SortedSets { + use std::cmp::Ordering; + + #[allow(non_camel_case_types)] + pub struct _default {} + impl _default { + pub fn SetToSequence( + elems: &::dafny_runtime::Set, + ) -> ::dafny_runtime::Sequence { + elems.iter().cloned().collect() + } + + pub fn SetToOrderedSequence( + elems: &::dafny_runtime::Set<::dafny_runtime::Sequence>, + less: &::std::rc::Rc bool>, + ) -> ::dafny_runtime::Sequence<::dafny_runtime::Sequence> { + let mut vec = elems.iter().cloned().collect::>(); + vec.sort_by(|a, b| Self::order(a, b, less)); + dafny_runtime::dafny_runtime_conversions::vec_to_dafny_sequence(&vec, |x| x.clone()) + } + + pub fn SetToOrderedSequence2( + elems: &::dafny_runtime::Set<::dafny_runtime::Sequence>, + less: &::std::rc::Rc bool>, + ) -> ::dafny_runtime::Sequence<::dafny_runtime::Sequence> { + Self::SetToOrderedSequence(elems, less) + } + + fn order( + x: &::dafny_runtime::Sequence, + y: &::dafny_runtime::Sequence, + less: &::std::rc::Rc bool>, + ) -> Ordering { + let mut iter1 = x.iter(); + let mut iter2 = y.iter(); + + loop { + match (iter1.next(), iter2.next()) { + (Some(lhs), Some(rhs)) => { + if less(&lhs, &rhs) { + return Ordering::Less; + } + if less(&rhs, &lhs) { + return Ordering::Greater; + } + } + (Some(_), None) => return Ordering::Greater, + (None, Some(_)) => return Ordering::Less, + (None, None) => return Ordering::Equal, + } + } + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/software_externs.rs b/AwsEncryptionSDK/runtimes/rust/src/software_externs.rs new file mode 100644 index 000000000..5ff57e57f --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/software_externs.rs @@ -0,0 +1,22 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] +#![allow(non_snake_case)] + +pub mod software { + pub mod amazon { + pub mod cryptography { + pub mod internaldafny { + pub mod StormTrackingCMC { + pub use crate::storm_tracker::internal_StormTrackingCMC::*; + } + pub mod SynchronizedLocalCMC { + pub use crate::local_cmc::internal_SynchronizedLocalCMC::*; + } + } + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/storm_tracker.rs b/AwsEncryptionSDK/runtimes/rust/src/storm_tracker.rs new file mode 100644 index 000000000..84576a736 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/storm_tracker.rs @@ -0,0 +1,80 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +#[allow(non_snake_case)] +pub mod internal_StormTrackingCMC { + use crate::_StormTracker_Compile::CacheState::*; + use crate::*; + use std::sync::Mutex; + use std::time::Duration; + + pub struct StormTrackingCMC { + wrapped: Mutex<::dafny_runtime::Object<_StormTracker_Compile::StormTracker>>, + } + impl StormTrackingCMC { + pub fn _allocate_object( + cmc: ::dafny_runtime::Object<_StormTracker_Compile::StormTracker>, + ) -> ::dafny_runtime::Object { + ::dafny_runtime::Object::new(StormTrackingCMC { + wrapped: Mutex::new(cmc), + }) + } + } + + impl ::dafny_runtime::UpcastObject for StormTrackingCMC { + ::dafny_runtime::UpcastObjectFn!(dyn ::std::any::Any); + } + + impl ::dafny_runtime::UpcastObject + for StormTrackingCMC { + ::dafny_runtime::UpcastObjectFn!(dyn software::amazon::cryptography::materialproviders::internaldafny::types::ICryptographicMaterialsCache); +} + + impl crate::software::amazon::cryptography::materialproviders::internaldafny::types::ICryptographicMaterialsCache for StormTrackingCMC { + fn r#_PutCacheEntry_k(&mut self, input: &std::rc::Rc) + -> std::rc::Rc>> + { + self.wrapped.lock().unwrap().as_mut().PutCacheEntry(input) + } + + fn r#_UpdateUsageMetadata_k(&mut self, input: &std::rc::Rc) + -> std::rc::Rc>> + { + self.wrapped.lock().unwrap().as_mut().UpdateUsageMetadata(input) + } + + fn r#_GetCacheEntry_k(&mut self, input: &std::rc::Rc) + -> std::rc::Rc, std::rc::Rc>> + { + loop { + let result = self.wrapped.lock().unwrap().as_mut().GetFromCache(input); + match &*result { + crate::_Wrappers_Compile::Result::Failure{error} => {return std::rc::Rc::new(crate::_Wrappers_Compile::Result::Failure{error : error.clone()});} + crate::_Wrappers_Compile::Result::Success{value} => { + match &**value { + Full { data } => { return std::rc::Rc::new(crate::_Wrappers_Compile::Result::Success{value : data.clone()}); } + EmptyFetch {} => { + return std::rc::Rc::new(crate::_Wrappers_Compile::Result::Failure{error : + std::rc::Rc::new(crate::software::amazon::cryptography::materialproviders::internaldafny::types::Error::EntryDoesNotExist { message: + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string( + "Entry does not exist" + ) + }, + )}); + } + EmptyWait {} => { std::thread::sleep(Duration::from_micros(50)); } + } + } + } + } + } + + fn r#_DeleteCacheEntry_k(&mut self, input: &std::rc::Rc) -> std::rc::Rc>> { + self.wrapped.lock().unwrap().as_mut().DeleteCacheEntry(input) + } + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/time.rs b/AwsEncryptionSDK/runtimes/rust/src/time.rs new file mode 100644 index 000000000..8e4c56292 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/time.rs @@ -0,0 +1,35 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +use crate::*; +use std::time::SystemTime; + +impl crate::Time::_default { + #[allow(non_snake_case)] + pub fn CurrentRelativeTime() -> i64 { + match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { + Ok(n) => n.as_secs() as i64, + Err(_) => 0, + } + } + + #[allow(non_snake_case)] + pub fn GetCurrentTimeStamp() -> ::std::rc::Rc< + _Wrappers_Compile::Result< + ::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + ::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + >, + > { + let now_utc = chrono::Utc::now(); + let formatted = format!("{}", now_utc.format("%Y-%m-%dT%H:%M:%S%.fZ")); + ::std::rc::Rc::new( + _Wrappers_Compile::Result::Success{value : + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string(&formatted) + } + ) + } +} diff --git a/AwsEncryptionSDK/runtimes/rust/src/uuid.rs b/AwsEncryptionSDK/runtimes/rust/src/uuid.rs new file mode 100644 index 000000000..700b2b1d2 --- /dev/null +++ b/AwsEncryptionSDK/runtimes/rust/src/uuid.rs @@ -0,0 +1,76 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#![deny(warnings, unconditional_panic)] +#![deny(nonstandard_style)] +#![deny(clippy::all)] + +use crate::*; +use ::uuid::Uuid; + +impl crate::UUID::_default { + #[allow(non_snake_case)] + pub fn ToByteArray( + bytes: &::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + ) -> ::std::rc::Rc< + _Wrappers_Compile::Result< + ::dafny_runtime::Sequence, + ::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + >, + > { + let my_str = + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::dafny_string_to_string( + bytes, + ); + match Uuid::parse_str(&my_str) { + Ok(u) => { + let b = u.as_bytes(); + std::rc::Rc::new(_Wrappers_Compile::Result::Success { value : + b.iter().cloned().collect() + }) + } + Err(e) => { + std::rc::Rc::new(_Wrappers_Compile::Result::Failure{ error : + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string( + &format!("{my_str} is not a valid UUID ({e}).")) + }) + } + } + } + + #[allow(non_snake_case)] + pub fn FromByteArray( + bytes: &::dafny_runtime::Sequence, + ) -> ::std::rc::Rc< + _Wrappers_Compile::Result< + ::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + ::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + >, + > { + let vec: Vec = bytes.iter().collect(); + if vec.len() != 16 { + return std::rc::Rc::new(_Wrappers_Compile::Result::Failure{ error : + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string("Not 16 bytes of input to FromByteArray.") + }); + } + let bytes: ::uuid::Bytes = vec[..16].try_into().unwrap(); + let uuid = Uuid::from_bytes_ref(&bytes); + let my_str = uuid.to_string(); + std::rc::Rc::new(_Wrappers_Compile::Result::Success { value : + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string(&my_str) + }) + } + + #[allow(non_snake_case)] + pub fn GenerateUUID() -> ::std::rc::Rc< + _Wrappers_Compile::Result< + ::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + ::dafny_runtime::Sequence<::dafny_runtime::DafnyCharUTF16>, + >, + > { + let my_str = Uuid::new_v4().to_string(); + std::rc::Rc::new(_Wrappers_Compile::Result::Success { value : + dafny_runtime::dafny_runtime_conversions::unicode_chars_false::string_to_dafny_string(&my_str) + }) + } +} diff --git a/smithy-dafny b/smithy-dafny index 98939e130..cb609e7ec 160000 --- a/smithy-dafny +++ b/smithy-dafny @@ -1 +1 @@ -Subproject commit 98939e130695095386059967509a19299dfac320 +Subproject commit cb609e7ecf53c26e795cc4b5ccd7311093b7c041