libsodium (Encryption/Decryption Library)

Started by airr, September 16, 2024, 05:56:57 PM

Previous topic - Next topic

airr

After spending the entire weekend trying to get Microsoft's CNG Encryption working, I decided to go in a different direction.

libsodium to the rescue.  Took about 90 min (in between stuff for work) to get this demo going.

Encrypt.bas

$HEADER
    #include "include\sodium.h"
    #define AES_KEYLEN crypto_aead_chacha20poly1305_KEYBYTES
    #define HMAC_KEYLEN crypto_auth_hmacsha256_KEYBYTES
    #define HMAC_LEN crypto_auth_hmacsha256_BYTES
    #define SALT_SIZE crypto_pwhash_SALTBYTES
    #define NONCE_SIZE crypto_aead_chacha20poly1305_NPUBBYTES
    #define CHUNK_SIZE 4096  // Size of chunks for encryption
$HEADER

$Library "libsodium.lib"

Function main(argc as integer, argv as pchar ptr)
    Dim As String password, filename, output_filename
    Dim As Int result

    ' Prompt User for input/output filenames, and Password
    Input "Enter Input Filename: ", filename
    Input "Enter Output Filename: ", output_filename
    Input "Enter Password to Encrypt: ", password

    ' Encrypt the file, saving to output filename
    result = encrypt(filename, output_filename, password)

    Return result

End Function

Function encrypt(filename as string, output_filename as string, password as string) as int
    Dim As UCHAR aes_key[AES_KEYLEN], hmac_key[HMAC_KEYLEN], salt[SALT_SIZE]
    Dim As UCHAR nonce[NONCE_SIZE], hmac_out[HMAC_LEN], chunk[CHUNK_SIZE]
    Dim As UCHAR encrypted_chunk[CHUNK_SIZE + crypto_aead_chacha20poly1305_ABYTES]
    Dim As ULONGLONG encrypted_chunk_len

    ' // Generate salt for key derivation
    randombytes_buf(salt, sizeof(salt));

    ' // Derive AES and HMAC keys from the password
    if crypto_pwhash(aes_key, sizeof(aes_key), password, len(password), salt, crypto_pwhash_OPSLIMIT_MIN, crypto_pwhash_MEMLIMIT_MIN, crypto_pwhash_ALG_DEFAULT) != 0 Then
        Print "Key derivation failed."
        return 1
    end if
    if crypto_pwhash(hmac_key, sizeof(hmac_key), password, len(password), salt, crypto_pwhash_OPSLIMIT_MIN, crypto_pwhash_MEMLIMIT_MIN, crypto_pwhash_ALG_DEFAULT) != 0 Then
        Print "Key derivation failed."
        return 1
    end if

    ' // Encrypt
    randombytes_buf(nonce, sizeof(nonce));

    ' Open Input and Output files
    Open filename For Binary Input as input_file
    Open output_filename For Binary New as output_file

    ' // Write salt and nonce to the output file
    Put$ output_file, salt, sizeof(salt)
    Put$ output_file, nonce, sizeof(nonce)

    ' // Encrypt data in chunks
    while (1)
        Dim As size_t bytes_read = fread(chunk, 1, sizeof(chunk), input_file);
        if bytes_read = 0 then Exit While

        if crypto_aead_chacha20poly1305_encrypt(encrypted_chunk, &encrypted_chunk_len, chunk, bytes_read, NULL, 0, NULL, nonce, aes_key) != 0 Then
            Close input_file
            Close output_file
            Print  "Encryption failed."
            return 1
        End If

        ' Write the current chuck to output_file
        Put$ output_file, encrypted_chunk, encrypted_chunk_len
    Wend

    ' Cleanup
    Close input_file
    Close output_file

    Print "Data written to file successfully."

    Return 0
End Function


Decrypt.bas

$HEADER
    #include "include\sodium.h"
    #define AES_KEYLEN crypto_aead_chacha20poly1305_KEYBYTES
    #define HMAC_KEYLEN crypto_auth_hmacsha256_KEYBYTES
    #define HMAC_LEN crypto_auth_hmacsha256_BYTES
    #define SALT_SIZE crypto_pwhash_SALTBYTES
    #define NONCE_SIZE crypto_aead_chacha20poly1305_NPUBBYTES
    #define CHUNK_SIZE 4096  // Size of chunks for encryption
$HEADER

$Library "libsodium.lib"

Function main(argc as integer, argv as pchar ptr)
    Dim As String password, filename, output_filename
    Dim As Int result

    ' Prompt User for input/output filenames, and Password
    Input "Enter Input Filename: ", filename
    Input "Enter Output Filename: ", output_filename
    Input "Enter Password to Decrypt: ", password

    ' Decrypt the file, saving to output filename
    result = decrypt(filename, output_filename, password)

    Return result

End Function

Function decrypt(filename as string, output_filename as string, password as string) as int
    Dim As UCHAR aes_key[AES_KEYLEN], hmac_key[HMAC_KEYLEN], salt[SALT_SIZE]
    Dim As UCHAR nonce[NONCE_SIZE], decrypted_chunk[CHUNK_SIZE]
    Dim As UCHAR chunk[CHUNK_SIZE + crypto_aead_chacha20poly1305_ABYTES]
    Dim As ULONGLONG decrypted_chunk_len, chunk_len

    ' Open Input and Output files
    Open filename For Binary Input as input_file
    Open output_filename For Binary New as output_file

    ' Read salt and nonce from the input file
    Get$ input_file, salt, sizeof(salt)
    Get$ input_file, nonce, sizeof(nonce)

    ' Derive AES and HMAC keys from the password
    if crypto_pwhash(aes_key, sizeof(aes_key), password, len(password), salt, crypto_pwhash_OPSLIMIT_MIN, crypto_pwhash_MEMLIMIT_MIN, crypto_pwhash_ALG_DEFAULT) != 0 Then
        Print "Key derivation failed."
        return 1
    end if

    if crypto_pwhash(hmac_key, sizeof(hmac_key), password, len(password), salt, crypto_pwhash_OPSLIMIT_MIN, crypto_pwhash_MEMLIMIT_MIN, crypto_pwhash_ALG_DEFAULT) != 0 Then
        Print "Key derivation failed."
        return 1
    end if   

    ' Decrypt data in chunks
    While True
        chunk_len = fread(chunk, 1, sizeof(chunk), input_file)
        If chunk_len = 0 Then Exit While

        If crypto_aead_chacha20poly1305_decrypt(decrypted_chunk, &decrypted_chunk_len, NULL, chunk, chunk_len, NULL, 0, nonce, aes_key) != 0 Then
            Close input_file
            Close output_file
            Print "Decryption failed."
            return 1;
        End If 

        Put$ output_file, decrypted_chunk, decrypted_chunk_len     
    Wend

    ' Cleanup
    Close input_file
    Close output_file

    Print "Data read from file and decrypted successfully."

    Return 0
End Function

Tested with a 900mb video file.  Compiles with both Pelles and MSVC.

Support files attached below.

AIR.