#!/bin/sh # Test suite for OneTime. # # How to write a new test: # # 1) start_new_test "some descriptive test string" # # 2) Do stuff. Your cwd is a temporary directory, tests/test-tmp/, # and it already has its own tmp config directories in place, e.g., # "tests/test-tmp/dot-onetime" and others (see reset_config for # the full list). So a typical invocation will look like this: # "../../onetime --config=dot-onetime -e -p ../test-pad-1 etc etc" # # (All the permanent test data lives in tests/, so you'll use # arguments like "../test-plaintext-b" and ../test-pad-1" a lot.) # # 3) If a tested condition fails, echo "ERROR: describe how & why", # then set PASSED="no". # # 4) At the end of the test, call check_result. # # Note on why tests should *always* pass a config dir explicitly: # # Even when passing -n (--no-trace), you should always specify one of # the test config directories explicitly (use "blank-dot-onetime" for # a no-op config dir). Otherwise, if you happen to have some of the # test pads recorded in your own ~/.onetime/pad-records (as can # accidentally happen if you've been doing OneTime development and # failed to pass '--config' every time you manually tested), you could # write a test here that either inadvertently depends on something in # your ~/.onetime/pad-records or is inadvertently sensitive to the # ~/.onetime/pad-records of other people who have played around with # the test pads and accidentally affected their ~/.onetime/pad-records. # Either way, your test would not be portable. Even when an invocation # couldn't possibly depend on nor affect any values in pad-records # (e.g., it just runs --show-id or something), you should still pass # "--config=blank-dot-onetime", because otherwise someone running the # test who doesn't currently have a ~/.onetime directory will suddenly # have one afterwards, which would be bad behavior for a test suite. TEST_PAD_1_ID="6d0031fd04e927feb893aad9478b9e7e213b56e7fc766fdb57f12d3a55fa36e4" TEST_PAD_1_V1_ID="6af6d0ac17081705cec30833da3cd436a400c429" TEST_PAD_2_ID="7613667562635a22e62c55aabbb22d7a39bc368a8d2263e611db5caa215cc4cf" TEST_PAD_2_V1_ID="de61f169bce003a1189b3e6ebb8ddfc0ef007ac2" # See start_new_test() and check_result() for what these do. THIS_TEST="(no test name initialized yet)" PASSED="(uninitialized)" cd tests rm -rf test-tmp mkdir test-tmp cd test-tmp start_new_test() { THIS_TEST="${1}" PASSED="yes" # see check_result() reset_config } check_result() { if [ "${1}x" = "XFAILx" ]; then THIS_XFAIL="yes" elif [ "${1}x" != "x" ]; then echo "ERROR: unknown argument '${1}' to check_result()" fi if [ ${PASSED} = "yes" ]; then if [ "${THIS_XFAIL}x" = "yesx" ]; then echo "(XPASS): ${THIS_TEST}" else echo "PASS: ${THIS_TEST}" fi else if [ "${THIS_XFAIL}x" = "yesx" ]; then echo "(XFAIL): ${THIS_TEST}" else echo "FAIL: ${THIS_TEST}" fi # Print an extra blank line separating this "FAIL" line from the # tests that come after it, so any already-printed errors related # to this failure are visually grouped together with it. echo "" fi unset THIS_XFAIL # Is this how to do local scope portably in shell? } # TODO: You'd think this function would be named 'reset_test_area' and # would just do something like this: # # cd .. # rm -rf test-tmp # mkdir test-tmp # cd test-tmp # # But apparently we've got some inter-test dependencies, because if # you try that, lots of tests fail. None of this means the test suite # is invalid, of course: it's testing what we think it's testing, it's # just not designed as well as it could be, and that should be fixed. reset_config() { for name in dot-onetime v1-dot-onetime blank-dot-onetime; do rm -rf ${name} cp -a ../${name} ./${name} rm -rf ${name}/.svn done } ######################################################################## ### Explain the test suite's output. ### ######################################################################## echo '' echo ' Each passing test is indicated by exactly one "PASS" line' echo ' that includes a brief description of the test.' echo '' echo ' A failing test will show "ERROR" details first -- which may span' echo ' multiple lines and be very noisy -- followed by a "FAIL" line' echo ' giving a brief description of that test. If a test is currently' echo ' expected to fail, for example due to a known bug not yet fixed, ' echo ' then its description starts with "(XFAIL)".' echo '' echo " Note that some tests may take a while; don't be alarmed" echo " if a minute or two goes by without output." echo '' ######################################################################## start_new_test "basic encryption, decryption" ../../onetime --config=blank-dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-b-1 ../test-plaintext-b ../../onetime --config=blank-dot-onetime -d -p ../test-pad-1 \ -o tmp-plaintext-b tmp-ciphertext-b-1 if ! cmp tmp-plaintext-b ../test-plaintext-b then echo "" echo "ERROR: decrypted plaintext does not match original plaintext" PASSED="no" fi check_result ######################################################################## start_new_test "encryption, decryption of large plaintext" # Assemble a pad and plaintext so large that not only are they many # times larger than our 8k chunk size, but they're larger than the # Python bzip2 compressor's maximum possible buffer. Experiments # indicate that range(0, 2) would be enough here, but let's use 5. for ignored in `python -c "for i in range(0, 5): print i"`; do cat ../test-pad-1 >> large-pad cat ../test-pad-2 >> large-plaintext done ../../onetime --config=blank-dot-onetime -e -p large-pad \ -o tmp-large-ciphertext large-plaintext ../../onetime --config=blank-dot-onetime -d -p large-pad \ -o tmp-large-plaintext tmp-large-ciphertext if ! cmp tmp-large-plaintext large-plaintext then echo "" echo "ERROR: decrypted large plaintext does not match original plaintext" PASSED="no" fi check_result ######################################################################## start_new_test "option parsing" ### # In the tests of the various option parsing methods, "e.N" is # encrypted text and "d.N" is decrypted text. ### # mode 1 ../../onetime -C dot-onetime -e -p ../test-pad-2 -o e.1 ../test-plaintext-a ../../onetime -C dot-onetime -d -p ../test-pad-2 -o d.1 e.1 # mode 2 ../../onetime -C dot-onetime -e -p ../test-pad-2 ../test-plaintext-a mv ../test-plaintext-a.onetime e.2.onetime ../../onetime -C dot-onetime -d -p ../test-pad-2 e.2.onetime mv e.2 d.2 # mode 3 ../../onetime -C dot-onetime -e -p ../test-pad-2 -o - ../test-plaintext-a > e.3 ../../onetime -C dot-onetime -d -p ../test-pad-2 -o - e.3 > d.3 # mode 4 ../../onetime -C dot-onetime -e -p ../test-pad-2 < ../test-plaintext-a > e.4 ../../onetime -C dot-onetime -d -p ../test-pad-2 < e.4 > d.4 # mode 5 ../../onetime -C dot-onetime -e -p ../test-pad-2 -o e.5 < ../test-plaintext-a ../../onetime -C dot-onetime -d -p ../test-pad-2 -o d.5 < e.5 for n in 1 2 3 4 5; do if cmp ../test-plaintext-a d.${n}; then true else echo "" echo "ERROR: one or more of the usage modes failed" PASSED="no" fi done check_result ######################################################################## start_new_test "failed decryption should give an error and create no output" ../../onetime --config=blank-dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-b-1 < ../test-plaintext-b ../../onetime --config=blank-dot-onetime -d -p ../test-pad-2 \ -o tmp-plaintext-b-1 tmp-ciphertext-b-1 2>err.out if ! grep -q "InnerFormat: unknown inner format version" err.out then echo "" echo "ERROR: did not see expected error on failed decryption" cat err.out PASSED="no" fi if [ -f tmp-plaintext-b-1 ] then echo "" echo "ERROR: output file left still created on failed decryption" PASSED="no" fi check_result ######################################################################## start_new_test "decryption should not shrink pad usage" # User sent in a report: # # $ onetime.py -e -p onetimepad.dat test1.txt # ==> pad-records says used length now 27340 # $ onetime.py -e -p onetimepad.dat test2.txt # ==> pad-records says used length now 54680 # $ onetime.py -e -p onetimepad.dat test3.txt # ==> pad-records says used length now 82020 # # (Now watch what happens on decryption...) # # $ onetime.py -d -p onetimepad.dat test1.txt.onetime # ==> pad-records says length reverted to 27340! ../../onetime -C dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-b-1.onetime ../test-plaintext-b if ! grep -q "12555" dot-onetime/pad-records; then echo "" echo "ERROR: Pad usage length is not 12555 after encryption iteration 1." cat dot-onetime/pad-records PASSED="no" fi ../../onetime -C dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-b-2.onetime ../test-plaintext-b if ! grep -q "25250" dot-onetime/pad-records; then echo "" echo "ERROR: Pad usage length is not 25250 after encryption iteration 2." cat dot-onetime/pad-records PASSED="no" fi ../../onetime -C dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-b-3.onetime ../test-plaintext-b if ! grep -q "37873" dot-onetime/pad-records; then echo "" echo "ERROR: Pad usage length is not 37873 after encryption iteration 3." cat dot-onetime/pad-records PASSED="no" fi ../../onetime -C dot-onetime -d -p ../test-pad-1 \ -o tmp-plaintext-b-1 tmp-ciphertext-b-1.onetime if ! grep -q "37873" dot-onetime/pad-records; then cat dot-onetime/pad-records if grep -q "12555" dot-onetime/pad-records; then # Note that as long as everything is working, this case will not # be triggered in normal test suite runs even if the length that # *would* indicate the return of this bug has changed from 12523 # to something else due to normal development progress. So if # we're inside the larger failure already, check carefully to see # whether we should actually be in this case and were just # expecting the wrong number. echo "" echo "ERROR: 'Decryption wrongly shrinks pad usage' bug is back." else echo "" echo "ERROR: Pad usage length is not 37873 after decryption 1, but don't know why." fi PASSED="no" fi if ! cmp tmp-plaintext-b-1 ../test-plaintext-b; then echo "" echo "ERROR: Decryption failed to produce correct plaintext." PASSED="no" fi check_result ######################################################################## start_new_test "decryption should record same pad usage as encryption" # If Alice encrypts, resulting in pad usage range R for that pad, then # if Bob decrypts starting from the same pad record state that Alice # had started with, the Bob's post-decryption pad record state should # be exactly the same as Alice's post-encryption pad record state. cp -a dot-onetime e-dot-onetime # separate encryption copy cp -a dot-onetime d-dot-onetime # separate decryption copy ../../onetime -C e-dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-b-1 ../test-plaintext-b ../../onetime -C d-dot-onetime -d -p ../test-pad-1 \ -o tmp-plaintext-b-1 tmp-ciphertext-b-1 if ! grep -q "12555" e-dot-onetime/pad-records; then grep "" e-dot-onetime/pad-records echo "" echo "ERROR: expected pad usage length of 12555 for encryption" PASSED="no" fi if ! grep -q "12555" d-dot-onetime/pad-records; then if grep -q "12265" d-dot-onetime/pad-records; then # Note that as long as everything is working, this case will not # be triggered in normal test suite runs even if the length that # *would* indicate the return of this bug has changed from 12265 # to something else due to normal development progress. So if # we're inside the larger failure already, check carefully to see # whether we should actually be in this case and were just # expecting the wrong number. echo "" echo "ERROR: tail fuzz authn isn't being counted in pad usage record" else grep "" d-dot-onetime/pad-records echo "" echo "ERROR: pad usage length is not 12555 after decryption, for some new reason" fi PASSED="no" fi check_result ######################################################################## start_new_test "test reconsumption via repeated encoding and decoding" # Debugging helper function. Deactivated by default -- change 'false' # to 'true' to turn this on. # Print the (string) first argument, then display all pad lengths. maybe_show_lengths() { if false; then echo ${1} grep "/length" dot-onetime/pad-records echo "" fi } # Encode ../../onetime --config=dot-onetime -e -p ../test-pad-1 \ < ../test-plaintext-a > tmp-ciphertext-a.onetime maybe_show_lengths "After encoding:" # Decode twice, to make sure the pad can reconsume safely. ../../onetime --config=dot-onetime -d -p ../test-pad-1 \ < tmp-ciphertext-a.onetime > tmp-plaintext-a.decoded-1 maybe_show_lengths "After decoding once:" if ! cmp ../test-plaintext-a tmp-plaintext-a.decoded-1; then echo "" echo "ERROR: tmp-plaintext-a.decoded-1 does not match test-plaintext-a input." PASSED="no" fi ../../onetime --config=dot-onetime -d -p ../test-pad-1 \ < tmp-ciphertext-a.onetime > tmp-plaintext-a.decoded-2 maybe_show_lengths "After decoding again:" if ! cmp ../test-plaintext-a tmp-plaintext-a.decoded-2; then echo "" echo "ERROR: tmp-plaintext-a.decoded-2 does not match test-plaintext-a input." PASSED="no" fi # Encode again with the same pad ../../onetime --config=dot-onetime -e -p ../test-pad-1 \ < ../test-plaintext-a > tmp-ciphertext-a.onetime maybe_show_lengths "After encoding again:" # Decode only once this time. ../../onetime --config=dot-onetime -d -p ../test-pad-1 \ < tmp-ciphertext-a.onetime > tmp-plaintext-a.decoded-3 maybe_show_lengths "After decoding:" if ! cmp ../test-plaintext-a tmp-plaintext-a.decoded-3; then echo "" echo "ERROR: tmp-plaintext-a.decoded-3 does not match test-plaintext-a input." PASSED="no" fi # Now do the entire thing again with the other pad. # Encode ../../onetime --config=dot-onetime -e -p ../test-pad-2 \ < ../test-plaintext-a > tmp-ciphertext-a.onetime maybe_show_lengths "After encoding:" # Decode twice, to make sure the pad can reconsume safely. ../../onetime --config=dot-onetime -d -p ../test-pad-2 \ < tmp-ciphertext-a.onetime > tmp-plaintext-a.decoded-1 maybe_show_lengths "After decoding once:" if ! cmp ../test-plaintext-a tmp-plaintext-a.decoded-1; then echo "" echo "ERROR: tmp-plaintext-a.decoded-1 (pad test-pad-2) does not match test-plaintext-a input." PASSED="no" fi ../../onetime --config=dot-onetime -d -p ../test-pad-2 \ < tmp-ciphertext-a.onetime > tmp-plaintext-a.decoded-2 maybe_show_lengths "After decoding again:" if ! cmp ../test-plaintext-a tmp-plaintext-a.decoded-2; then echo "" echo "ERROR: tmp-plaintext-a.decoded-2 (pad test-pad-2) does not match test-plaintext-a input." PASSED="no" fi # Encode again with the same pad ../../onetime --config=dot-onetime -e -p ../test-pad-2 \ < ../test-plaintext-a > tmp-ciphertext-a.onetime maybe_show_lengths "After encoding again:" # Decode only once this time. ../../onetime --config=dot-onetime -d -p ../test-pad-2 \ < tmp-ciphertext-a.onetime > tmp-plaintext-a.decoded-3 maybe_show_lengths "After decoding:" if ! cmp ../test-plaintext-a tmp-plaintext-a.decoded-3; then echo "" echo "ERROR: tmp-plaintext-a.decoded-3 (pad test-pad-2) does not match test-plaintext-a input." PASSED="no" fi check_result ######################################################################## start_new_test "make sure '--show-id' shows everything it should" ##### ## Check that both v2 and v1 pad IDs are displayed with --show-id. if ! ../../onetime --config=blank-dot-onetime --show-id -p ../test-pad-1 \ | grep -q ${TEST_PAD_1_ID} then echo "" echo "ERROR: --show-id -p test-pad-1 failed to display ID" PASSED="no" fi if ! ../../onetime --config=blank-dot-onetime --show-id -p ../test-pad-1 \ | grep -q " ${TEST_PAD_1_V1_ID}" then echo "" echo "ERROR: --show-id -p test-pad-1 failed to display v2 ID" PASSED="no" fi check_result ######################################################################## start_new_test "same plaintext should encrypt smaller with v2+ than with v1" ## Encrypt message, compare against same message encrypted with v1. ## Result: the more recent ciphertext should be noticeably smaller. ../../onetime -n --config=blank-dot-onetime -e -p ../test-pad-1 \ -o test-ciphertext-b-1 ../test-plaintext-b BYTES_NOW=`wc -c test-ciphertext-b-1 | cut -d " " -f1` BYTES_V1=`wc -c ../test-v1-ciphertext-b-1 | cut -d " " -f1` if [ ${BYTES_NOW} -ge ${BYTES_V1} ] then echo "" echo "ERROR: new ciphertext is bigger than v1 encryption of same plaintext" PASSED="no" fi check_result ######################################################################## start_new_test "decode v1 msg, where v1 entry has range already used" ## Receive v1 msg M, have v1 pad-records file with pad entry for M's ## pad, and that stretch of pad already marked as used. Decode msg. ## Result: upgraded pad ID, everything else stays same. ../../onetime --config=v1-dot-onetime -d -p ../test-pad-1 \ < ../test-v1-ciphertext-offset-0-a-1 > tmp-plaintext-a if ! cmp ../test-plaintext-a tmp-plaintext-a; then echo "" echo "ERROR: tmp-plaintext-a does not match original plaintext." PASSED="no" fi rm tmp-plaintext-a if ! grep -q "${TEST_PAD_1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input failed to upgrade pad ID in pad-records" PASSED="no" fi if grep -q "${TEST_PAD_1_V1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input failed to remove v1 pad ID from pad-records" PASSED="no" fi if ! grep -q "0" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input removed 0 offset from pad-records" PASSED="no" fi if ! grep -q "15" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input affected length 15 in pad-records" PASSED="no" fi if [ `grep -c "" v1-dot-onetime/pad-records` -gt 1 ] then echo "" echo "ERROR: decoding v1 input inserted spurious length into pad-records" PASSED="no" fi check_result ######################################################################## start_new_test "decode v1 msg, where v1 entry has range not already used" ## Receive v1 msg M, have v1 pad-records file with pad entry for M's ## pad, but this stretch of pad not marked as used. ## Result: upgraded pad ID, stretch marked as used. ../../onetime --config=v1-dot-onetime -d -p ../test-pad-1 \ < ../test-v1-ciphertext-offset-15-a-1 > tmp-plaintext-a if ! cmp ../test-plaintext-a tmp-plaintext-a; then echo "" echo "ERROR: tmp-plaintext-a does not match original plaintext." PASSED="no" fi rm tmp-plaintext-a if ! grep -q "${TEST_PAD_1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input failed to upgrade pad ID in pad-records" PASSED="no" fi if grep -q "${TEST_PAD_1_V1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input failed to remove v1 pad ID from pad-records" PASSED="no" fi if ! grep -q "0" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input removed 0 offset from pad-records" PASSED="no" fi if grep -q "15" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input left length 15 still in pad-records" PASSED="no" fi if ! grep -q "30" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input failed to put length 30 in pad-records" PASSED="no" fi if [ `grep -c "" v1-dot-onetime/pad-records` -gt 1 ] then echo "" echo "ERROR: decoding v1 input inserted spurious length into pad-records" PASSED="no" fi check_result ######################################################################## start_new_test "decode v2 msg, where v1 entry has range already used" ## Receive v2 msg M, have v1 pad-records file with pad entry for M's ## pad and that stretch of pad already marked as used. ## Result: upgraded pad ID, everything else stays same. # Create the ciphertext, leaving no trace (this is test prep only). ../../onetime -n --config=blank-dot-onetime -e -p ../test-pad-1 \ < ../test-plaintext-a > tmp-ciphertext-a-1 # Manually tweak the v1 pad-records file to claim that range is # already used, since otherwise it's a bit tough (using v2 onetime) to # get that result, given that v2 would automatically upgrade the # record's ID along with the range, and the whole point here is that # we want to test that the ID can get upgraded without the range being # affected. sed -e 's|15|512|' \ < v1-dot-onetime/pad-records > v1-dot-onetime/TMP-pad-records mv v1-dot-onetime/TMP-pad-records v1-dot-onetime/pad-records # Decrypt the v2 file, updating the newly range-expanded v1 pad-records. ../../onetime --config=v1-dot-onetime -d -p ../test-pad-1 \ < tmp-ciphertext-a-1 > tmp-plaintext-a if ! cmp ../test-plaintext-a tmp-plaintext-a; then echo "" echo "ERROR: tmp-plaintext-a does not match original plaintext." PASSED="no" fi rm tmp-plaintext-a rm tmp-ciphertext-a-1 if ! grep -q "${TEST_PAD_1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input failed to upgrade pad ID in pad-records" PASSED="no" fi if grep -q "${TEST_PAD_1_V1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input failed to remove v1 pad ID from pad-records" PASSED="no" fi if ! grep -q "0" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input removed 0 offset from pad-records" PASSED="no" fi if ! grep -q "695" v1-dot-onetime/pad-records then echo "" cat v1-dot-onetime/pad-records echo "ERROR: decoding v2 input affected length 695 in pad-records" PASSED="no" fi if [ `grep -c "" v1-dot-onetime/pad-records` -gt 1 ] then echo "" echo "ERROR: decoding v2 input inserted spurious length into pad-records" PASSED="no" fi check_result ######################################################################## start_new_test "decode v2 msg, where v1 entry range needs stretching" ## Receive v2 msg M, have v1 pad-records file with pad entry for M's ## pad, but this stretch of pad not marked as used and starting within ## the highest current used range. ## Result: upgraded pad ID, stretch marked as used. # Create the ciphertext, leaving no trace (this is test prep only). ../../onetime -n --config=blank-dot-onetime -e -p ../test-pad-1 \ < ../test-plaintext-a > tmp-ciphertext-a-1 # Manually tweak the v1 pad-records file to put the already-used range # just above 32, which is the offset where our v2 ciphertext starts # using the pad. This gives us a chance to see if the range gets # stretched and replaced when we decrypt. sed -e 's|15|33|' \ < v1-dot-onetime/pad-records > v1-dot-onetime/TMP-pad-records mv v1-dot-onetime/TMP-pad-records v1-dot-onetime/pad-records # Decrypt the v2 file, updating the newly range-expanded v1 pad-records. ../../onetime --config=v1-dot-onetime -d -p ../test-pad-1 \ < tmp-ciphertext-a-1 > tmp-plaintext-a if ! cmp ../test-plaintext-a tmp-plaintext-a; then echo "" echo "ERROR: tmp-plaintext-a does not match original plaintext." PASSED="no" fi rm tmp-plaintext-a rm tmp-ciphertext-a-1 if ! grep -q "${TEST_PAD_1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input failed to upgrade pad ID in pad-records" PASSED="no" fi if grep -q "${TEST_PAD_1_V1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input failed to remove v1 pad ID from pad-records" PASSED="no" fi if ! grep -q "0" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input removed 0 offset from pad-records" PASSED="no" fi if ! grep -q "695" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input failed to use length 695 in pad-records" cat v1-dot-onetime/pad-records PASSED="no" fi if [ `grep -c "" v1-dot-onetime/pad-records` -gt 1 ] then echo "" echo "ERROR: decoding v2 input inserted spurious length into pad-records" PASSED="no" fi check_result ######################################################################## start_new_test "decode v2 msg, where v1 entry needs new range" ## Receive v2 msg M, have v1 pad-records file with pad entry for M's ## pad, but this stretch of pad not marked as used and starting ## after the end of the highest current used range. ## Result: upgraded pad ID and new range added. # Create the ciphertext, leaving no trace (this is test prep only). ../../onetime -n --config=blank-dot-onetime -e -p ../test-pad-1 \ < ../test-plaintext-a > tmp-ciphertext-a-1 # Decrypt the v2 file, updating the newly range-expanded v1 pad-records. ../../onetime --config=v1-dot-onetime -d -p ../test-pad-1 \ < tmp-ciphertext-a-1 > tmp-plaintext-a if ! cmp ../test-plaintext-a tmp-plaintext-a; then echo "" echo "ERROR: tmp-plaintext-a does not match original plaintext." PASSED="no" fi rm tmp-plaintext-a rm tmp-ciphertext-a-1 if ! grep -q "${TEST_PAD_1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input failed to upgrade pad ID in pad-records" PASSED="no" fi if grep -q "${TEST_PAD_1_V1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input failed to remove v1 pad ID from pad-records" PASSED="no" fi if ! grep -q "0" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input removed 0 offset from pad-records" PASSED="no" fi if ! grep -q "15" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input removed length 15 from pad-records" PASSED="no" fi if ! grep -q "32" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input failed to add offset 32 to pad-records" PASSED="no" fi if ! grep -q "663" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v2 input failed to add length 663 to pad-records" cat v1-dot-onetime/pad-records PASSED="no" fi if [ `grep -c "" v1-dot-onetime/pad-records` -gt 2 ] then echo "" echo "ERROR: decoding v2 input inserted spurious length into pad-records" PASSED="no" fi check_result ######################################################################## start_new_test "decode v1 msg, where no entry in pad-records at all" ## Receive v1 msg M, have no entry in pad-records file for M's pad. ## Result: new v2 entry ../../onetime --config=v1-dot-onetime -d -p ../test-pad-2 \ < ../test-v1-ciphertext-b-2 > tmp-plaintext-b if ! cmp ../test-plaintext-b tmp-plaintext-b; then echo "" echo "ERROR: tmp-plaintext-b does not match original plaintext." PASSED="no" fi rm tmp-plaintext-b if ! grep -q "${TEST_PAD_2_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input failed to insert pad ID into pad-records" PASSED="no" fi if grep -q "${TEST_PAD_2_V1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input somehow inserted v1 pad ID into pad-records" PASSED="no" fi if ! grep -q "0" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input removed 0 offset from pad-records" PASSED="no" fi # Expect the new length on the 10th line, which is in the second pad entry. # But first test to make sure it's there at all; otherwise grep -n outputs # nothing and the conditional gets harder to write. if ! grep -q "45541" v1-dot-onetime/pad-records then echo "" echo "ERROR: decoding v1 input failed to insert new 45541 record" PASSED="no" elif grep -q "45541" v1-dot-onetime/pad-records && \ [ `grep -n "45541" v1-dot-onetime/pad-records \ | cut -d ":" -f 1` -ne 10 ] then echo "" echo "ERROR: decoding v1 input mis-inserted new 45541 record into pad-records" PASSED="no" fi check_result ######################################################################## start_new_test "encode msg, where v1 pad entry has some range already used" ## Encrypt message, have v1 pad-records file with entry for pad used. ## Result: pad entry should be upgraded, with stretch now marked used. ../../onetime --config=v1-dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-b-1 < ../test-plaintext-b # Toss the encryption, as it's not what we're testing here. rm tmp-ciphertext-b-1 if ! grep -q "${TEST_PAD_1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: encoding failed to upgrade v1 pad ID in pad-records" PASSED="no" fi if grep -q "${TEST_PAD_1_V1_ID}" v1-dot-onetime/pad-records then echo "" echo "ERROR: encoding failed to remove v1 pad ID from pad-records" PASSED="no" fi if ! grep -q "0" v1-dot-onetime/pad-records then echo "" echo "ERROR: encoding removed 0 offset from v1 entry in pad-records" PASSED="no" fi # Expect the new offset on the 6th line, in second range of first entry. # But first test to make sure it's there at all; otherwise grep -n outputs # nothing and the conditional gets harder to write. if ! grep -q "32" v1-dot-onetime/pad-records then echo "" echo "ERROR: encoding failed to insert new offset 32 into pad-records" PASSED="no" elif grep -q "32" v1-dot-onetime/pad-records && \ [ `grep -n "32" v1-dot-onetime/pad-records \ | cut -d ":" -f 1` -ne 6 ] then echo "" echo "ERROR: encoding mis-inserted new offset into pad-records" PASSED="no" fi # Expect the new length on the 7th line, in second range of first entry. if ! grep -q "12659" v1-dot-onetime/pad-records then echo "" echo "ERROR: failed to insert new length 12659 into pad-records" cat v1-dot-onetime/pad-records PASSED="no" elif grep -q "12659" v1-dot-onetime/pad-records && \ [ `grep -n "12659" v1-dot-onetime/pad-records \ | cut -d ":" -f 1` -ne 7 ] then echo "" echo "ERROR: encoding mis-inserted new length 12659 into pad-records" PASSED="no" fi check_result ######################################################################## start_new_test "decode msg, erroring because garbage after base64 data" ## Encrypt message ../../onetime --config=blank-dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-b-1 < ../test-plaintext-b sed -e 's/-----END OneTime MESSAGE-----/ \n-----END OneTime MESSAGE-----/' \ < tmp-ciphertext-b-1 > tmp-ciphertext-b-1.damaged ../../onetime --config=blank-dot-onetime -d -p ../test-pad-1 \ -o tmp-plaintext-b-1 tmp-ciphertext-b-1.damaged 2>err.out if ! grep -q "DecodingError: unexpected input" err.out then echo "" echo "ERROR: decoder failed to detect trailing garbage in input stream" PASSED="no" fi check_result ######################################################################## start_new_test "tampered head fuzz is detected, but decryption succeeds" # This is actually a questionable "feature", really more an artifact # of where the head fuzz digest is located in the stream than of # deliberate UX design. Do we really want decryption to succeed if # the head fuzz digest failed? But to do otherwise, we'd need to put # the digest before the message text section. ## Encrypt message ../../onetime --test-mode \ --config=blank-dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-b-1 < ../test-plaintext-b 2>err.out # In the base64-encoded ciphertext file, position 286 is 'v' (118). ../zap tmp-ciphertext-b-1 286 118 119 # tweak 'v' to 'w' ../../onetime --config=blank-dot-onetime -d -p ../test-pad-1 \ -o tmp-plaintext-b-1 < tmp-ciphertext-b-1 2>err.out if ! grep -q "DigestMismatch: digest mismatch:" err.out || \ ! grep -q " computed: fa258423e6c7559a529905a001d2e4c32ba322a73f5e867589025b1eeb91b220" err.out || \ ! grep -q " received: 9581b039c3902390984fc98ef38f0ea1e44fb5a401cc0eb4a237b6777ffe0bb5" err.out then echo "" echo "ERROR: did not see expected DigestMismatch error from tampered head fuzz" cat err.out PASSED="no" fi if ! cmp ../test-plaintext-b tmp-plaintext-b-1 then echo "" echo "ERROR: decryption unexpectedly failed when head fuzz tampered with" head tmp-plaintext-b-1 echo "[...etc...]" PASSED="no" fi check_result ######################################################################## start_new_test "tampering with ciphertext causes bzip decoder error" ## Encrypt message ../../onetime --config=blank-dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-b-1 < ../test-plaintext-b 2>err.out # In the base64-encoded ciphertext file, position 8563 is 'h' (104). ../zap tmp-ciphertext-b-1 8563 104 105 # tweak 'h' to 'i' ../../onetime --config=blank-dot-onetime -d -p ../test-pad-1 \ < tmp-ciphertext-b-1 2>err.out if ! grep -q "IOError: invalid data stream" err.out then echo "" echo "ERROR: did not see expected IOerror from bzip decoder" cat err.out PASSED="no" fi check_result ######################################################################## start_new_test "basic encryption/decryption with all-nulls plaintext" ## There was no actual regression that motivated this test. It's just ## that head and tail fuzz are both raw pad, i.e., null bytes ## encrypted against pad, so out of a sense of dutiful paranoia we ## should make sure that decryptions aren't accidentally looking like ## they're succeeding just because in some weird environment maybe ## the programs we use to check the output stop on the first null byte. ## Also, it's just good practice in general to verify that a tool that ## is supposed to work transparently with binary data works with large ## all-nulls input -- if there's any problem handling binary data, ## that input is likely to stimulate it. ../../onetime --config=blank-dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-all-nulls ../all-nulls ../../onetime --config=blank-dot-onetime -d -p ../test-pad-1 \ -o tmp-plaintext-all-nulls tmp-ciphertext-all-nulls if ! cmp tmp-plaintext-all-nulls ../all-nulls then echo "" echo "ERROR: decrypted all-nulls plaintext does not match original" PASSED="no" fi check_result ######################################################################## start_new_test "tampering with tail fuzz should have no effect" ## Encrypt message ../../onetime --config=blank-dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-b-1 < ../test-plaintext-b 2>err.out ../zap tmp-ciphertext-b-1 17110 ? 52 ../../onetime --config=blank-dot-onetime -d -p ../test-pad-1 \ -o tmp-plaintext-b-1 < tmp-ciphertext-b-1 2>err.out if grep -q "FuzzMismatch" err.out then echo "" echo "ERROR: saw unexpected FuzzMismatch error on tampered tail fuzz" cat err.out PASSED="no" fi if ! cmp ../test-plaintext-b tmp-plaintext-b-1 then echo "" echo "ERROR: decryption failed when tail fuzz tampered with" cat tmp-plaintext-b-1 PASSED="no" fi check_result ######################################################################## start_new_test "basic encryption/decryption with zero-length tail fuzz" ## If the head-fuzz or tail-fuzz source bytes are all zeros, ## everything should still work fine. There was briefly a bug where ## we assumed that tail-fuzz always had non-zero length, but that's ## fixed now and this is the regression test for it. Note that no one ## would ever use an all-nulls pad in practice, of course; it's just a ## convenient way to guarantee that the fuzz lengths are zero. ../../onetime --config=blank-dot-onetime -e -p ../all-nulls \ -o tmp-ciphertext-a-1 ../test-plaintext-a ../../onetime --config=blank-dot-onetime -d -p ../all-nulls \ -o tmp-plaintext-a tmp-ciphertext-a-1 2>err.out if grep -q "FuzzMismatch: some source tail fuzz left over" err.out then echo "" echo "ERROR: unable to handle zero-length tail fuzz" cat err.out PASSED="no" fi if ! cmp tmp-plaintext-a ../test-plaintext-a then echo "" echo "ERROR: unexpected decryption failure with all-nulls pad" PASSED="no" fi check_result ######################################################################## start_new_test "tampering with message digest causes authentication error" ## Encrypt message ../../onetime --test-mode \ --config=blank-dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-a-1 < ../test-plaintext-a 2>err.out # In the base64-encoded ciphertext file, position 824 is 'a' (97). ../zap tmp-ciphertext-a-1 824 97 98 # tweaking 'a' to 'b' ../../onetime --config=blank-dot-onetime -d -p ../test-pad-1 \ -o tmp-plaintext-a-1 < tmp-ciphertext-a-1 2>err.out if ! grep -q "DigestMismatch: digest mismatch:" err.out || \ ! grep -q " computed: b0b8c3001080d56ace90280f2cebc389e9b7df5a31064aaf1ca26f80a92f3889" err.out || \ ! grep -q " received: b0b8c3001080d56ace90280f2cabc389e9b7df5a31064aaf1ca26f80a92f3889" err.out # here is where they differ -------------------> ^ then echo "" echo "ERROR: did not see expected DigestMismatch error (30c7cd...)" cat err.out PASSED="no" fi if ! cmp ../test-plaintext-a tmp-plaintext-a-1 then echo "" echo "ERROR: decryption failed when digest tampered with" cat tmp-plaintext-a-1 echo "" PASSED="no" fi check_result ######################################################################## start_new_test "tampering with head fuzz causes authentication error" ## Encrypt message ../../onetime --test-mode \ --config=blank-dot-onetime -e -p ../test-pad-1 \ -o tmp-ciphertext-a-1 < ../test-plaintext-a 2>err.out # In the base64-encoded ciphertext file, position 221 is 'p' (). ../zap tmp-ciphertext-a-1 221 112 113 # tweak 'p' to 'q' ../../onetime --config=blank-dot-onetime -d -p ../test-pad-1 \ -o tmp-plaintext-a-1 < tmp-ciphertext-a-1 2>err.out if ! grep -q "DigestMismatch: digest mismatch:" err.out || \ ! grep -q " computed: 7a177e60428451ac87eb9eb59c9b37e26b7061e5ef36ddf316333dc6baf821a8" err.out || \ ! grep -q " received: b0b8c3001080d56ace90280f2cebc389e9b7df5a31064aaf1ca26f80a92f3889" err.out then echo "" echo "ERROR: did not see expected DigestMismatch error (7a177e vs b0b8c3)" cat err.out PASSED="no" fi if ! cmp ../test-plaintext-a tmp-plaintext-a-1 then echo "" echo "ERROR: decryption failed when head fuzz tampered with" cat tmp-plaintext-a-1 echo "" PASSED="no" fi check_result ############################################################################ ### All tests finished. Leave the test area in place for inspection. ### ############################################################################ cd ../..