Using encryption as authentication I am building an app in which I would like to encrypt user data and support offline login.

From this answer about using passwords for encryption:


  Best practice is to use a PBKDF to generate a 'key encryption key' (KEK), then a full entropy random key is used to encrypt the data, and the KEK is used to encrypt that key.


Would it be reasonable to include in the data a known value such as "hello everybody" (or the username, if the variability per user would help?), and authenticate users by attempting to decrypt their data and checking that the decrypted value matches the known value?
With the numbering in your comment: 1 yes, 2 no.

If you just need to encrypt files, use age if you can.

I'm going to answer the rest of this in the context of using libsodium as your cryptographic library. You might have a different choice, like NaCl or , but any choice should support a high-level API like I'll describe. If your library doesn't, it's not safe and you should pick a better one.

For generating random numbers (nonces, salts, etc), use your library's secure random generation function(s). For libsodium, this is almost always going to be the randombytes_buf function.

For turning a password into a key, use your library's password hashing function. If that's libsodium, you want to hash it using the crypto_pwhash function. That takes in a random salt generated via randombytes_buf, some parameter limits, and a password, and outputs a key. You'll need to store the parameters and the salt with the encrypted data.

The reason to use the password-derived key as a KEK instead of using it directly is to allow the user to change their password without needing to re-encrypt all the data. You'd just need to re-encrypt the actual data key. The encryption method used should be your library's preferred authenticated encryption function. For libsodium, that's crypto_secretbox_easy (and decryption is crypto_secretbox_open_easy). If you don't actually need this functionality, don't include it, just use the output key of the PBKDF to encrypt the data.

You don't need to check that the data has been decrypted successfully. That's handled by the Authentication part of an AEAD (Authenticated Encryption with Associated Data) mode. If the authentication tag verifies, it decrypts correctly. If it doesn't verify, it discards the (potentially very dangerous) decrypted data without doing anything else to it.

Assuming you have to use libsodium or similar and are going with the KEK system, you'll need a random salt, a unique nonce for the AEAD encrypting/decrypting the data key with the password-derived KEK, and a second unique nonce for encrypting/decrypting the data.