elasticdog / transcrypt

transparently encrypt files within a git repository
MIT License
1.43k stars 102 forks source link

OpenSSL 3 will break transcrypt #133

Closed Erotemic closed 2 years ago

Erotemic commented 2 years ago

In openssl3 the behavior has changed such that when the -S flag is specified to give a specific salt, the output will no longer be prefixed with "Salted__" and contain the salt in the encoding. Note that when -S is not given and the salt is randomized, the prepend still happens, but we need determinism, so we have to use -S, and unfortunately there is no option to output ciphertext with the 1.x encoding.

https://stackoverflow.com/questions/72149327/openssl-3-0-2-with-custom-salt-doesnt-start-with-salted

I'm working on fixing this in my PR such that if the version of openssl is detected to be 3.x, it prepends the salt encoding to the output. The decryption step still works fine when -S is not given in the decrypt command (which we don't do).


So, it seems to be really tricky to concatenate these encodings together. You can't store the ascii encoding in bash variables because they might contain null bytes, and you can concatenate the base64 encodings because that introduces padding in the middle of the text. It seems like you have to concat the bytes by writing to a file. Here is a MWE for doing this that I came up with.

    #### TESTS
    SALT_PREFIX="Salted__"
    SALT_HEX="deadbeaf00000bad"
    printf "secret" | SECRET_PASSWORD=12345 openssl enc -aes-256-cbc -pbkdf2 -md sha512 -pass env:SECRET_PASSWORD -S "$SALT_HEX" -e -a 
    # OpenSSL 1.x output
    SALTED_CIPHERTEXT_B64_ORIG="U2FsdGVkX1/erb6vAAALrWJ5ujRXTt7kQoCwvQPCjJE="
    # OpenSSL 3.x output
    UNSALTED_CIPHERTEXT_B64="Ynm6NFdO3uRCgLC9A8KMkQ=="

    PART1_ACII="$SALT_PREFIX"
    PART2_HEX="$SALT_HEX"
    PART3_B64="$UNSALTED_CIPHERTEXT_B64"

    b64_to_ascii(){
        base64 -d
    }
    ascii_to_b64(){
        base64
    }
    hex_to_ascii(){
        xxd -r -p 
    }

    TMP_FPATH=$(mktemp)
    printf "" > "$TMP_FPATH"
    # shellcheck disable=SC2059
    printf "$PART1_ACII" > "$TMP_FPATH"
    # shellcheck disable=SC2059
    printf "$PART2_HEX" | hex_to_ascii >> "$TMP_FPATH"
    # shellcheck disable=SC2059
    printf "$PART3_B64" | b64_to_ascii >> "$TMP_FPATH"

    SALTED_CIPHERTEXT_B64_RECON=$(cat "$TMP_FPATH" | ascii_to_b64)

    if [[ "$SALTED_CIPHERTEXT_B64_RECON" == "$SALTED_CIPHERTEXT_B64_ORIG" ]]; then
        echo "correct reconstruction"
    else
        echo "incorrect reconstruction"
    fi

When I go to implement this, I'll take out the extra bash function as it's clear enough what base64 and xxd are doing. I spent way to long trying to get consistent bash conversions between ascii, hex, oct, and baes64 and these are some of the resulting functions.

But if there is a better way that does not involve writing temporary files like this (or there is a better way to write the temporary file) then I'd like to know.

I did find a better way of doing the concatenation without needing to write to a file:

    (printf "Salted__" && printf "deadbeaf00000bad" | xxd -r -p && echo "$UNSALTED_CIPHERTEXT_B64" | base64 -d ) | base64
jmurty commented 2 years ago

Fixed in main branch, pending release in version 2.2.0