{"record":{"id":"ca4a740a9481837d","repo":"googleworkspace/cli","slug":"failed-to-create-cipher-e","errorCode":null,"errorMessage":"Failed to create cipher: {e}","messagePattern":"Failed to create cipher: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/google-workspace-cli/src/credential_store.rs","lineNumber":386,"sourceCode":"\n    let key = resolve_key(backend, &provider, &key_file)?;\n\n    // Cache for subsequent calls within this process.\n    if KEY.set(key).is_ok() {\n        Ok(key)\n    } else {\n        Ok(*KEY\n            .get()\n            .expect(\"key must be initialized if OnceLock::set() failed\"))\n    }\n}\n\n/// Encrypts plaintext bytes using AES-256-GCM with a machine-derived key.\n/// Returns nonce (12 bytes) || ciphertext.\npub fn encrypt(plaintext: &[u8]) -> anyhow::Result<Vec<u8>> {\n    let key = get_or_create_key()?;\n    let cipher = Aes256Gcm::new_from_slice(&key)\n        .map_err(|e| anyhow::anyhow!(\"Failed to create cipher: {e}\"))?;\n\n    let nonce = Aes256Gcm::generate_nonce(&mut OsRng);\n    let ciphertext = cipher\n        .encrypt(&nonce, plaintext)\n        .map_err(|e| anyhow::anyhow!(\"Encryption failed: {e}\"))?;\n\n    // Prepend nonce to ciphertext\n    let mut result = nonce.to_vec();\n    result.extend_from_slice(&ciphertext);\n    Ok(result)\n}\n\n/// Decrypts data produced by `encrypt()`.\npub fn decrypt(data: &[u8]) -> anyhow::Result<Vec<u8>> {\n    if data.len() < 12 {\n        anyhow::bail!(\"Encrypted data too short\");\n    }\n","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/googleworkspace/cli/blob/a3768d0e82ad83cca2da97724e46bea4ff0e6dbd/crates/google-workspace-cli/src/credential_store.rs#L368-L404","documentation":"Defensive invariant check in encrypt(): Aes256Gcm::new_from_slice(&key) only fails when the slice is not exactly 32 bytes, and get_or_create_key() returns a [u8; 32]. In practice this branch is unreachable — it exists to convert a hypothetical future invariant violation (wrong key length) into a clear error instead of a panic. Hitting it indicates the key pipeline was bypassed or modified.","triggerScenarios":"Code changes that source the key from somewhere other than get_or_create_key(); a refactor that alters the key type; memory corruption. No normal runtime input reaches it.","commonSituations":"Contributors forking the credential store and introducing a variable-length key; essentially never seen by end users.","solutions":["If you are building from source, verify the key is still obtained via get_or_create_key() and is exactly 32 bytes","Report a bug with reproduction steps — this indicates an internal invariant break"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never source the AES key outside get_or_create_key() — the [u8; 32] type is the invariant","In forks, keep key length assertions in unit tests (read_key_file wrong-length tests exist as a model)"],"tags":["aes-256-gcm","crypto","invariant","defensive"],"backgroundTag":"invalid-aes-key-length","analyzedSha":"a3768d0e82ad83cca2da97724e46bea4ff0e6dbd","analyzedAt":"2026-08-16T19:51:46.516Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}