// // This file was automatically generated by witx-codegen - Do not edit manually. // #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Error { WasiError(i32), } impl std::error::Error for Error {} impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Error::WasiError(e) => write!(f, "Wasi error {}", e), } } } pub type WasiHandle = i32; pub type Char8 = u8; pub type Char32 = u32; pub type WasiPtr = *const T; pub type WasiMutPtr = *mut T; pub type WasiStringBytesPtr = WasiPtr; #[repr(C)] #[derive(Copy, Clone, Debug)] pub struct WasiSlice { ptr: WasiPtr, len: usize, } #[repr(C)] #[derive(Copy, Clone, Debug)] pub struct WasiMutSlice { ptr: WasiMutPtr, len: usize, } impl WasiSlice { pub fn as_slice(&self) -> &[T] { unsafe { std::slice::from_raw_parts(self.ptr, self.len) } } pub fn from_slice(&self, slice: &[T]) -> Self { WasiSlice { ptr: slice.as_ptr() as _, len: slice.len(), } } } impl WasiMutSlice { pub fn as_slice(&self) -> &[T] { unsafe { std::slice::from_raw_parts(self.ptr, self.len) } } pub fn as_mut_slice(&self) -> &mut [T] { unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) } } pub fn from_slice(&self, slice: &[T]) -> Self { WasiMutSlice { ptr: slice.as_ptr() as _, len: slice.len(), } } pub fn from_mut_slice(&self, slice: &mut [T]) -> Self { WasiMutSlice { ptr: slice.as_mut_ptr(), len: slice.len(), } } } #[repr(C)] #[derive(Copy, Clone, Debug)] pub struct WasiString { ptr: WasiStringBytesPtr, len: usize, } impl> From for WasiString { fn from(s: T) -> Self { let s = s.as_ref(); WasiString { ptr: s.as_ptr() as _, len: s.len(), } } } impl WasiString { pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> { std::str::from_utf8(unsafe { std::slice::from_raw_parts(self.ptr, self.len) }) } pub fn as_slice(&self) -> &[u8] { unsafe { std::slice::from_raw_parts(self.ptr, self.len) } } pub fn from_slice(&self, slice: &[u8]) -> Self { WasiString { ptr: slice.as_ptr() as _, len: slice.len(), } } } // ---------------------- Module: [wasi_ephemeral_crypto_symmetric] ---------------------- /// Error codes. pub type CryptoErrno = u16; #[allow(non_snake_case)] pub mod CRYPTO_ERRNO { use super::CryptoErrno; pub const SUCCESS: CryptoErrno = 0; pub const GUEST_ERROR: CryptoErrno = 1; pub const NOT_IMPLEMENTED: CryptoErrno = 2; pub const UNSUPPORTED_FEATURE: CryptoErrno = 3; pub const PROHIBITED_OPERATION: CryptoErrno = 4; pub const UNSUPPORTED_ENCODING: CryptoErrno = 5; pub const UNSUPPORTED_ALGORITHM: CryptoErrno = 6; pub const UNSUPPORTED_OPTION: CryptoErrno = 7; pub const INVALID_KEY: CryptoErrno = 8; pub const INVALID_LENGTH: CryptoErrno = 9; pub const VERIFICATION_FAILED: CryptoErrno = 10; pub const RNG_ERROR: CryptoErrno = 11; pub const ALGORITHM_FAILURE: CryptoErrno = 12; pub const INVALID_SIGNATURE: CryptoErrno = 13; pub const CLOSED: CryptoErrno = 14; pub const INVALID_HANDLE: CryptoErrno = 15; pub const OVERFLOW: CryptoErrno = 16; pub const INTERNAL_ERROR: CryptoErrno = 17; pub const TOO_MANY_HANDLES: CryptoErrno = 18; pub const KEY_NOT_SUPPORTED: CryptoErrno = 19; pub const KEY_REQUIRED: CryptoErrno = 20; pub const INVALID_TAG: CryptoErrno = 21; pub const INVALID_OPERATION: CryptoErrno = 22; pub const NONCE_REQUIRED: CryptoErrno = 23; pub const INVALID_NONCE: CryptoErrno = 24; pub const OPTION_NOT_SET: CryptoErrno = 25; pub const NOT_FOUND: CryptoErrno = 26; pub const PARAMETERS_MISSING: CryptoErrno = 27; pub const IN_PROGRESS: CryptoErrno = 28; pub const INCOMPATIBLE_KEYS: CryptoErrno = 29; pub const EXPIRED: CryptoErrno = 30; } /// Encoding to use for importing or exporting a key pair. pub type KeypairEncoding = u16; #[allow(non_snake_case)] pub mod KEYPAIR_ENCODING { use super::KeypairEncoding; pub const RAW: KeypairEncoding = 0; pub const PKCS_8: KeypairEncoding = 1; pub const PEM: KeypairEncoding = 2; pub const LOCAL: KeypairEncoding = 3; } /// Encoding to use for importing or exporting a public key. pub type PublickeyEncoding = u16; #[allow(non_snake_case)] pub mod PUBLICKEY_ENCODING { use super::PublickeyEncoding; pub const RAW: PublickeyEncoding = 0; pub const PKCS_8: PublickeyEncoding = 1; pub const PEM: PublickeyEncoding = 2; pub const SEC: PublickeyEncoding = 3; pub const COMPRESSED_SEC: PublickeyEncoding = 4; pub const LOCAL: PublickeyEncoding = 5; } /// Encoding to use for importing or exporting a secret key. pub type SecretkeyEncoding = u16; #[allow(non_snake_case)] pub mod SECRETKEY_ENCODING { use super::SecretkeyEncoding; pub const RAW: SecretkeyEncoding = 0; pub const PKCS_8: SecretkeyEncoding = 1; pub const PEM: SecretkeyEncoding = 2; pub const SEC: SecretkeyEncoding = 3; pub const COMPRESSED_SEC: SecretkeyEncoding = 4; pub const LOCAL: SecretkeyEncoding = 5; } /// Encoding to use for importing or exporting a signature. pub type SignatureEncoding = u16; #[allow(non_snake_case)] pub mod SIGNATURE_ENCODING { use super::SignatureEncoding; pub const RAW: SignatureEncoding = 0; pub const DER: SignatureEncoding = 1; } /// An algorithm category. pub type AlgorithmType = u16; #[allow(non_snake_case)] pub mod ALGORITHM_TYPE { use super::AlgorithmType; pub const SIGNATURES: AlgorithmType = 0; pub const SYMMETRIC: AlgorithmType = 1; pub const KEY_EXCHANGE: AlgorithmType = 2; } /// Version of a managed key. /// /// A version can be an arbitrary `u64` integer, with the expection of some reserved values. pub type Version = u64; /// Size of a value. pub type Size = usize; /// A UNIX timestamp, in seconds since 01/01/1970. pub type Timestamp = u64; /// A 64-bit value pub type U64 = u64; /// Handle for functions returning output whose size may be large or not known in advance. /// /// An `array_output` object contains a host-allocated byte array. /// /// A guest can get the size of that array after a function returns in order to then allocate a buffer of the correct size. /// In addition, the content of such an object can be consumed by a guest in a streaming fashion. /// /// An `array_output` handle is automatically closed after its full content has been consumed. pub type ArrayOutput = WasiHandle; /// A set of options. /// /// This type is used to set non-default parameters. /// /// The exact set of allowed options depends on the algorithm being used. pub type Options = WasiHandle; /// A handle to the optional secrets management facilities offered by a host. /// /// This is used to generate, retrieve and invalidate managed keys. pub type SecretsManager = WasiHandle; /// A key pair. pub type Keypair = WasiHandle; /// A state to absorb data to be signed. /// /// After a signature has been computed or verified, the state remains valid for further operations. /// /// A subsequent signature would sign all the data accumulated since the creation of the state object. pub type SignatureState = WasiHandle; /// A signature. pub type Signature = WasiHandle; /// A public key, for key exchange and signature verification. pub type Publickey = WasiHandle; /// A secret key, for key exchange mechanisms. pub type Secretkey = WasiHandle; /// A state to absorb signed data to be verified. pub type SignatureVerificationState = WasiHandle; /// A state to perform symmetric operations. /// /// The state is not reset nor invalidated after an option has been performed. /// Incremental updates and sessions are thus supported. pub type SymmetricState = WasiHandle; /// A symmetric key. /// /// The key can be imported from raw bytes, or can be a reference to a managed key. /// /// If it was imported, the host will wipe it from memory as soon as the handle is closed. pub type SymmetricKey = WasiHandle; /// An authentication tag. /// /// This is an object returned by functions computing authentication tags. /// /// A tag can be compared against another tag (directly supplied as raw bytes) in constant time with the `symmetric_tag_verify()` function. /// /// This object type can't be directly created from raw bytes. They are only returned by functions computing MACs. /// /// The host is reponsible for securely wiping them from memory on close. pub type SymmetricTag = WasiHandle; /// Options index, only required by the Interface Types translation layer. pub type OptOptionsU = u8; #[allow(non_snake_case)] pub mod OPT_OPTIONS_U { use super::OptOptionsU; pub const SOME: OptOptionsU = 0; pub const NONE: OptOptionsU = 1; } /// An optional options set. /// /// This union simulates an `Option` type to make the `options` parameter of some functions optional. #[repr(C)] pub union OptOptionsMember { some: Options, // if tag=0 // none with no associated value if tag=1 } #[repr(C, packed)] pub struct OptOptions { pub tag: u8, __pad8_0: u8, __pad16_0: u16, __pad32_0: u32, pub member: std::mem::MaybeUninit, } impl OptOptions { fn new(tag: u8) -> Self { let mut tu = unsafe { std::mem::zeroed::() }; tu.tag = tag; tu } // --- some: Options if tag=0 pub fn new_some(val: Options) -> Self { let mut tu = Self::new(0); tu.member = std::mem::MaybeUninit::new(OptOptionsMember { some: val }); tu } pub fn into_some(self) -> Options { assert_eq!(self.tag, 0); unsafe { self.member.assume_init().some } } pub fn set_some(&mut self, val: Options) { assert_eq!(self.tag, 0); let uval = OptOptionsMember { some: val }; unsafe { *self.member.as_mut_ptr() = uval }; } pub fn is_some(&self) -> bool { self.tag == 0 } // --- none: (no associated content) if tag=1 pub fn new_none() -> Self { Self::new(1) } pub fn is_none(&self) -> bool { self.tag == 1 } } /// Symmetric key index, only required by the Interface Types translation layer. pub type OptSymmetricKeyU = u8; #[allow(non_snake_case)] pub mod OPT_SYMMETRIC_KEY_U { use super::OptSymmetricKeyU; pub const SOME: OptSymmetricKeyU = 0; pub const NONE: OptSymmetricKeyU = 1; } /// An optional symmetric key. /// /// This union simulates an `Option` type to make the `symmetric_key` parameter of some functions optional. #[repr(C)] pub union OptSymmetricKeyMember { some: SymmetricKey, // if tag=0 // none with no associated value if tag=1 } #[repr(C, packed)] pub struct OptSymmetricKey { pub tag: u8, __pad8_0: u8, __pad16_0: u16, __pad32_0: u32, pub member: std::mem::MaybeUninit, } impl OptSymmetricKey { fn new(tag: u8) -> Self { let mut tu = unsafe { std::mem::zeroed::() }; tu.tag = tag; tu } // --- some: SymmetricKey if tag=0 pub fn new_some(val: SymmetricKey) -> Self { let mut tu = Self::new(0); tu.member = std::mem::MaybeUninit::new(OptSymmetricKeyMember { some: val }); tu } pub fn into_some(self) -> SymmetricKey { assert_eq!(self.tag, 0); unsafe { self.member.assume_init().some } } pub fn set_some(&mut self, val: SymmetricKey) { assert_eq!(self.tag, 0); let uval = OptSymmetricKeyMember { some: val }; unsafe { *self.member.as_mut_ptr() = uval }; } pub fn is_some(&self) -> bool { self.tag == 0 } // --- none: (no associated content) if tag=1 pub fn new_none() -> Self { Self::new(1) } pub fn is_none(&self) -> bool { self.tag == 1 } } /// Generate a new symmetric key for a given algorithm. /// /// `options` can be `None` to use the default parameters, or an algoritm-specific set of parameters to override. /// /// This function may return `unsupported_feature` if key generation is not supported by the host for the chosen algorithm, or `unsupported_algorithm` if the algorithm is not supported by the host. pub fn symmetric_key_generate( algorithm_ptr: WasiPtr, algorithm_len: usize, options: OptOptions, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_key_generate( algorithm_ptr: WasiPtr, algorithm_len: usize, options: OptOptions, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_key_generate( algorithm_ptr, algorithm_len, options, result_ptr.as_mut_ptr(), ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Create a symmetric key from raw material. /// /// The algorithm is internally stored along with the key, and trying to use the key with an operation expecting a different algorithm will return `invalid_key`. /// /// The function may also return `unsupported_algorithm` if the algorithm is not supported by the host. pub fn symmetric_key_import( algorithm_ptr: WasiPtr, algorithm_len: usize, raw: WasiPtr, raw_len: Size, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_key_import( algorithm_ptr: WasiPtr, algorithm_len: usize, raw: WasiPtr, raw_len: Size, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_key_import( algorithm_ptr, algorithm_len, raw, raw_len, result_ptr.as_mut_ptr(), ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Export a symmetric key as raw material. /// /// This is mainly useful to export a managed key. /// /// May return `prohibited_operation` if this operation is denied. pub fn symmetric_key_export(symmetric_key: SymmetricKey) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_key_export( symmetric_key: SymmetricKey, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_key_export(symmetric_key, result_ptr.as_mut_ptr()) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Destroy a symmetric key. /// /// Objects are reference counted. It is safe to close an object immediately after the last function needing it is called. pub fn symmetric_key_close(symmetric_key: SymmetricKey) -> Result<(), Error> { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_key_close(symmetric_key: SymmetricKey) -> CryptoErrno; } let res = unsafe { symmetric_key_close(symmetric_key) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(()) } /// __(optional)__ /// Generate a new managed symmetric key. /// /// The key is generated and stored by the secrets management facilities. /// /// It may be used through its identifier, but the host may not allow it to be exported. /// /// The function returns the `unsupported_feature` error code if secrets management facilities are not supported by the host, /// or `unsupported_algorithm` if a key cannot be created for the chosen algorithm. /// /// The function may also return `unsupported_algorithm` if the algorithm is not supported by the host. /// /// This is also an optional import, meaning that the function may not even exist. pub fn symmetric_key_generate_managed( secrets_manager: SecretsManager, algorithm_ptr: WasiPtr, algorithm_len: usize, options: OptOptions, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_key_generate_managed( secrets_manager: SecretsManager, algorithm_ptr: WasiPtr, algorithm_len: usize, options: OptOptions, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_key_generate_managed( secrets_manager, algorithm_ptr, algorithm_len, options, result_ptr.as_mut_ptr(), ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// __(optional)__ /// Store a symmetric key into the secrets manager. /// /// On success, the function stores the key identifier into `$symmetric_key_id`, /// into which up to `$symmetric_key_id_max_len` can be written. /// /// The function returns `overflow` if the supplied buffer is too small. pub fn symmetric_key_store_managed( secrets_manager: SecretsManager, symmetric_key: SymmetricKey, symmetric_key_id: WasiMutPtr, symmetric_key_id_max_len: Size, ) -> Result<(), Error> { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_key_store_managed( secrets_manager: SecretsManager, symmetric_key: SymmetricKey, symmetric_key_id: WasiMutPtr, symmetric_key_id_max_len: Size, ) -> CryptoErrno; } let res = unsafe { symmetric_key_store_managed( secrets_manager, symmetric_key, symmetric_key_id, symmetric_key_id_max_len, ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(()) } /// __(optional)__ /// Replace a managed symmetric key. /// /// This function crates a new version of a managed symmetric key, by replacing `$kp_old` with `$kp_new`. /// /// It does several things: /// /// - The key identifier for `$symmetric_key_new` is set to the one of `$symmetric_key_old`. /// - A new, unique version identifier is assigned to `$kp_new`. This version will be equivalent to using `$version_latest` until the key is replaced. /// - The `$symmetric_key_old` handle is closed. /// /// Both keys must share the same algorithm and have compatible parameters. If this is not the case, `incompatible_keys` is returned. /// /// The function may also return the `unsupported_feature` error code if secrets management facilities are not supported by the host, /// or if keys cannot be rotated. /// /// Finally, `prohibited_operation` can be returned if `$symmetric_key_new` wasn't created by the secrets manager, and the secrets manager prohibits imported keys. /// /// If the operation succeeded, the new version is returned. /// /// This is an optional import, meaning that the function may not even exist. pub fn symmetric_key_replace_managed( secrets_manager: SecretsManager, symmetric_key_old: SymmetricKey, symmetric_key_new: SymmetricKey, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_key_replace_managed( secrets_manager: SecretsManager, symmetric_key_old: SymmetricKey, symmetric_key_new: SymmetricKey, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_key_replace_managed( secrets_manager, symmetric_key_old, symmetric_key_new, result_ptr.as_mut_ptr(), ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// __(optional)__ /// Return the key identifier and version of a managed symmetric key. /// /// If the key is not managed, `unsupported_feature` is returned instead. /// /// This is an optional import, meaning that the function may not even exist. pub fn symmetric_key_id( symmetric_key: SymmetricKey, symmetric_key_id: WasiMutPtr, symmetric_key_id_max_len: Size, ) -> Result<(Size, Version), Error> { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_key_id( symmetric_key: SymmetricKey, symmetric_key_id: WasiMutPtr, symmetric_key_id_max_len: Size, result_0_ptr: WasiMutPtr, result_1_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_0_ptr = std::mem::MaybeUninit::uninit(); let mut result_1_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_key_id( symmetric_key, symmetric_key_id, symmetric_key_id_max_len, result_0_ptr.as_mut_ptr(), result_1_ptr.as_mut_ptr(), ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { (result_0_ptr.assume_init(), result_1_ptr.assume_init()) }) } /// __(optional)__ /// Return a managed symmetric key from a key identifier. /// /// `kp_version` can be set to `version_latest` to retrieve the most recent version of a symmetric key. /// /// If no key matching the provided information is found, `not_found` is returned instead. /// /// This is an optional import, meaning that the function may not even exist. pub fn symmetric_key_from_id( secrets_manager: SecretsManager, symmetric_key_id: WasiPtr, symmetric_key_id_len: Size, symmetric_key_version: Version, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_key_from_id( secrets_manager: SecretsManager, symmetric_key_id: WasiPtr, symmetric_key_id_len: Size, symmetric_key_version: Version, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_key_from_id( secrets_manager, symmetric_key_id, symmetric_key_id_len, symmetric_key_version, result_ptr.as_mut_ptr(), ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Create a new state to aborb and produce data using symmetric operations. /// /// The state remains valid after every operation in order to support incremental updates. /// /// The function has two optional parameters: a key and an options set. /// /// It will fail with a `key_not_supported` error code if a key was provided but the chosen algorithm doesn't natively support keying. /// /// On the other hand, if a key is required, but was not provided, a `key_required` error will be thrown. /// /// Some algorithms may require additional parameters. They have to be supplied as an options set: /// /// ```rust /// let options_handle = ctx.options_open()?; /// ctx.options_set("context", b"My application")?; /// ctx.options_set_u64("fanout", 16)?; /// let state_handle = ctx.symmetric_state_open("BLAKE2b-512", None, Some(options_handle))?; /// ``` /// /// If some parameters are mandatory but were not set, the `parameters_missing` error code will be returned. /// /// A notable exception is the `nonce` parameter, that is common to most AEAD constructions. /// /// If a nonce is required but was not supplied: /// /// - If it is safe to do so, the host will automatically generate a nonce. This is true for nonces that are large enough to be randomly generated, or if the host is able to maintain a global counter. /// - If not, the function will fail and return the dedicated `nonce_required` error code. /// /// A nonce that was automatically generated can be retrieved after the function returns with `symmetric_state_get(state_handle, "nonce")`. /// /// **Sample usage patterns:** /// /// - **Hashing** /// /// ```rust /// let mut out = [0u8; 64]; /// let state_handle = ctx.symmetric_state_open("SHAKE-128", None, None)?; /// ctx.symmetric_state_absorb(state_handle, b"data")?; /// ctx.symmetric_state_absorb(state_handle, b"more_data")?; /// ctx.symmetric_state_squeeze(state_handle, &mut out)?; /// ``` /// /// - **MAC** /// /// ```rust /// let mut raw_tag = [0u8; 64]; /// let key_handle = ctx.symmetric_key_import("HMAC/SHA-512", b"key")?; /// let state_handle = ctx.symmetric_state_open("HMAC/SHA-512", Some(key_handle), None)?; /// ctx.symmetric_state_absorb(state_handle, b"data")?; /// ctx.symmetric_state_absorb(state_handle, b"more_data")?; /// let computed_tag_handle = ctx.symmetric_state_squeeze_tag(state_handle)?; /// ctx.symmetric_tag_pull(computed_tag_handle, &mut raw_tag)?; /// ``` /// /// Verification: /// /// ```rust /// let state_handle = ctx.symmetric_state_open("HMAC/SHA-512", Some(key_handle), None)?; /// ctx.symmetric_state_absorb(state_handle, b"data")?; /// ctx.symmetric_state_absorb(state_handle, b"more_data")?; /// let computed_tag_handle = ctx.symmetric_state_squeeze_tag(state_handle)?; /// ctx.symmetric_tag_verify(computed_tag_handle, expected_raw_tag)?; /// ``` /// /// - **Tuple hashing** /// /// ```rust /// let mut out = [0u8; 64]; /// let state_handle = ctx.symmetric_state_open("TupleHashXOF256", None, None)?; /// ctx.symmetric_state_absorb(state_handle, b"value 1")?; /// ctx.symmetric_state_absorb(state_handle, b"value 2")?; /// ctx.symmetric_state_absorb(state_handle, b"value 3")?; /// ctx.symmetric_state_squeeze(state_handle, &mut out)?; /// ``` /// Unlike MACs and regular hash functions, inputs are domain separated instead of being concatenated. /// /// - **Key derivation using extract-and-expand** /// /// Extract: /// /// ```rust /// let mut prk = vec![0u8; 64]; /// let key_handle = ctx.symmetric_key_import("HKDF-EXTRACT/SHA-512", b"key")?; /// let state_handle = ctx.symmetric_state_open("HKDF-EXTRACT/SHA-512", Some(key_handle), None)?; /// ctx.symmetric_state_absorb(state_handle, b"salt")?; /// let prk_handle = ctx.symmetric_state_squeeze_key(state_handle, "HKDF-EXPAND/SHA-512")?; /// ``` /// /// Expand: /// /// ```rust /// let mut subkey = vec![0u8; 32]; /// let state_handle = ctx.symmetric_state_open("HKDF-EXPAND/SHA-512", Some(prk_handle), None)?; /// ctx.symmetric_state_absorb(state_handle, b"info")?; /// ctx.symmetric_state_squeeze(state_handle, &mut subkey)?; /// ``` /// /// - **Key derivation using a XOF** /// /// ```rust /// let mut subkey1 = vec![0u8; 32]; /// let mut subkey2 = vec![0u8; 32]; /// let key_handle = ctx.symmetric_key_import("BLAKE3", b"key")?; /// let state_handle = ctx.symmetric_state_open("BLAKE3", Some(key_handle), None)?; /// ctx.symmetric_absorb(state_handle, b"context")?; /// ctx.squeeze(state_handle, &mut subkey1)?; /// ctx.squeeze(state_handle, &mut subkey2)?; /// ``` /// /// - **Password hashing** /// /// ```rust /// let mut memory = vec![0u8; 1_000_000_000]; /// let options_handle = ctx.symmetric_options_open()?; /// ctx.symmetric_options_set_guest_buffer(options_handle, "memory", &mut memory)?; /// ctx.symmetric_options_set_u64(options_handle, "opslimit", 5)?; /// ctx.symmetric_options_set_u64(options_handle, "parallelism", 8)?; /// /// let state_handle = ctx.symmetric_state_open("ARGON2-ID-13", None, Some(options))?; /// ctx.symmtric_state_absorb(state_handle, b"password")?; /// /// let pw_str_handle = ctx.symmetric_state_squeeze_tag(state_handle)?; /// let mut pw_str = vec![0u8; ctx.symmetric_tag_len(pw_str_handle)?]; /// ctx.symmetric_tag_pull(pw_str_handle, &mut pw_str)?; /// ``` /// /// - **AEAD encryption with an explicit nonce** /// /// ```rust /// let key_handle = ctx.symmetric_key_generate("AES-256-GCM", None)?; /// let message = b"test"; /// /// let options_handle = ctx.symmetric_options_open()?; /// ctx.symmetric_options_set(options_handle, "nonce", nonce)?; /// /// let state_handle = ctx.symmetric_state_open("AES-256-GCM", Some(key_handle), Some(options_handle))?; /// let mut ciphertext = vec![0u8; message.len() + ctx.symmetric_state_max_tag_len(state_handle)?]; /// ctx.symmetric_state_absorb(state_handle, "additional data")?; /// ctx.symmetric_state_encrypt(state_handle, &mut ciphertext, message)?; /// ``` /// /// - **AEAD encryption with automatic nonce generation** /// /// ```rust /// let key_handle = ctx.symmetric_key_generate("AES-256-GCM-SIV", None)?; /// let message = b"test"; /// let mut nonce = [0u8; 24]; /// /// let state_handle = ctx.symmetric_state_open("AES-256-GCM-SIV", Some(key_handle), None)?; /// /// let nonce_handle = ctx.symmetric_state_options_get(state_handle, "nonce")?; /// ctx.array_output_pull(nonce_handle, &mut nonce)?; /// /// let mut ciphertext = vec![0u8; message.len() + ctx.symmetric_state_max_tag_len(state_handle)?]; /// ctx.symmetric_state_absorb(state_handle, "additional data")?; /// ctx.symmetric_state_encrypt(state_handle, &mut ciphertext, message)?; /// ``` /// /// - **Session authenticated modes** /// /// ```rust /// let mut out = [0u8; 16]; /// let mut out2 = [0u8; 16]; /// let mut ciphertext = [0u8; 20]; /// let key_handle = ctx.symmetric_key_generate("Xoodyak-128", None)?; /// let state_handle = ctx.symmetric_state_open("Xoodyak-128", Some(key_handle), None)?; /// ctx.symmetric_state_absorb(state_handle, b"data")?; /// ctx.symmetric_state_encrypt(state_handle, &mut ciphertext, b"abcd")?; /// ctx.symmetric_state_absorb(state_handle, b"more data")?; /// ctx.symmetric_state_squeeze(state_handle, &mut out)?; /// ctx.symmetric_state_squeeze(state_handle, &mut out2)?; /// ctx.symmetric_state_ratchet(state_handle)?; /// ctx.symmetric_state_absorb(state_handle, b"more data")?; /// let next_key_handle = ctx.symmetric_state_squeeze_key(state_handle, "Xoodyak-128")?; /// // ... /// ``` pub fn symmetric_state_open( algorithm_ptr: WasiPtr, algorithm_len: usize, key: OptSymmetricKey, options: OptOptions, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_open( algorithm_ptr: WasiPtr, algorithm_len: usize, key: OptSymmetricKey, options: OptOptions, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_state_open( algorithm_ptr, algorithm_len, key, options, result_ptr.as_mut_ptr(), ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Retrieve a parameter from the current state. /// /// In particular, `symmetric_state_options_get("nonce")` can be used to get a nonce that as automatically generated. /// /// The function may return `options_not_set` if an option was not set, which is different from an empty value. /// /// It may also return `unsupported_option` if the option doesn't exist for the chosen algorithm. pub fn symmetric_state_options_get( handle: SymmetricState, name_ptr: WasiPtr, name_len: usize, value: WasiMutPtr, value_max_len: Size, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_options_get( handle: SymmetricState, name_ptr: WasiPtr, name_len: usize, value: WasiMutPtr, value_max_len: Size, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_state_options_get( handle, name_ptr, name_len, value, value_max_len, result_ptr.as_mut_ptr(), ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Retrieve an integer parameter from the current state. /// /// In particular, `symmetric_state_options_get("nonce")` can be used to get a nonce that as automatically generated. /// /// The function may return `options_not_set` if an option was not set. /// /// It may also return `unsupported_option` if the option doesn't exist for the chosen algorithm. pub fn symmetric_state_options_get_u_64( handle: SymmetricState, name_ptr: WasiPtr, name_len: usize, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_options_get_u_64( handle: SymmetricState, name_ptr: WasiPtr, name_len: usize, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_state_options_get_u_64(handle, name_ptr, name_len, result_ptr.as_mut_ptr()) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Destroy a symmetric state. /// /// Objects are reference counted. It is safe to close an object immediately after the last function needing it is called. pub fn symmetric_state_close(handle: SymmetricState) -> Result<(), Error> { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_close(handle: SymmetricState) -> CryptoErrno; } let res = unsafe { symmetric_state_close(handle) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(()) } /// Absorb data into the state. /// /// - **Hash functions:** adds data to be hashed. /// - **MAC functions:** adds data to be authenticated. /// - **Tuplehash-like constructions:** adds a new tuple to the state. /// - **Key derivation functions:** adds to the IKM or to the subkey information. /// - **AEAD constructions:** adds additional data to be authenticated. /// - **Stateful hash objects, permutation-based constructions:** absorbs. /// /// If the chosen algorithm doesn't accept input data, the `invalid_operation` error code is returned. /// /// If too much data has been fed for the algorithm, `overflow` may be thrown. pub fn symmetric_state_absorb( handle: SymmetricState, data: WasiPtr, data_len: Size, ) -> Result<(), Error> { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_absorb( handle: SymmetricState, data: WasiPtr, data_len: Size, ) -> CryptoErrno; } let res = unsafe { symmetric_state_absorb(handle, data, data_len) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(()) } /// Squeeze bytes from the state. /// /// - **Hash functions:** this tries to output an `out_len` bytes digest from the absorbed data. The hash function output will be truncated if necessary. If the requested size is too large, the `invalid_len` error code is returned. /// - **Key derivation functions:** : outputs an arbitrary-long derived key. /// - **RNGs, DRBGs, stream ciphers:**: outputs arbitrary-long data. /// - **Stateful hash objects, permutation-based constructions:** squeeze. /// /// Other kinds of algorithms may return `invalid_operation` instead. /// /// For password-stretching functions, the function may return `in_progress`. /// In that case, the guest should retry with the same parameters until the function completes. pub fn symmetric_state_squeeze( handle: SymmetricState, out: WasiMutPtr, out_len: Size, ) -> Result<(), Error> { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_squeeze( handle: SymmetricState, out: WasiMutPtr, out_len: Size, ) -> CryptoErrno; } let res = unsafe { symmetric_state_squeeze(handle, out, out_len) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(()) } /// Compute and return a tag for all the data injected into the state so far. /// /// - **MAC functions**: returns a tag authenticating the absorbed data. /// - **Tuplehash-like constructions:** returns a tag authenticating all the absorbed tuples. /// - **Password-hashing functions:** returns a standard string containing all the required parameters for password verification. /// /// Other kinds of algorithms may return `invalid_operation` instead. /// /// For password-stretching functions, the function may return `in_progress`. /// In that case, the guest should retry with the same parameters until the function completes. pub fn symmetric_state_squeeze_tag(handle: SymmetricState) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_squeeze_tag( handle: SymmetricState, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_state_squeeze_tag(handle, result_ptr.as_mut_ptr()) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Use the current state to produce a key for a target algorithm. /// /// For extract-then-expand constructions, this returns the PRK. /// For session-base authentication encryption, this returns a key that can be used to resume a session without storing a nonce. /// /// `invalid_operation` is returned for algorithms not supporting this operation. pub fn symmetric_state_squeeze_key( handle: SymmetricState, alg_str_ptr: WasiPtr, alg_str_len: usize, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_squeeze_key( handle: SymmetricState, alg_str_ptr: WasiPtr, alg_str_len: usize, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_state_squeeze_key(handle, alg_str_ptr, alg_str_len, result_ptr.as_mut_ptr()) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Return the maximum length of an authentication tag for the current algorithm. /// /// This allows guests to compute the size required to store a ciphertext along with its authentication tag. /// /// The returned length may include the encryption mode's padding requirements in addition to the actual tag. /// /// For an encryption operation, the size of the output buffer should be `input_len + symmetric_state_max_tag_len()`. /// /// For a decryption operation, the size of the buffer that will store the decrypted data must be `ciphertext_len - symmetric_state_max_tag_len()`. pub fn symmetric_state_max_tag_len(handle: SymmetricState) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_max_tag_len( handle: SymmetricState, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_state_max_tag_len(handle, result_ptr.as_mut_ptr()) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Encrypt data with an attached tag. /// /// - **Stream cipher:** adds the input to the stream cipher output. `out_len` and `data_len` can be equal, as no authentication tags will be added. /// - **AEAD:** encrypts `data` into `out`, including the authentication tag to the output. Additional data must have been previously absorbed using `symmetric_state_absorb()`. The `symmetric_state_max_tag_len()` function can be used to retrieve the overhead of adding the tag, as well as padding if necessary. /// - **SHOE, Xoodyak, Strobe:** encrypts data, squeezes a tag and appends it to the output. /// /// If `out` and `data` are the same address, encryption may happen in-place. /// /// The function returns the actual size of the ciphertext along with the tag. /// /// `invalid_operation` is returned for algorithms not supporting encryption. pub fn symmetric_state_encrypt( handle: SymmetricState, out: WasiMutPtr, out_len: Size, data: WasiPtr, data_len: Size, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_encrypt( handle: SymmetricState, out: WasiMutPtr, out_len: Size, data: WasiPtr, data_len: Size, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_state_encrypt( handle, out, out_len, data, data_len, result_ptr.as_mut_ptr(), ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Encrypt data, with a detached tag. /// /// - **Stream cipher:** returns `invalid_operation` since stream ciphers do not include authentication tags. /// - **AEAD:** encrypts `data` into `out` and returns the tag separately. Additional data must have been previously absorbed using `symmetric_state_absorb()`. The output and input buffers must be of the same length. /// - **SHOE, Xoodyak, Strobe:** encrypts data and squeezes a tag. /// /// If `out` and `data` are the same address, encryption may happen in-place. /// /// The function returns the tag. /// /// `invalid_operation` is returned for algorithms not supporting encryption. pub fn symmetric_state_encrypt_detached( handle: SymmetricState, out: WasiMutPtr, out_len: Size, data: WasiPtr, data_len: Size, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_encrypt_detached( handle: SymmetricState, out: WasiMutPtr, out_len: Size, data: WasiPtr, data_len: Size, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_state_encrypt_detached( handle, out, out_len, data, data_len, result_ptr.as_mut_ptr(), ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// - **Stream cipher:** adds the input to the stream cipher output. `out_len` and `data_len` can be equal, as no authentication tags will be added. /// - **AEAD:** decrypts `data` into `out`. Additional data must have been previously absorbed using `symmetric_state_absorb()`. /// - **SHOE, Xoodyak, Strobe:** decrypts data, squeezes a tag and verify that it matches the one that was appended to the ciphertext. /// /// If `out` and `data` are the same address, decryption may happen in-place. /// /// `out_len` must be exactly `data_len` + `max_tag_len` bytes. /// /// The function returns the actual size of the decrypted message, which can be smaller than `out_len` for modes that requires padding. /// /// `invalid_tag` is returned if the tag didn't verify. /// /// `invalid_operation` is returned for algorithms not supporting encryption. pub fn symmetric_state_decrypt( handle: SymmetricState, out: WasiMutPtr, out_len: Size, data: WasiPtr, data_len: Size, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_decrypt( handle: SymmetricState, out: WasiMutPtr, out_len: Size, data: WasiPtr, data_len: Size, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_state_decrypt( handle, out, out_len, data, data_len, result_ptr.as_mut_ptr(), ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// - **Stream cipher:** returns `invalid_operation` since stream ciphers do not include authentication tags. /// - **AEAD:** decrypts `data` into `out`. Additional data must have been previously absorbed using `symmetric_state_absorb()`. /// - **SHOE, Xoodyak, Strobe:** decrypts data, squeezes a tag and verify that it matches the expected one. /// /// `raw_tag` is the expected tag, as raw bytes. /// /// `out` and `data` be must have the same length. /// If they also share the same address, decryption may happen in-place. /// /// The function returns the actual size of the decrypted message. /// /// `invalid_tag` is returned if the tag verification failed. /// /// `invalid_operation` is returned for algorithms not supporting encryption. pub fn symmetric_state_decrypt_detached( handle: SymmetricState, out: WasiMutPtr, out_len: Size, data: WasiPtr, data_len: Size, raw_tag: WasiPtr, raw_tag_len: Size, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_decrypt_detached( handle: SymmetricState, out: WasiMutPtr, out_len: Size, data: WasiPtr, data_len: Size, raw_tag: WasiPtr, raw_tag_len: Size, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_state_decrypt_detached( handle, out, out_len, data, data_len, raw_tag, raw_tag_len, result_ptr.as_mut_ptr(), ) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Make it impossible to recover the previous state. /// /// This operation is supported by some systems keeping a rolling state over an entire session, for forward security. /// /// `invalid_operation` is returned for algorithms not supporting ratcheting. pub fn symmetric_state_ratchet(handle: SymmetricState) -> Result<(), Error> { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_state_ratchet(handle: SymmetricState) -> CryptoErrno; } let res = unsafe { symmetric_state_ratchet(handle) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(()) } /// Return the length of an authentication tag. /// /// This function can be used by a guest to allocate the correct buffer size to copy a computed authentication tag. pub fn symmetric_tag_len(symmetric_tag: SymmetricTag) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_tag_len( symmetric_tag: SymmetricTag, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_tag_len(symmetric_tag, result_ptr.as_mut_ptr()) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Copy an authentication tag into a guest-allocated buffer. /// /// The handle automatically becomes invalid after this operation. Manually closing it is not required. /// /// Example usage: /// /// ```rust /// let mut raw_tag = [0u8; 16]; /// ctx.symmetric_tag_pull(raw_tag_handle, &mut raw_tag)?; /// ``` /// /// The function returns `overflow` if the supplied buffer is too small to copy the tag. /// /// Otherwise, it returns the number of bytes that have been copied. pub fn symmetric_tag_pull( symmetric_tag: SymmetricTag, buf: WasiMutPtr, buf_len: Size, ) -> Result { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_tag_pull( symmetric_tag: SymmetricTag, buf: WasiMutPtr, buf_len: Size, result_ptr: WasiMutPtr, ) -> CryptoErrno; } let mut result_ptr = std::mem::MaybeUninit::uninit(); let res = unsafe { symmetric_tag_pull(symmetric_tag, buf, buf_len, result_ptr.as_mut_ptr()) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(unsafe { result_ptr.assume_init() }) } /// Verify that a computed authentication tag matches the expected value, in constant-time. /// /// The expected tag must be provided as a raw byte string. /// /// The function returns `invalid_tag` if the tags don't match. /// /// Example usage: /// /// ```rust /// let key_handle = ctx.symmetric_key_import("HMAC/SHA-256", b"key")?; /// let state_handle = ctx.symmetric_state_open("HMAC/SHA-256", Some(key_handle), None)?; /// ctx.symmetric_state_absorb(state_handle, b"data")?; /// let computed_tag_handle = ctx.symmetric_state_squeeze_tag(state_handle)?; /// ctx.symmetric_tag_verify(computed_tag_handle, expected_raw_tag)?; /// ``` pub fn symmetric_tag_verify( symmetric_tag: SymmetricTag, expected_raw_tag_ptr: WasiPtr, expected_raw_tag_len: Size, ) -> Result<(), Error> { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_tag_verify( symmetric_tag: SymmetricTag, expected_raw_tag_ptr: WasiPtr, expected_raw_tag_len: Size, ) -> CryptoErrno; } let res = unsafe { symmetric_tag_verify(symmetric_tag, expected_raw_tag_ptr, expected_raw_tag_len) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(()) } /// Explicitly destroy an unused authentication tag. /// /// This is usually not necessary, as `symmetric_tag_pull()` automatically closes a tag after it has been copied. /// /// Objects are reference counted. It is safe to close an object immediately after the last function needing it is called. pub fn symmetric_tag_close(symmetric_tag: SymmetricTag) -> Result<(), Error> { #[link(wasm_import_module = "wasi_ephemeral_crypto_symmetric")] extern "C" { fn symmetric_tag_close(symmetric_tag: SymmetricTag) -> CryptoErrno; } let res = unsafe { symmetric_tag_close(symmetric_tag) }; if res != 0 { return Err(Error::WasiError(res as _)); } Ok(()) }