/* Provenance: openssl/openssl aes_ocb_cipher (fixed). * repo: openssl/openssl * commit: 119ab9555dc62275bbd71f6f49529b1a44feba42 * parent: 39488cb863074d5b8ece833905f7ad2b357d44fe * commit_url: https://github.com/openssl/openssl/commit/119ab9555dc62275bbd71f6f49529b1a44feba42 * cve: CVE-2026-75803 * license: Apache-2.0 * function: aes_ocb_cipher * relpath: providers/implementations/ciphers/cipher_aes_ocb.c */ static int aes_ocb_cipher(void *vctx, unsigned char *out, size_t *outl, size_t outsize, const unsigned char *in, size_t inl) { PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; if (!ossl_prov_is_running()) return 0; /* NULL input indicates Final, which must generate or check the tag. */ if (in == NULL) return aes_ocb_block_final(vctx, out, outl, outsize); if (outsize < inl) { ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); return 0; } /* * Mirror the streaming handler: refuse if the key has not been set, * and push the buffered IV into the OCB context before any data is * processed. Without this, CRYPTO_ocb128_encrypt/decrypt runs with * Offset_0 = 0 regardless of the caller's IV -- catastrophic * (key, nonce) reuse, and a subsequent EVP_*Final_ex() emits a tag * that is a function of (key, iv) only. */ if (!ctx->key_set || !update_iv(ctx)) { ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); return 0; } if (!aes_generic_ocb_cipher(ctx, in, out, inl)) { ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); return 0; } *outl = inl; return 1; }