risingwavelabs/risingwave · error · CryptographyError

{stage:?} stage, reason: {reason}

Error message

{stage:?} stage, reason: {reason}

What it means

CryptographyError is a thiserror error wrapper for openssl failures in encrypt/decrypt scalar functions. Its message reports which stage (Encrypt or Decrypt) failed plus the underlying openssl::error::ErrorStack as the source, so the real cause (key size, padding, bad input length) is in the chained source.

Source

Thrown at src/expr/impl/src/scalar/encrypt.rs:192

    config.eval(data, CryptographyStage::Decrypt)
}

#[function(
    "encrypt(bytea, bytea, varchar) -> bytea",
    prebuild = "CipherConfig::parse_cipher_config($1, $2)?"
)]
fn encrypt(data: &[u8], config: &CipherConfig) -> Result<Box<[u8]>, CryptographyError> {
    config.eval(data, CryptographyStage::Encrypt)
}

#[derive(Debug)]
enum CryptographyStage {
    Encrypt,
    Decrypt,
}

#[derive(Debug, thiserror::Error)]
#[error("{stage:?} stage, reason: {reason}")]
struct CryptographyError {
    pub stage: CryptographyStage,
    #[source]
    pub reason: openssl::error::ErrorStack,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_decrypt() {
        let data = b"hello world";
        let mode = "aes";

        let config = CipherConfig::parse_cipher_config(
            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" as &[u8],
            mode,

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Inspect the chained openssl::error::ErrorStack source (`err.source()`) for the concrete OpenSSL reason code.
  2. Verify the encryption key length matches the algorithm (16/24/32 bytes for AES-128/192/256).
  3. Ensure ciphertext length is a valid multiple of the cipher block size and padding settings match between encrypt and decrypt.

Example fix

// before
let key = user_key.as_bytes(); // arbitrary length
// after
let key = derive_key(user_key.as_bytes(), 32); // pad/hash to required AES-256 key length
Defensive patterns

Strategy: try-catch

Validate before calling

if key.len() != 16 && key.len() != 24 && key.len() != 32 {
    return Err("AES key must be 16, 24, or 32 bytes".into());
}

Try / catch

match encrypt_fn(key, plaintext) {
    Ok(ct) => ct,
    Err(e) => {
        // inspect openssl source chain
        let mut src = e.source();
        while let Some(s) = src { eprintln!("caused by: {s}"); src = s.source(); }
        return Err(anyhow!("crypto failed: {e}"));
    }
}

Prevention

When it happens

Trigger: Invoking the encrypt() or decrypt() SQL functions when the OpenSSL crypto operation returns an ErrorStack — e.g. wrong key length for AES, input not a multiple of the block size, or corrupted ciphertext during decrypt.

Common situations: Encrypting data with a key that doesn't match the required AES key size; decrypting data encrypted elsewhere with different parameters; OpenSSL misconfiguration or FIPS provider restrictions.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/97f805d6a0f4f989. Report an issue: GitHub.