(use "proposal_common.witx") ;;; Symmetric operations. (module $wasi_ephemeral_crypto_symmetric (import "memory" (memory)) ;;; 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. (@interface func (export "symmetric_key_generate") (param $algorithm string) (param $options $opt_options) (result $error (expected $symmetric_key (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_key_import") (param $algorithm string) (param $raw (@witx const_pointer u8)) (param $raw_len $size) (result $error (expected $symmetric_key (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_key_export") (param $symmetric_key $symmetric_key) (result $error (expected $array_output (error $crypto_errno))) ) ;;; Destroy a symmetric key. ;;; ;;; Objects are reference counted. It is safe to close an object immediately after the last function needing it is called. (@interface func (export "symmetric_key_close") (param $symmetric_key $symmetric_key) (result $error (expected (error $crypto_errno))) ) ;;; __(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. (@interface func (export "symmetric_key_generate_managed") (param $secrets_manager $secrets_manager) (param $algorithm string) (param $options $opt_options) (result $error (expected $symmetric_key (error $crypto_errno))) ) ;;; __(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. (@interface func (export "symmetric_key_store_managed") (param $secrets_manager $secrets_manager) (param $symmetric_key $symmetric_key) (param $symmetric_key_id (@witx pointer u8)) (param $symmetric_key_id_max_len $size) (result $error (expected (error $crypto_errno))) ) ;;; __(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. (@interface func (export "symmetric_key_replace_managed") (param $secrets_manager $secrets_manager) (param $symmetric_key_old $symmetric_key) (param $symmetric_key_new $symmetric_key) (result $error (expected $version (error $crypto_errno))) ) ;;; __(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. (@interface func (export "symmetric_key_id") (param $symmetric_key $symmetric_key) (param $symmetric_key_id (@witx pointer u8)) (param $symmetric_key_id_max_len $size) (result $error (expected (tuple $size $version) (error $crypto_errno))) ) ;;; __(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. (@interface func (export "symmetric_key_from_id") (param $secrets_manager $secrets_manager) (param $symmetric_key_id (@witx const_pointer u8)) (param $symmetric_key_id_len $size) (param $symmetric_key_version $version) (result $error (expected $symmetric_key (error $crypto_errno))) ) ;;; 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")?; ;;; // ... ;;; ``` (@interface func (export "symmetric_state_open") (param $algorithm string) (param $key $opt_symmetric_key) (param $options $opt_options) (result $error (expected $symmetric_state (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_state_options_get") (param $handle $symmetric_state) (param $name string) (param $value (@witx pointer u8)) (param $value_max_len $size) (result $error (expected $size (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_state_options_get_u64") (param $handle $symmetric_state) (param $name string) (result $error (expected $u64 (error $crypto_errno))) ) ;;; Destroy a symmetric state. ;;; ;;; Objects are reference counted. It is safe to close an object immediately after the last function needing it is called. (@interface func (export "symmetric_state_close") (param $handle $symmetric_state) (result $error (expected (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_state_absorb") (param $handle $symmetric_state) (param $data (@witx const_pointer u8)) (param $data_len $size) (result $error (expected (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_state_squeeze") (param $handle $symmetric_state) (param $out (@witx pointer u8)) (param $out_len $size) (result $error (expected (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_state_squeeze_tag") (param $handle $symmetric_state) (result $error (expected $symmetric_tag (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_state_squeeze_key") (param $handle $symmetric_state) (param $alg_str string) (result $error (expected $symmetric_key (error $crypto_errno))) ) ;;; 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()`. (@interface func (export "symmetric_state_max_tag_len") (param $handle $symmetric_state) (result $error (expected $size (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_state_encrypt") (param $handle $symmetric_state) (param $out (@witx pointer u8)) (param $out_len $size) (param $data (@witx const_pointer u8)) (param $data_len $size) (result $error (expected $size (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_state_encrypt_detached") (param $handle $symmetric_state) (param $out (@witx pointer u8)) (param $out_len $size) (param $data (@witx const_pointer u8)) (param $data_len $size) (result $error (expected $symmetric_tag (error $crypto_errno))) ) ;;; - **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. (@interface func (export "symmetric_state_decrypt") (param $handle $symmetric_state) (param $out (@witx pointer u8)) (param $out_len $size) (param $data (@witx const_pointer u8)) (param $data_len $size) (result $error (expected $size (error $crypto_errno))) ) ;;; - **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. (@interface func (export "symmetric_state_decrypt_detached") (param $handle $symmetric_state) (param $out (@witx pointer u8)) (param $out_len $size) (param $data (@witx const_pointer u8)) (param $data_len $size) (param $raw_tag (@witx const_pointer u8)) (param $raw_tag_len $size) (result $error (expected $size (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_state_ratchet") (param $handle $symmetric_state) (result $error (expected (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_tag_len") (param $symmetric_tag $symmetric_tag) (result $error (expected $size (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_tag_pull") (param $symmetric_tag $symmetric_tag) (param $buf (@witx pointer u8)) (param $buf_len $size) (result $error (expected $size (error $crypto_errno))) ) ;;; 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)?; ;;; ``` (@interface func (export "symmetric_tag_verify") (param $symmetric_tag $symmetric_tag) (param $expected_raw_tag_ptr (@witx const_pointer u8)) (param $expected_raw_tag_len $size) (result $error (expected (error $crypto_errno))) ) ;;; 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. (@interface func (export "symmetric_tag_close") (param $symmetric_tag $symmetric_tag) (result $error (expected (error $crypto_errno))) ) )