-c: pattern fill support.
[tfcrypt.git] / tfcrypt.c
index dcf9ea98c394e8405cad36b55752c70ea15dfc35..4ba1dd530cd179dd269abfa4cca8033c347f853b 100644 (file)
--- a/tfcrypt.c
+++ b/tfcrypt.c
@@ -51,6 +51,62 @@ static int getps_hex_filter(struct getpasswd_state *getps, char chr, size_t pos)
        return 0;
 }
 
+static inline int isctrlchr(int c)
+{
+       if (c == 9) return 0;
+       if (c >= 0 && c <= 31) return 1;
+       if (c == 127) return 1;
+       return 0;
+}
+
+static int getps_plain_filter(struct getpasswd_state *getps, char chr, size_t pos)
+{
+       int x;
+
+       x = getps_filter(getps, chr, pos);
+       if (x != 1) return x;
+
+       if (pos < getps->pwlen && !isctrlchr(chr))
+               write(getps->efd, &chr, sizeof(char));
+       return 1;
+}
+
+static int getps_plain_hex_filter(struct getpasswd_state *getps, char chr, size_t pos)
+{
+       int x;
+
+       x = getps_hex_filter(getps, chr, pos);
+       if (x != 1) return x;
+
+       if (pos < getps->pwlen && !isctrlchr(chr))
+               write(getps->efd, &chr, sizeof(char));
+       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;
@@ -62,9 +118,25 @@ int main(int argc, char **argv)
 
        if (!isatty(2)) do_statline_dynamic = NO;
 
+       s = (char *)srcblk;
+       d = getenv("HOME");
+       if (!d) d = "";
+       n = PATH_MAX > sizeof(srcblk) ? sizeof(srcblk) : PATH_MAX;
+       if (xstrlcpy(s, d, n) >= n) goto _baddfname;
+       if (xstrlcat(s, "/.tfcrypt.defs", n) >= n) goto _baddfname;
+       read_defaults(s, YES);
+_baddfname:
+       memset(s, 0, n);
+
        opterr = 0;
-       while ((c = getopt(argc, argv, "aU:C:r:K:t:TPkzxc: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:S:AmM:R:Z:WHD:")) != -1) {
                switch (c) {
+                       case 'L':
+                               read_defaults(optarg, NO);
+                               break;
+                       case 's':
+                               saltf = optarg;
+                               break;
                        case 'r':
                                randsource = optarg;
                                break;
@@ -75,6 +147,31 @@ int main(int argc, char **argv)
                                        counter_opt = TFC_CTR_HEAD;
                                else if (!strcasecmp(optarg, "rand"))
                                        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':
@@ -141,9 +238,7 @@ int main(int argc, char **argv)
                                break;
                        case 't':
                                tweakf = optarg;
-                               break;
-                       case 'T':
-                               do_tfcrypt1 = YES;
+                               do_full_key = NO;
                                break;
                        case 'l':
                                if (maxlen != NOFSIZE) break;
@@ -167,12 +262,18 @@ int main(int argc, char **argv)
                                        catch_all_errors = YES;
                                        break;
                                }
+                               if (!strcmp(optarg, "xseek")) {
+                                       ignore_seek_errors = YES;
+                                       break;
+                               }
                                if (!strcmp(optarg, "exit"))
                                        error_action = TFC_ERRACT_EXIT;
                                else if (!strncmp(optarg, "cont", 4))
                                        error_action = TFC_ERRACT_CONT;
                                else if (!strcmp(optarg, "sync"))
                                        error_action = TFC_ERRACT_SYNC;
+                               else if (!strcmp(optarg, "lsync"))
+                                       error_action = TFC_ERRACT_LSYNC;
                                else xerror(NO, YES, YES, "invalid error action %s specified", optarg);
                                break;
                        case 'O':
@@ -203,6 +304,10 @@ int main(int argc, char **argv)
                                                mac_pw_prompt = s+10;
                                        else if (!strcmp(s, "shorthex"))
                                                do_full_hexdump = NO;
+                                       else if (!strcmp(s, "fullkey"))
+                                               do_full_key = YES;
+                                       else if (!strcmp(s, "showsecrets"))
+                                               show_secrets = YES;
                                        else if (!strncmp(s, "iobs", 4) && *(s+4) == '=') {
                                                s += 5;
                                                blksize = (size_t)tfc_humanfsize(s, &stoi);
@@ -318,17 +423,40 @@ int main(int argc, char **argv)
                                                if (counter_opt == TFC_CTR_HEAD)
                                                        maxlen += TF_BLOCK_SIZE;
                                        }
+                                       else if (!strncmp(s, "ftrunc", 6) && *(s+6) == '=') {
+                                               s += 7;
+                                               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);
                                                if (!str_empty(stoi)) {
-                                                       maxkeylen = (size_t)tfc_fnamesize(s, YES);
-                                                       maxkeylen = (size_t)tfc_modifysize((tfc_fsize)maxkeylen, strchr(s, ':'));
+                                                       maxkeylen = tfc_fnamesize(s, YES);
+                                                       maxkeylen = tfc_modifysize(maxkeylen, strchr(s, ':'));
                                                        if (maxkeylen == NOSIZE)
                                                                xerror(NO, YES, YES,
                                                                "%s: invalid key length value", s);
                                                }
-                                               else maxkeylen = (size_t)tfc_modifysize((tfc_fsize)maxkeylen, strchr(s, ':'));
+                                               else maxkeylen = tfc_modifysize(maxkeylen, strchr(s, ':'));
+                                       }
+                                       else if (!strncmp(s, "okey", 4) && *(s+4) == '=') {
+                                               s += 5;
+                                               keyoffset = tfc_humanfsize(s, &stoi);
+                                               if (!str_empty(stoi)) {
+                                                       keyoffset = tfc_fnamesize(s, YES);
+                                                       keyoffset = tfc_modifysize(keyoffset, strchr(s, ':'));
+                                                       if (keyoffset == NOFSIZE)
+                                                               xerror(NO, YES, YES,
+                                                               "%s: invalid key offset value", s);
+                                               }
+                                               else keyoffset = tfc_modifysize(keyoffset, strchr(s, ':'));
                                        }
                                        else if (!strncmp(s, "xctr", 4) && *(s+4) == '=') {
                                                s += 5;
@@ -407,6 +535,7 @@ int main(int argc, char **argv)
                        case 'q':
                                quiet = YES;
                                verbose = NO;
+                               do_full_hexdump = NO;
                                status_timer = 0;
                                break;
                        case 'v':
@@ -456,8 +585,6 @@ int main(int argc, char **argv)
                xerror(NO, YES, YES, "Cannot encrypt and read CTR from source!");
        if (overwrite_source && counter_opt == TFC_CTR_RAND)
                xerror(NO, YES, YES, "Cannot embed a CTR into file when overwriting it!");
-       if (tweakf && do_tfcrypt1 == NO)
-               xerror(NO, YES, YES, "Use -T with -t tweakfile to enable old tfcrypt mode!");
        if (ctr_mode == TFC_MODE_PLAIN
        && (do_edcrypt || do_mac || rawkey
        || mackey_opt || counter_opt || counter_file))
@@ -466,6 +593,23 @@ int main(int argc, char **argv)
        errno = 0;
        do_stop = NO;
 
+       if (saltf) {
+               int saltfd;
+
+               memset(tfc_salt, 0, TFC_MAX_SALT);
+               tfc_saltsz = 0;
+               if (!strcasecmp(saltf, "disable")) goto _nosalt;
+
+               if (!strcmp(saltf, "-")) saltfd = 0;
+               else saltfd = open(saltf, O_RDONLY | O_LARGEFILE);
+               if (saltfd == -1) xerror(NO, NO, YES, "%s", saltf);
+               lio = xread(saltfd, tfc_salt, TFC_MAX_SALT - TF_FROM_BITS(TFC_KEY_BITS));
+               if (lio == NOSIZE) xerror(NO, NO, YES, "%s", saltf);
+               tfc_saltsz = lio;
+               xclose(saltfd);
+       }
+
+_nosalt:
        if (mackey_opt == TFC_MACKEY_FILE && mackeyf) {
                int mkfd = -1;
                tfc_yesno do_stop;
@@ -482,7 +626,8 @@ int main(int argc, char **argv)
                        pblk = tmpdata;
                        ldone = 0;
                        lrem = lblock = sizeof(tmpdata);
-_mkragain:             lio = read(mkfd, pblk, lrem);
+                       if (error_action == TFC_ERRACT_SYNC) rdpos = tfc_fdgetpos(mkfd);
+_mkragain:             lio = xread(mkfd, pblk, lrem);
                        if (lio == 0) do_stop = YES;
                        if (lio != NOSIZE) ldone += lio;
                        else {
@@ -491,10 +636,12 @@ _mkragain:                lio = read(mkfd, pblk, lrem);
                                switch (error_action) {
                                        case TFC_ERRACT_CONT: xerror(YES, NO, NO, "%s", mackeyf); goto _mkragain; break;
                                        case TFC_ERRACT_SYNC:
+                                       case TFC_ERRACT_LSYNC:
                                                xerror(YES, NO, NO, "%s", mackeyf);
                                                lio = ldone = lrem = lblock;
                                                memset(tmpdata, 0, lio);
-                                               lseek(mkfd, lio, SEEK_CUR);
+                                               if (rdpos == NOFSIZE) lseek(mkfd, lio, SEEK_CUR);
+                                               else lseek(mkfd, rdpos + lio, SEEK_SET);
                                                break;
                                        default: xerror(NO, NO, NO, "%s", mackeyf); break;
                                }
@@ -518,20 +665,14 @@ _mkragain:                lio = read(mkfd, pblk, lrem);
                getps.passwd = pwdask;
                getps.pwlen = sizeof(pwdask)-1;
                getps.echo = mac_pw_prompt ? mac_pw_prompt : "Enter MAC password: ";
-               getps.charfilter = getps_filter;
-               getps.maskchar = 'x';
+               getps.charfilter = (show_secrets == YES) ? getps_plain_filter : getps_filter;
+               getps.maskchar = (show_secrets == YES) ? 0 : 'x';
                getps.flags = GETP_WAITFILL;
                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));
-               }
        }
 
        
@@ -561,16 +702,16 @@ _mkragain:                lio = read(mkfd, pblk, lrem);
        else password = YES;
 
        errno = 0;
-       if (do_tfcrypt1 == YES && tweakf) {
+       if (do_full_key == NO && tweakf) {
                int twfd;
 
                if (!strcmp(tweakf, "-")) twfd = 0;
                else twfd = open(tweakf, O_RDONLY | O_LARGEFILE);
                if (twfd == -1) xerror(NO, NO, YES, "%s", tweakf);
-               lio = ldone = read(twfd, key+TF_FROM_BITS(TF_MAX_BITS)+TF_SIZE_UNIT, 2*TF_SIZE_UNIT);
+               lio = ldone = xread(twfd, tweak, TF_TWEAK_SIZE);
                if (lio == NOSIZE) xerror(NO, NO, YES, "%s", tweakf);
-               if (ldone < 2*TF_SIZE_UNIT)
-                       xerror(NO, NO, YES, "%s: %zu bytes tweak required", tweakf, 2*TF_SIZE_UNIT);
+               if (ldone < TF_TWEAK_SIZE)
+                       xerror(NO, NO, YES, "%s: %zu bytes tweak required", tweakf, TF_TWEAK_SIZE);
                xclose(twfd);
        }
 
@@ -616,7 +757,7 @@ _nokeyfd:
                if (!strcmp(counter_file, "-")) ctrfd = 0;
                else ctrfd = open(counter_file, O_RDONLY | O_LARGEFILE);
                if (ctrfd == -1) xerror(NO, NO, YES, "%s", counter_file);
-               lio = read(ctrfd, ctr, ctrsz);
+               lio = xread(ctrfd, ctr, ctrsz);
                if (lio == NOSIZE) xerror(NO, NO, YES, "%s", counter_file);
                if (lio < ctrsz) xerror(NO, YES, YES, "counter file is too small (%zu)!", lio);
                xclose(ctrfd);
@@ -625,7 +766,8 @@ _nokeyfd:
                pblk = ctr;
                ldone = 0;
                lrem = lblock = ctrsz;
-_ctrragain:    lio = read(sfd, pblk, lrem);
+               if (error_action == TFC_ERRACT_SYNC) rdpos = tfc_fdgetpos(sfd);
+_ctrragain:    lio = xread(sfd, pblk, lrem);
                if (lio != NOSIZE) ldone += lio;
                else {
                        if (errno != EIO && catch_all_errors != YES)
@@ -633,10 +775,12 @@ _ctrragain:       lio = read(sfd, pblk, lrem);
                        switch (error_action) {
                                case TFC_ERRACT_CONT: xerror(YES, NO, NO, "%s", srcfname); goto _ctrragain; break;
                                case TFC_ERRACT_SYNC:
+                               case TFC_ERRACT_LSYNC:
                                        xerror(YES, NO, NO, "%s", srcfname);
                                        lio = ldone = lrem = lblock;
                                        memset(ctr, 0, lio);
-                                       lseek(sfd, lio, SEEK_CUR);
+                                       if (rdpos == NOFSIZE) lseek(sfd, lio, SEEK_CUR);
+                                       else lseek(sfd, rdpos + lio, SEEK_SET);
                                        break;
                                default: xerror(NO, NO, NO, "%s", srcfname); break;
                        }
@@ -654,7 +798,7 @@ _ctrskip1:
                if (counter_opt == TFC_CTR_HEAD && ctr_mode != TFC_MODE_ECB)
                        iseek += ctrsz;
                if (lseek(sfd, iseek, SEEK_SET) == -1)
-                       xerror(YES, NO, NO, "%s: seek failed", srcfname);
+                       xerror(ignore_seek_errors, NO, NO, "%s: seek failed", srcfname);
        }
 
        if (ctr_mode == TFC_MODE_PLAIN) goto _plain;
@@ -667,7 +811,8 @@ _ctrskip1:
                pblk = key;
 _xts2key:      ldone = 0;
                lrem = lblock = TF_FROM_BITS(TFC_KEY_BITS);
-_keyragain:    lio = read(kfd, pblk, lrem);
+               if (error_action == TFC_ERRACT_SYNC) rdpos = tfc_fdgetpos(kfd);
+_keyragain:    lio = xread(kfd, pblk, lrem);
                if (lio != NOSIZE) ldone += lio;
                else {
                        if (errno != EIO && catch_all_errors != YES)
@@ -675,10 +820,12 @@ _keyragain:       lio = read(kfd, pblk, lrem);
                        switch (error_action) {
                                case TFC_ERRACT_CONT: xerror(YES, NO, NO, "reading key"); goto _keyragain; break;
                                case TFC_ERRACT_SYNC:
+                               case TFC_ERRACT_LSYNC:
                                        xerror(YES, NO, NO, "reading key");
                                        lio = ldone = lrem = lblock;
                                        memset(key, 0, lio);
-                                       lseek(kfd, lio, SEEK_CUR);
+                                       if (rdpos == NOFSIZE) lseek(kfd, lio, SEEK_CUR);
+                                       else lseek(kfd, rdpos + lio, SEEK_SET);
                                        break;
                                default: xerror(NO, NO, NO, "reading key"); break;
                        }
@@ -707,12 +854,13 @@ _xts2keyaskstr:   memset(&getps, 0, sizeof(struct getpasswd_state));
                getps.passwd = (char *)pblk;
                getps.pwlen = n;
                getps.echo = pw_prompt ? pw_prompt : "Enter rawkey (str): ";
-               getps.charfilter = getps_filter;
-               getps.maskchar = 'x';
+               getps.charfilter = (show_secrets == YES) ? getps_plain_filter : getps_filter;
+               getps.maskchar = (show_secrets == YES) ? 0 : 'x';
                getps.flags = GETP_WAITFILL;
                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);
@@ -731,8 +879,8 @@ _rawkey_hex_again:
                getps.passwd = pwdask;
                getps.pwlen = (TF_FROM_BITS(TFC_KEY_BITS)*2);
                getps.echo = pw_prompt ? pw_prompt : "Enter rawkey (hex): ";
-               getps.charfilter = getps_hex_filter;
-               getps.maskchar = 'x';
+               getps.charfilter = (show_secrets == YES) ? getps_plain_hex_filter : getps_hex_filter;
+               getps.maskchar = (show_secrets == YES) ? 0 : 'x';
                getps.flags = GETP_WAITFILL;
                n = xgetpasswd(&getps);
                if (n == NOSIZE) xerror(NO, NO, YES, "getting hex rawkey");
@@ -743,6 +891,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;
@@ -757,8 +906,8 @@ _pwdagain:  memset(&getps, 0, sizeof(struct getpasswd_state));
                getps.passwd = pwdask;
                getps.pwlen = sizeof(pwdask)-1;
                getps.echo = pw_prompt ? pw_prompt : "Enter password: ";
-               getps.charfilter = getps_filter;
-               getps.maskchar = 'x';
+               getps.charfilter = (show_secrets == YES) ? getps_plain_filter : getps_filter;
+               getps.maskchar = (show_secrets == YES) ? 0 : 'x';
                getps.flags = GETP_WAITFILL;
                n = xgetpasswd(&getps);
                if (n == NOSIZE) xerror(NO, NO, YES, "getting password");
@@ -777,18 +926,24 @@ _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));
        }
        else {
-               if (skeinfd(key, TFC_KEY_BITS, mackey_opt ? mackey : NULL, kfd, maxkeylen) != YES)
+               if (skeinfd(key, TFC_KEY_BITS, mackey_opt ? mackey : NULL, kfd, keyoffset, maxkeylen) != YES)
                        xerror(NO, NO, YES, "hashing key");
        }
 
-       if (nr_turns > 1 && rawkey == NO) {
-               for (x = 0; x < nr_turns; x++)
+       if (rawkey == NO) {
+               if (tfc_saltsz > 0) {
+                       memcpy(tfc_salt+tfc_saltsz, key, TF_FROM_BITS(TFC_KEY_BITS));
+                       skein(key, TFC_KEY_BITS, mackey_opt ? mackey : NULL, tfc_salt, tfc_saltsz+TF_FROM_BITS(TFC_KEY_BITS));
+               }
+               if (nr_turns > 1) for (x = 0; x < nr_turns; x++)
                        skein(key, TFC_KEY_BITS, mackey_opt ? mackey : NULL, key, TF_FROM_BITS(TFC_KEY_BITS));
+               memset(tfc_salt, 0, TFC_MAX_SALT);
        }
 
        if (ctr_mode == TFC_MODE_XTS && rawkey == NO) {
@@ -803,7 +958,7 @@ _pwdagain:  memset(&getps, 0, sizeof(struct getpasswd_state));
                if (!strcmp(genkeyf, "-")) krfd = 1;
                else krfd = open(genkeyf, O_WRONLY | O_CREAT | O_LARGEFILE | write_flags, 0666);
                if (krfd == -1) xerror(NO, NO, YES, "%s", genkeyf);
-_xts2genkey:   if (write(krfd, pblk, TF_FROM_BITS(TFC_KEY_BITS)) == -1) xerror(NO, NO, YES, "%s", genkeyf);
+_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);
@@ -846,25 +1001,25 @@ _xts2genkey:     if (write(krfd, pblk, TF_FROM_BITS(TFC_KEY_BITS)) == -1) xerror(NO,
 
        tf_convkey(key);
        if (ctr_mode == TFC_MODE_XTS) tf_convkey(xtskey);
-       if (do_tfcrypt1 == YES) {
-               if (!tweakf) skein(key+TF_FROM_BITS(TF_MAX_BITS)+TF_SIZE_UNIT, 2*TF_UNIT_BITS, NULL, key, TF_FROM_BITS(TFC_KEY_BITS));
-               tf_key_tweak_compat(key);
+       if (do_full_key == NO) {
+               if (!tweakf) skein(tweak, TF_NR_TWEAK_BITS, NULL, key, TF_FROM_BITS(TFC_KEY_BITS));
+               tf_tweak_set(key, tweak);
        }
-       if (ctr_mode == TFC_MODE_STREAM) tfe_init_iv(&tfe, key, ctr);
        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));
 
-       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: write(2, ctr, ctrsz); break;
-                               case TFC_OUTFMT_HEX: mhexdump(ctr, ctrsz, ctrsz, YES); break;
-                       }
-                       break;
-               case TFC_CTR_RAND: tfc_getrandom(ctr, 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) {
@@ -889,7 +1044,7 @@ _plain:
 
        if (oseek) {
                if (lseek(dfd, oseek, SEEK_SET) == -1)
-                       xerror(YES, NO, NO, "%s: seek failed", dstfname);
+                       xerror(ignore_seek_errors, NO, NO, "%s: seek failed", dstfname);
        }
 
        for (x = 1; x < NSIG; x++) signal(x, SIG_IGN);
@@ -923,7 +1078,7 @@ _plain:
                pblk = ctr;
                lio = lrem = ctrsz;
                ldone = 0;
-_ctrwagain:    lio = write(dfd, pblk, lrem);
+_ctrwagain:    lio = xwrite(dfd, pblk, lrem);
                if (lio != NOSIZE) ldone += lio;
                else xerror(NO, NO, NO, "%s", dstfname);
                if (do_fsync && fsync(dfd) == -1) xerror(NO, NO, NO, "%s", dstfname);
@@ -936,6 +1091,8 @@ _ctrwagain:        lio = write(dfd, pblk, lrem);
                delta_processed += ldone;
        }
 
+       if (ctr_mode == TFC_MODE_STREAM) tfe_init_iv(&tfe, key, ctr);
+
        errno = 0;
        do_stop = NO;
        while (1) {
@@ -943,7 +1100,8 @@ _ctrwagain:        lio = write(dfd, pblk, lrem);
                pblk = srcblk;
                ldone = 0;
                lrem = lblock = blk_len_adj(maxlen, total_processed_src, blksize);
-_ragain:       lio = read(sfd, pblk, lrem);
+               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 != NOSIZE) ldone += lio;
                else {
@@ -952,10 +1110,12 @@ _ragain: lio = read(sfd, pblk, lrem);
                        switch (error_action) {
                                case TFC_ERRACT_CONT: xerror(YES, NO, NO, "%s", srcfname); goto _ragain; break;
                                case TFC_ERRACT_SYNC:
+                               case TFC_ERRACT_LSYNC:
                                        xerror(YES, NO, NO, "%s", srcfname);
                                        lio = ldone = lrem = lblock;
                                        memset(srcblk, 0, lio);
-                                       lseek(sfd, lio, SEEK_CUR);
+                                       if (rdpos == NOFSIZE) lseek(sfd, lio, SEEK_CUR);
+                                       else lseek(sfd, rdpos + lio, SEEK_SET);
                                        break;
                                default: xerror(NO, NO, NO, "%s", srcfname); break;
                        }
@@ -1007,7 +1167,7 @@ _ragain:  lio = read(sfd, pblk, lrem);
                pblk = dstblk;
                lrem = ldone;
                ldone = 0;
-_wagain:       lio = write(dfd, pblk, lrem);
+_wagain:       lio = xwrite(dfd, pblk, lrem);
                if (lio != NOSIZE) ldone += lio;
                else xerror(NO, NO, NO, "%s", dstfname);
                if (do_fsync && fsync(dfd) == -1) xerror(NO, NO, NO, "%s", dstfname);
@@ -1030,7 +1190,8 @@ _nowrite: total_processed_dst += ldone;
                        pblk = macvrfy;
                        ldone = 0;
                        lrem = lblock = TF_FROM_BITS(macbits);
-_macragain:            lio = read(sfd, pblk, lrem);
+                       if (error_action == TFC_ERRACT_SYNC) rdpos = tfc_fdgetpos(sfd);
+_macragain:            lio = xread(sfd, pblk, lrem);
                        if (lio != NOSIZE) ldone += lio;
                        else {
                                if (errno != EIO && catch_all_errors != YES)
@@ -1038,10 +1199,12 @@ _macragain:             lio = read(sfd, pblk, lrem);
                                switch (error_action) {
                                        case TFC_ERRACT_CONT: xerror(YES, NO, NO, "%s", srcfname); goto _macragain; break;
                                        case TFC_ERRACT_SYNC:
+                                       case TFC_ERRACT_LSYNC:
                                                xerror(YES, NO, NO, "%s", srcfname);
                                                lio = ldone = lrem = lblock;
                                                memset(macvrfy, 0, lio);
-                                               lseek(sfd, lio, SEEK_CUR);
+                                               if (rdpos == NOFSIZE) lseek(sfd, lio, SEEK_CUR);
+                                               else lseek(sfd, rdpos + lio, SEEK_SET);
                                                break;
                                        default: xerror(NO, NO, NO, "%s", srcfname); break;
                                }
@@ -1059,7 +1222,7 @@ _macragain:               lio = read(sfd, pblk, lrem);
                        if (!strcmp(do_mac_file, "-")) mfd = 0;
                        else mfd = open(do_mac_file, O_RDONLY | O_LARGEFILE);
                        if (mfd == -1) xerror(YES, NO, NO, "%s", do_mac_file);
-                       lio = ldone = read(mfd, tmpdata, sizeof(tmpdata));
+                       lio = ldone = xread(mfd, tmpdata, sizeof(tmpdata));
                        if (lio == NOSIZE) xerror(NO, NO, YES, "%s", do_mac_file);
                        if (!memcmp(tmpdata, TFC_ASCII_TFC_MAC_FOURCC, TFC_ASCII_TFC_MAC_FOURCC_LEN)) {
                                memmove(tmpdata, tmpdata+TFC_ASCII_TFC_MAC_FOURCC_LEN,
@@ -1093,7 +1256,7 @@ _macragain:               lio = read(sfd, pblk, lrem);
                                tfc_esay("%s: signature is good", progname);
                                if (verbose) {
                                        if (do_outfmt == TFC_OUTFMT_B64) tfc_printbase64(stderr, macresult, TF_FROM_BITS(macbits), YES);
-                                       else mhexdump(macresult, TF_FROM_BITS(macbits), TF_FROM_BITS(macbits), YES);
+                                       else mehexdump(macresult, TF_FROM_BITS(macbits), TF_FROM_BITS(macbits), YES);
                                }
                        }
                }
@@ -1124,7 +1287,7 @@ _shortmac:        memset(macvrfy, 0, sizeof(macvrfy));
                        pblk = tmpdata;
                        lio = lrem = TF_FROM_BITS(macbits);
                        ldone = 0;
-_macwagain:            lio = write(dfd, pblk, lrem);
+_macwagain:            lio = xwrite(dfd, pblk, lrem);
                        if (lio != NOSIZE) ldone += lio;
                        else xerror(NO, NO, NO, "%s", dstfname);
                        if (do_fsync && fsync(dfd) == -1) xerror(NO, NO, NO, "%s", dstfname);
@@ -1152,9 +1315,9 @@ _macwagain:               lio = write(dfd, pblk, lrem);
                                        tmpdata[lrem] = '\n';
                                        lrem++;
                                }
-                               lio = write(mfd, tmpdata, lrem);
+                               lio = xwrite(mfd, tmpdata, lrem);
                        }
-                       else lio = write(mfd, tmpdata, TF_FROM_BITS(macbits));
+                       else lio = xwrite(mfd, tmpdata, TF_FROM_BITS(macbits));
                        if (lio == NOSIZE) xerror(NO, NO, YES, "%s", do_mac_file);
                        if (do_fsync && fsync(mfd) == -1) xerror(NO, NO, YES, "%s", do_mac_file);
                        xclose(mfd);
@@ -1170,6 +1333,7 @@ _nomac:
 
        if (do_preserve_time) fcopy_matime(dfd, &s_stat);
        xclose(sfd);
+       if (ftrunc_dfd != NOFSIZE) if (ftruncate(dfd, (off_t)ftrunc_dfd) == -1) xerror(YES, NO, YES, "ftruncate(%d)", dfd);
        xclose(dfd);
 
        xexit(exitcode);