-O pid: show/log process id of running tfcrypt.
[tfcrypt.git] / tfcrypt.c
index 125e5c9c3743e56c0ad01b6ef15a6bf931070730..dd137ae6ba4d9ac26d05fd5596bee5c4e9d22d6e 100644 (file)
--- a/tfcrypt.c
+++ b/tfcrypt.c
@@ -2,7 +2,7 @@
  * tfcrypt -- high security Threefish encryption tool.
  *
  * tfcrypt is copyrighted:
- * Copyright (C) 2012-2018 Andrey Rys. All rights reserved.
+ * Copyright (C) 2012-2019 Andrey Rys. All rights reserved.
  *
  * tfcrypt is licensed to you under the terms of std. MIT/X11 license:
  *
 
 #include "tfcrypt.h"
 
+static tfc_byte svctr[TF_BLOCK_SIZE];
+
+static void open_log(const char *logfile)
+{
+       int fd;
+
+       if (!strcmp(logfile, "-")) return;
+
+       fd = open(logfile, O_WRONLY | O_CREAT | O_LARGEFILE | O_TRUNC, 0666);
+       if (fd == -1) xerror(NO, NO, YES, "%s", logfile);
+       xclose(2);
+       if (dup2(fd, 2) == -1) xexit(2);
+       xclose(fd);
+       do_statline_dynamic = NO;
+}
+
 static int getps_filter(struct getpasswd_state *getps, char chr, size_t pos)
 {
        if (chr == '\x03') {
@@ -83,13 +99,39 @@ static int getps_plain_hex_filter(struct getpasswd_state *getps, char chr, size_
        return 1;
 }
 
+static void make_hint(void *hint, size_t szhint, const void *data, size_t szdata)
+{
+       char t[TF_FROM_BITS(TF_MAX_BITS)];
+
+       skein(t, TF_MAX_BITS, NULL, data, szdata);
+       xor_shrink(hint, szhint, t, sizeof(t));
+       memset(t, 0, sizeof(t));
+}
+
+static void raw_say_hint(void *hint, size_t szhint, const void *data, size_t szdata, const char *prompt)
+{
+       make_hint(hint, szhint, data, szdata);
+       if (prompt) tfc_nfsay(stderr, "%s: ", prompt);
+       mehexdump(hint, szhint, szhint, 1);
+       memset(hint, 0, szhint);
+}
+
+static void say_hint(const void *data, size_t szdata, const char *prompt)
+{
+       char t[TF_SIZE_UNIT];
+       raw_say_hint(t, TF_SIZE_UNIT, data, szdata, prompt);
+       /* t[] is erased (automatically) */
+}
+
 int main(int argc, char **argv)
 {
        int c;
        double td;
        char *s, *d, *t, *stoi;
        size_t x, n;
+       tfc_fsize rwd;
 
+       progpid = getpid();
        progname = basename(argv[0]);
 
        if (!isatty(2)) do_statline_dynamic = NO;
@@ -104,8 +146,14 @@ int main(int argc, char **argv)
 _baddfname:
        memset(s, 0, n);
 
+       if (!strcmp(progname, "iotool")) {
+               do_edcrypt = TFC_DO_PLAIN;
+               password = YES;
+               ctr_mode = TFC_MODE_PLAIN;
+       }
+
        opterr = 0;
-       while ((c = getopt(argc, argv, "L:s:aU:C:r:K:t:Pkzxc:l:qedn:vV:pwE:O:S:AmM:R:Z:WHD:")) != -1) {
+       while ((c = getopt(argc, argv, "L:s:aU:C:r:K:t:Pkzxc:l:qedn:vV:pwE:o:O:S:AmuM:R:Z:WHD:")) != -1) {
                switch (c) {
                        case 'L':
                                read_defaults(optarg, NO);
@@ -125,6 +173,29 @@ _baddfname:
                                        counter_opt = TFC_CTR_RAND;
                                else if (!strcasecmp(optarg, "zero"))
                                        counter_opt = TFC_CTR_ZERO;
+                               else if (strchr(optarg, ':')) {
+                                       char *ss, chr;
+
+                                       counter_opt = TFC_CTR_SSET;
+                                       n = sizeof(ctr);
+
+                                       s = d = optarg; t = NULL;
+                                       while ((s = strtok_r(d, ",", &t))) {
+                                               if (d) d = NULL;
+
+                                               if (n == 0) break;
+                                               ss = strchr(s, ':');
+                                               if (!ss) continue;
+                                               *ss = 0; ss++;
+                                               chr = (char)strtoul(s, &stoi, 16);
+                                               if (!str_empty(stoi)) continue;
+                                               x = (size_t)strtoul(ss, &stoi, 10);
+                                               if (!str_empty(stoi)) continue;
+                                               if (x > n) x = n;
+                                               memset(ctr+(sizeof(ctr)-n), (int)chr, x);
+                                               n -= x;
+                                       }
+                               }
                                else counter_file = sksum_hashlist_file = optarg;
                                break;
                        case 'C':
@@ -148,10 +219,10 @@ _baddfname:
                                ctr_mode = TFC_MODE_PLAIN;
                                break;
                        case 'e':
-                               do_edcrypt = TFC_DO_ENCRYPT;
+                               if (do_edcrypt != TFC_DO_PLAIN) do_edcrypt = TFC_DO_ENCRYPT;
                                break;
                        case 'd':
-                               do_edcrypt = TFC_DO_DECRYPT;
+                               if (do_edcrypt != TFC_DO_PLAIN) do_edcrypt = TFC_DO_DECRYPT;
                                break;
                        case 'D':
                                macbits = strtoul(optarg, &stoi, 10);
@@ -229,6 +300,9 @@ _baddfname:
                                        error_action = TFC_ERRACT_LSYNC;
                                else xerror(NO, YES, YES, "invalid error action %s specified", optarg);
                                break;
+                       case 'o':
+                               open_log(optarg);
+                               break;
                        case 'O':
                                s = d = optarg; t = NULL;
                                while ((s = strtok_r(d, ",", &t))) {
@@ -261,6 +335,12 @@ _baddfname:
                                                do_full_key = YES;
                                        else if (!strcmp(s, "showsecrets"))
                                                show_secrets = YES;
+                                       else if (!strcmp(s, "finished"))
+                                               show_when_done = YES;
+                                       else if (!strcmp(s, "pid"))
+                                               show_pid = YES;
+                                       else if (!strncmp(s, "logfile", 7) && *(s+7) == '=')
+                                               open_log(s+8);
                                        else if (!strncmp(s, "iobs", 4) && *(s+4) == '=') {
                                                s += 5;
                                                blksize = (size_t)tfc_humanfsize(s, &stoi);
@@ -376,6 +456,24 @@ _baddfname:
                                                if (counter_opt == TFC_CTR_HEAD)
                                                        maxlen += TF_BLOCK_SIZE;
                                        }
+                                       else if (!strncmp(s, "ftrunc", 6) && *(s+6) == '=') {
+                                               s += 7;
+                                               if (!strcmp(s, "tail")) {
+                                                       do_ftrunc = TFC_FTRUNC_TAIL;
+                                                       ftrunc_dfd = NOFSIZE;
+                                               }
+                                               else {
+                                                       do_ftrunc = TFC_DO_FTRUNC;
+                                                       ftrunc_dfd = tfc_humanfsize(s, &stoi);
+                                                       if (!str_empty(stoi)) {
+                                                               ftrunc_dfd = tfc_fnamesize(s, YES);
+                                                               ftrunc_dfd = tfc_modifysize(ftrunc_dfd, strchr(s, ':'));
+                                                               if (ftrunc_dfd == NOFSIZE) xerror(NO, YES, YES,
+                                                               "%s: invalid ftrunc value", s);
+                                                       }
+                                                       else ftrunc_dfd = tfc_modifysize(ftrunc_dfd, strchr(s, ':'));
+                                               }
+                                       }
                                        else if (!strncmp(s, "xkey", 4) && *(s+4) == '=') {
                                                s += 5;
                                                maxkeylen = tfc_humanfsize(s, &stoi);
@@ -430,9 +528,11 @@ _baddfname:
                                        do_mac_file = optarg;
                                break;
                        case 'm':
+                       case 'u':
                                if (do_mac != TFC_MAC_VRFY)
                                        xerror(NO, YES, YES, "signature source was not specified");
                                do_mac = TFC_MAC_JUST_VRFY;
+                               if (c == 'u') do_mac = TFC_MAC_JUST_VRFY2;
                                break;
                        case 'R':
                        case 'Z':
@@ -477,6 +577,7 @@ _baddfname:
                        case 'q':
                                quiet = YES;
                                verbose = NO;
+                               do_full_hexdump = NO;
                                status_timer = 0;
                                break;
                        case 'v':
@@ -569,7 +670,7 @@ _nosalt:
                        lrem = lblock = sizeof(tmpdata);
                        if (error_action == TFC_ERRACT_SYNC) rdpos = tfc_fdgetpos(mkfd);
 _mkragain:             lio = xread(mkfd, pblk, lrem);
-                       if (lio == 0) do_stop = YES;
+                       if (lio == 0 && do_stop == NO) do_stop = YES;
                        if (lio != NOSIZE) ldone += lio;
                        else {
                                if (errno != EIO && catch_all_errors != YES)
@@ -612,14 +713,8 @@ _mkragain:         lio = xread(mkfd, pblk, lrem);
                n = xgetpasswd(&getps);
                if (n == NOSIZE) xerror(NO, NO, YES, "getting MAC password");
                if (n == ((size_t)-2)) xexit(1);
+               if (verbose) say_hint(pwdask, n, "MAC password hint");
                skein(mackey, TF_MAX_BITS, NULL, pwdask, n);
-               if (verbose) {
-                       skein(tmpdata, TF_MAX_BITS, NULL, mackey, TF_FROM_BITS(TF_MAX_BITS));
-                       xor_shrink(tmpdata+TF_FROM_BITS(TF_MAX_BITS), TF_SIZE_UNIT, tmpdata, TF_FROM_BITS(TF_MAX_BITS));
-                       tfc_nfsay(stderr, "MAC password hint: ");
-                       mehexdump(tmpdata+TF_FROM_BITS(TF_MAX_BITS), TF_SIZE_UNIT, TF_SIZE_UNIT, 1);
-                       memset(tmpdata, 0, sizeof(tmpdata));
-               }
        }
 
        
@@ -675,7 +770,7 @@ _nokeyfd:
 
                if (do_edcrypt == TFC_DO_DECRYPT && do_mac != NO && maxlen != NOFSIZE) {
                        if (verbose) tfc_esay("%s: disabling signature verification on "
-                               "requested partial decryption.", progname);
+                               "requested partial decryption.", tfc_format_pid(progname));
                        do_mac = NO;
                }
 
@@ -750,7 +845,7 @@ _ctrskip1:
 
        if (ctr_mode == TFC_MODE_PLAIN) goto _plain;
 
-       if (verbose) tfc_esay("%s: hashing password", progname);
+       if (verbose) tfc_esay("%s: hashing password", tfc_format_pid(progname));
 
        if (rawkey == TFC_RAWKEY_KEYFILE) {
                tfc_yesno xtskeyset = NO;
@@ -807,6 +902,7 @@ _xts2keyaskstr:     memset(&getps, 0, sizeof(struct getpasswd_state));
                n = xgetpasswd(&getps);
                if (n == NOSIZE) xerror(NO, NO, YES, "getting string rawkey");
                if (n == ((size_t)-2)) xexit(1);
+               if (verbose) say_hint(pblk, n, "Raw string key hint");
                if (ctr_mode == TFC_MODE_XTS) {
                        if (xtskeyset == NO) {
                                pblk = xtskey; n = sizeof(xtskey);
@@ -837,6 +933,7 @@ _rawkey_hex_again:
                }
                hex2bin(pblk, pwdask);
                memset(pwdask, 0, sizeof(pwdask));
+               if (verbose) say_hint(pblk, n/2, "Raw hex key hint");
                if (ctr_mode == TFC_MODE_XTS) {
                        if (xtskeyset == NO) {
                                pblk = xtskey;
@@ -871,6 +968,7 @@ _pwdagain:  memset(&getps, 0, sizeof(struct getpasswd_state));
                                goto _pwdagain;
                        }
                }
+               if (verbose) say_hint(pwdask, n, "Password hint");
                skein(key, TFC_KEY_BITS, mackey_opt ? mackey : NULL, pwdask, n);
                memset(pwdask, 0, sizeof(pwdask));
                memset(pwdagain, 0, sizeof(pwdagain));
@@ -905,9 +1003,9 @@ _pwdagain: memset(&getps, 0, sizeof(struct getpasswd_state));
 _xts2genkey:   if (xwrite(krfd, pblk, TF_FROM_BITS(TFC_KEY_BITS)) == NOSIZE) xerror(NO, NO, YES, "%s", genkeyf);
                if (do_fsync && fsync(krfd) == -1) xerror(NO, NO, YES, "%s", genkeyf);
                if (verbose && xtskeyset == NO) {
-                       tfc_esay("%s: password hashing done", progname);
-                       tfc_esay("%s: rawkey written to %s.", progname, genkeyf);
-                       tfc_esay("%s: Have a nice day!", progname);
+                       tfc_esay("%s: password hashing done", tfc_format_pid(progname));
+                       tfc_esay("%s: rawkey written to %s.", tfc_format_pid(progname), genkeyf);
+                       tfc_esay("%s: Have a nice day!", tfc_format_pid(progname));
                }
 
                if (ctr_mode == TFC_MODE_XTS) {
@@ -925,7 +1023,7 @@ _xts2genkey:       if (xwrite(krfd, pblk, TF_FROM_BITS(TFC_KEY_BITS)) == NOSIZE) xerro
 
        if (iseek_blocks && (do_edcrypt == TFC_DO_DECRYPT && do_mac != NO)) {
                if (verbose) tfc_esay("%s: disabling signature verification on "
-                       "requested partial decryption.", progname);
+                       "requested partial decryption.", tfc_format_pid(progname));
                do_mac = NO;
        }
 
@@ -933,7 +1031,7 @@ _xts2genkey:       if (xwrite(krfd, pblk, TF_FROM_BITS(TFC_KEY_BITS)) == NOSIZE) xerro
                if (mackey_opt == TFC_MACKEY_RAWKEY) skein(mackey, TF_MAX_BITS, key, key, TF_FROM_BITS(TFC_KEY_BITS));
                if (ctr_mode < TFC_MODE_OCB) {
                        if (verbose) tfc_esay("%s: doing MAC calculation, processing speed "
-                               "will be slower.", progname);
+                               "will be slower.", tfc_format_pid(progname));
                        if (mackey_opt) skein_init_key(&sk, mackey, macbits);
                        else skein_init(&sk, macbits);
                }
@@ -950,27 +1048,28 @@ _xts2genkey:     if (xwrite(krfd, pblk, TF_FROM_BITS(TFC_KEY_BITS)) == NOSIZE) xerro
                tf_tweak_set(key, tweak);
        }
        if (ctr_mode == TFC_MODE_ECB) goto _ctrskip2;
+
+       if (counter_opt == TFC_CTR_ZERO) memset(ctr, 0, ctrsz);
+
        tfc_data_to_words64(&iseek_blocks, sizeof(iseek_blocks));
        tf_ctr_set(ctr, &iseek_blocks, sizeof(iseek_blocks));
+       if (do_mac == TFC_MAC_JUST_VRFY2) memcpy(svctr, ctr, TF_BLOCK_SIZE);
 
-       switch (counter_opt) {
-               case TFC_CTR_SHOW:
-                       switch (do_outfmt) {
-                               case TFC_OUTFMT_B64: tfc_printbase64(stderr, ctr, ctrsz, YES); break;
-                               case TFC_OUTFMT_RAW: xwrite(2, ctr, ctrsz); break;
-                               case TFC_OUTFMT_HEX: mehexdump(ctr, ctrsz, ctrsz, YES); break;
-                       }
-                       break;
-               case TFC_CTR_RAND: tfc_getrandom(ctr, ctrsz); break;
-               case TFC_CTR_ZERO: memset(ctr, 0, ctrsz); break;
+       if (counter_opt == TFC_CTR_SHOW) {
+               switch (do_outfmt) {
+                       case TFC_OUTFMT_B64: tfc_printbase64(stderr, ctr, ctrsz, YES); break;
+                       case TFC_OUTFMT_RAW: xwrite(2, ctr, ctrsz); break;
+                       case TFC_OUTFMT_HEX: mehexdump(ctr, ctrsz, ctrsz, YES); break;
+               }
        }
+       else if (counter_opt == TFC_CTR_RAND) tfc_getrandom(ctr, ctrsz);
 
 _ctrskip2:
        if (kfd != -1) {
                xclose(kfd);
                kfd = -1;
        }
-       if (verbose) tfc_esay("%s: password hashing done", progname);
+       if (verbose) tfc_esay("%s: password hashing done", tfc_format_pid(progname));
 
        if (overwrite_source && srcfname) argv[idx] = srcfname;
 
@@ -996,9 +1095,8 @@ _plain:
        sigact.sa_flags = SA_RESTART;
        sigact.sa_handler = print_crypt_status;
        sigaction(SIGUSR1, &sigact, NULL);
-       sigaction(SIGTSTP, &sigact, NULL);
        sigaction(SIGALRM, &sigact, NULL);
-       if (status_timer) setup_next_alarm(status_timer);
+       if (status_timer) setup_next_alarm(status_timer > 1000000 ? 1000000 : status_timer);
        sigact.sa_handler = change_status_width;
        sigaction(SIGQUIT, &sigact, NULL);
        sigact.sa_handler = change_status_timer;
@@ -1007,11 +1105,14 @@ _plain:
                sigact.sa_handler = print_crypt_status;
                sigaction(SIGINT, &sigact, NULL);
                sigaction(SIGTERM, &sigact, NULL);
+               sigaction(SIGTSTP, &sigact, NULL);
        }
        else {
                sigact.sa_handler = exit_sigterm;
                sigaction(SIGINT, &sigact, NULL);
                sigaction(SIGTERM, &sigact, NULL);
+               sigact.sa_handler = handle_sigtstp;
+               sigaction(SIGTSTP, &sigact, NULL);
        }
        memset(&sigact, 0, sizeof(struct sigaction));
 
@@ -1037,6 +1138,24 @@ _ctrwagain:      lio = xwrite(dfd, pblk, lrem);
 
        if (ctr_mode == TFC_MODE_STREAM) tfe_init_iv(&tfe, key, ctr);
 
+       if (do_mac == TFC_MAC_JUST_VRFY2) {
+               rwd = tfc_fdgetpos(sfd);
+               if (rwd == NOFSIZE) {
+                       tfc_esay("%s: WARNING: input is not seekable, disabling MAC testing mode", tfc_format_pid(progname));
+                       do_mac = TFC_MAC_VRFY;
+               }
+               goto _nodecrypt_again_vrfy2;
+
+_decrypt_again_vrfy2:
+               if (lseek(sfd, (off_t)rwd, SEEK_SET) == ((off_t)-1)) {
+                       xerror(ignore_seek_errors, NO, YES, "MAC testing seek failed");
+               }
+               total_processed_src = rwd;
+               memcpy(ctr, svctr, TF_BLOCK_SIZE);
+               memset(svctr, 0, TF_BLOCK_SIZE);
+       }
+
+_nodecrypt_again_vrfy2:
        errno = 0;
        do_stop = NO;
        while (1) {
@@ -1046,7 +1165,7 @@ _ctrwagain:       lio = xwrite(dfd, pblk, lrem);
                lrem = lblock = blk_len_adj(maxlen, total_processed_src, blksize);
                if (error_action == TFC_ERRACT_SYNC) rdpos = tfc_fdgetpos(sfd);
 _ragain:       lio = xread(sfd, pblk, lrem);
-               if (lio == 0) do_stop = TFC_STOP_BEGAN;
+               if (lio == 0) do_stop = YES;
                if (lio != NOSIZE) ldone += lio;
                else {
                        if (errno != EIO && catch_all_errors != YES)
@@ -1106,7 +1225,7 @@ _ragain:  lio = xread(sfd, pblk, lrem);
 
                if (do_mac >= TFC_MAC_VRFY && ctr_mode < TFC_MODE_OCB)
                        skein_update(&sk, dstblk, ldone);
-               if (do_mac == TFC_MAC_JUST_VRFY) goto _nowrite;
+               if (do_mac >= TFC_MAC_JUST_VRFY) goto _nowrite;
 
                pblk = dstblk;
                lrem = ldone;
@@ -1120,14 +1239,16 @@ _wagain:        lio = xwrite(dfd, pblk, lrem);
                        lrem -= lio;
                        goto _wagain;
                }
+               total_written_dst += ldone;
 _nowrite:      total_processed_dst += ldone;
                delta_processed += ldone;
 
-               if (maxlen != NOFSIZE && total_processed_src >= maxlen) break;
+               if (maxlen != NOFSIZE && total_processed_src >= maxlen) {
+                       do_stop = YES;
+                       break;
+               }
        }
 
-       if (do_stop == TFC_STOP_FULL) goto _nomac;
-
        errno = 0;
        if (do_mac >= TFC_MAC_VRFY) {
                if (!do_mac_file) {
@@ -1180,7 +1301,7 @@ _macragain:               lio = xread(sfd, pblk, lrem);
 
                if (ldone < TF_FROM_BITS(macbits)) {
                        if (quiet == NO) tfc_esay("%s: short signature (%zu), "
-                               "not verifying", progname, ldone);
+                               "not verifying", tfc_format_pid(progname), ldone);
                        exitcode = 1;
                        goto _shortmac;
                }
@@ -1197,16 +1318,24 @@ _macragain:             lio = xread(sfd, pblk, lrem);
 
                if (!memcmp(tmpdata, macresult, TF_FROM_BITS(macbits))) {
                        if (quiet == NO) {
-                               tfc_esay("%s: signature is good", progname);
+                               tfc_esay("%s: signature is good", tfc_format_pid(progname));
                                if (verbose) {
                                        if (do_outfmt == TFC_OUTFMT_B64) tfc_printbase64(stderr, macresult, TF_FROM_BITS(macbits), YES);
                                        else mehexdump(macresult, TF_FROM_BITS(macbits), TF_FROM_BITS(macbits), YES);
                                }
                        }
+                       if (do_mac == TFC_MAC_JUST_VRFY2) {
+                               if (verbose) tfc_esay("%s: -u: MAC signature is valid, proceeding with decrypting it again", tfc_format_pid(progname));
+                               do_mac = TFC_MAC_DROP;
+                               goto _decrypt_again_vrfy2;
+                       }
                }
                else {
-                       if (quiet == NO) tfc_esay("%s: signature is BAD: "
-                               "wrong password, key, mode, or file is not signed", progname);
+                       if (quiet == NO) {
+                               tfc_esay("%s: signature is BAD: "
+                               "wrong password, key, mode, or file is not signed", tfc_format_pid(progname));
+                               if (do_mac == TFC_MAC_JUST_VRFY2) tfc_esay("%s: -u: MAC signature is invalid, not decrypting it again", tfc_format_pid(progname));
+                       }
                        exitcode = 1;
                }
 
@@ -1272,12 +1401,7 @@ _macwagain:              lio = xwrite(dfd, pblk, lrem);
                memset(tmpdata, 0, sizeof(tmpdata));
        }
 
-_nomac:
-       if (verbose || status_timer || do_stop == TFC_STOP_FULL) print_crypt_status(0);
-
-       if (do_preserve_time) fcopy_matime(dfd, &s_stat);
-       xclose(sfd);
-       xclose(dfd);
+       if (verbose || status_timer || (do_stop == YES && quiet == NO)) print_crypt_status(0);
 
        xexit(exitcode);
        return -1;