When using the stream_get_meta_data() function, the manual states not to use the value of 'unread_bytes' in a script, which lead to a non functioning script in my environment. I guess you'd rather use the value of 'eof'. Or, even better, use the feof() function.

`$secret_key` is a secret key. Not a password. It's binary data, notsomething designed to be human readable, but rather to have a keyspace as large as possible for a given length.The `keygen()` function creates such a key. That has to remain secret,as it is used both to encrypt and decrypt data.

`$nonce` is a unique value. Like the secret, its length is fixed. Butit doesn't have to be secret, and can be sent along with the encryptedmessage. The nonce doesn't have to be unpredicable either. It just hasto be unique for a given key. With the `secretbox()` API, using`random_bytes()` is a totally fine way to generate nonces.

Encrypted messages are slightly larger than unencrypted messages,because they include an authenticator, used by the decryption functionto check that the content was not altered.

Sometimes, the length of a message may provide a lot of informationabout its nature. If a message is one of "yes", "no" and "maybe",encrypting the message doesn't help: knowing the length is enough toknow what the message is.

Padding is a technique to mitigate this, by making the length amultiple of a given block size.

Messages must be padded prior to encryption, and unpadded afterdecryption.

A password cannot be directly used as a secret key. Passwords areshort, must be typable on a keyboard, and people who don't use apassword manager should be able to remember them.

A 8 characters password is thus way weaker than a 8 bytes key.

The `sodium_crypto_pwhash()` function perform a computationallyintensive operation on a password in order to derive a secret key.

By doing do, brute-forcing all possible passwords in order to find thesecret key used to encrypt the data becomes an expensive operation.

Multiple algorithms can be used to derive a key from a password, andfor each of them, different parameters can be chosen. It is importantto store all of these along with encrypted data. Using the samealgorithm and the same parameters, the same secret key can bedeterministically recomputed.

In fact, `crypto_secretbox()` would work to encrypt as file, but onlyif that file is pretty small. Since we have to provide the entirecontent as a string, it has to fit in memory.

If the file is large, we can split it into small chunks, and encryptchunks individually.

By doing do, we can encrypt arbitrary large files. But we need to makesure that chunks cannot be deleted, truncated, duplicated andreordered. In other words, we don't have a single "message", but astream of messages, and during the decryption process, we need a wayto check that the whole stream matches what we encrypted.

So we create a new stream (`init_push`) and push a sequence of messagesinto it (`push`). Each individual message has a tag attached to it, bydefault `TAG_MESSAGE`. In order for the decryption process to knowwhere the end of the stream is, we tag the last message with the`TAG_FINAL` tag.

When we consume the stream (`init_pull`, then `pull` for eachmessage), we check that they can be properly decrypted, and retrieveboth the decrypted chunks and the attached tags. If we read the lastchunk (`TAG_FINAL`) and we are at the end of the file, we know that wecompletely recovered the original stream.