Logo Search packages:      
Sourcecode: aespipe version File versions  Download package

aespipe.c

/*
 *  aespipe.c
 *
 *  Written by Jari Ruusu, February 18 2007
 *
 *  Copyright 2002-2007 by Jari Ruusu.
 *  Redistribution of this file is permitted under the GNU Public License.
 *
 *  AES encrypting or decrypting "pipe", reads from stdin, writes to stdout
 */

#include <stdio.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <pwd.h>
#include <sys/types.h>
#include <signal.h>
#if HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#if HAVE_FCNTL_H
# include <fcntl.h>
#endif
#include <errno.h>
#if HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif
#if HAVE_TERMIOS_H
# include <termios.h>
#endif
#if HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif

#include "aes.h"
#include "md5.h"
#include "sha512.h"
#include "rmd160.h"

#if !defined(AESPIPE_PASSWORD_MIN_LENGTH)
# define  AESPIPE_PASSWORD_MIN_LENGTH   20
#endif

#if WORDS_BIGENDIAN
# define xcpu_to_le32(x) ({u_int32_t __x=(x);((u_int32_t)((((u_int32_t)(__x)&(u_int32_t)0x000000ffUL)<<24)|(((u_int32_t)(__x)&(u_int32_t)0x0000ff00UL)<<8)|(((u_int32_t)(__x)&(u_int32_t)0x00ff0000UL)>>8)|(((u_int32_t)(__x)&(u_int32_t)0xff000000UL)>>24)));})
#else
# define xcpu_to_le32(x) ((u_int32_t)(x))
#endif

char            *progName;
int             ivCounter = 0;
u_int32_t       devSect0 = 0;
u_int32_t       devSect1 = 0;
u_int32_t       devSect2 = 0;
u_int32_t       devSect3 = 0;
int             passFDnumber = -1;
char            *passSeedString = (char *)0;
int             passAskTwice = 0;
char            *gpgKeyFile = (char *)0;
char            *gpgHomeDir = (char *)0;
char            *clearTextKeyFile = (char *)0;
char            *passIterThousands = (char *)0;
int             complainWriteErr = 1;
unsigned int    waitSeconds = 0;
aes_context     ctx;
u_int32_t       iv[8];
union {
    u_int32_t       w[2048];
    unsigned char   b[8192];
} buf;
int             multiKeyMode = 0; /* 0=single-key 64=multi-key-v2 65=multi-key-v3 */
char            *multiKeyPass[66];
aes_context     multiKeyCtx[64];
u_int32_t       partialMD5[4];

int rd_wr_retry(int fd, char *buf, int cnt, int w)
{
    int x, y, z;

    x = 0;
    while(x < cnt) {
        y = cnt - x;
        if(w) {
            z = write(fd, buf + x, y);
        } else {
            z = read(fd, buf + x, y);
            if (!z) return x;
        }
        if(z < 0) {
            if ((errno == EAGAIN) || (errno == ENOMEM) || (errno == EINTR)) {
                continue;
            }
            return x;
        }
        x += z;
    }
    return x;
}

char *get_FD_pass(int fd)
{
    char *p = NULL, *n;
    int x = 0, y = 0;

    do {
        if(y >= (x - 1)) {
            x += 128;
            /* Must enforce some max limit here.      */
            /* This code may have successfully called */
            /* mlockall(MCL_CURRENT | MCL_FUTURE)     */
            if(x > (4*1024)) return(NULL);
            n = malloc(x);
            if(!n) return(NULL);
            if(p) {
                memcpy(n, p, y);
                memset(p, 0, y);
                free(p);
            }
            p = n;
        }
        if(rd_wr_retry(fd, p + y, 1, 0) != 1) break;
        if((p[y] == '\n') || !p[y]) break;
        y++;
    } while(1);
    if(p) p[y] = 0;
    return p;
}

static void warnAboutBadKeyData(int x)
{
    if((x > 1) && (x != 64) && (x != 65)) {
        fprintf(stderr, "Warning: Unknown key data format - using it anyway\n");
    }
}
        
char *do_GPG_pipe(char *pass)
{
    int     x, pfdi[2], pfdo[2];
    char    str[10], *a[16], *e[2], *h;
    pid_t   gpid;
    struct passwd *p;
    void    *oldSigPipeHandler;

    if(gpgHomeDir && gpgHomeDir[0]) {
        h = gpgHomeDir;
    } else {
        if(!(p = getpwuid(getuid()))) {
            fprintf(stderr, "Error: Unable to detect home directory for uid %d\n", (int)getuid());
            return NULL;
        }
        h = p->pw_dir;
    }
    if(!(e[0] = malloc(strlen(h) + 6))) {
        nomem1:
        fprintf(stderr, "Error: Unable to allocate memory\n");
        return NULL;
    }
    sprintf(e[0], "HOME=%s", h);
    e[1] = 0;

    if(pipe(&pfdi[0])) {
        nomem2:
        free(e[0]);
        goto nomem1;
    }
    if(pipe(&pfdo[0])) {
        close(pfdi[0]);
        close(pfdi[1]);
        goto nomem2;
    }

    if((x = open(gpgKeyFile, O_RDONLY)) == -1) {
        fprintf(stderr, "Error: unable to open %s for reading\n", gpgKeyFile);
        free(e[0]);
        close(pfdo[0]);
        close(pfdo[1]);
        close(pfdi[0]);
        close(pfdi[1]);
        return NULL;
    }

    sprintf(str, "%d", pfdi[0]);
    if(!(gpid = fork())) {
        dup2(x, 0);
        dup2(pfdo[1], 1);
        close(x);
        close(pfdi[1]);
        close(pfdo[0]);
        close(pfdo[1]);
        if((x = open("/dev/null", O_WRONLY)) >= 0) {
            dup2(x, 2);
            close(x);
        }
        x = 0;
        a[x++] = "gpg";
        if(gpgHomeDir && gpgHomeDir[0]) {
            a[x++] = "--homedir";
            a[x++] = gpgHomeDir;
        }
        a[x++] = "--no-options";
        a[x++] = "--quiet";
        a[x++] = "--batch";
        a[x++] = "--no-tty";
        a[x++] = "--passphrase-fd";
        a[x++] = str;
        a[x++] = "--decrypt";
        a[x] = 0;
#if defined(PATH_TO_GPG_PROGRAM)
        execve(PATH_TO_GPG_PROGRAM, &a[0], &e[0]);
#endif
        execve("/bin/gpg", &a[0], &e[0]);
        execve("/usr/bin/gpg", &a[0], &e[0]);
        execve("/usr/local/bin/gpg", &a[0], &e[0]);
        /* as last resort try to run gpg from same dir as aespipe */
        x = strlen(progName);
        if((h = malloc(x + 4)) != NULL) {
            strcpy(h, progName);
            while(--x >= 0) {
                if(h[x] == '/') break;
                h[x] = 0;
            }
            if(strlen(h) > 0) {
                strcat(h, "gpg");
                execve(h, &a[0], &e[0]);
            }
        }
        exit(1);
    }
    free(e[0]);
    close(x);
    close(pfdi[0]);
    close(pfdo[1]);
    if(gpid == -1) {
        close(pfdi[1]);
        close(pfdo[0]);
        goto nomem1;
    }

    x = strlen(pass);

    /* ignore possible SIGPIPE signal while writing to gpg */
    oldSigPipeHandler = signal(SIGPIPE, SIG_IGN);
    rd_wr_retry(pfdi[1], pass, x, 1);
    rd_wr_retry(pfdi[1], "\n", 1, 1);
    if(oldSigPipeHandler != SIG_ERR) signal(SIGPIPE, oldSigPipeHandler);

    close(pfdi[1]);
    memset(pass, 0, x);
    x = 0;
    while(x < 66) {
        multiKeyPass[x] = get_FD_pass(pfdo[0]);
        if(!multiKeyPass[x]) {
            /* mem alloc failed - abort */
            multiKeyPass[0] = 0;
            break;
        }
        if(strlen(multiKeyPass[x]) < AESPIPE_PASSWORD_MIN_LENGTH) break;
        x++;
    }
    warnAboutBadKeyData(x);
    if(x >= 65)
        multiKeyMode = 65;
    if(x == 64)
        multiKeyMode = 64;
    close(pfdo[0]);
    waitpid(gpid, &x, 0);
    if(!multiKeyPass[0]) goto nomem1;
    return multiKeyPass[0];
}

#ifndef TCSASOFT
# define TCSASOFT 0
#endif
char *getPass(char *prompt)
{
    int fd, changed = 0;
    struct termios oldt, newt;
    char *p;

    fd = open("/dev/tty", O_RDWR);
    if(fd < 0) return(NULL);
    if(!tcgetattr(fd, &oldt)) {
        newt = oldt;
        newt.c_lflag &= ~(ECHO | ISIG);
        changed = (tcsetattr(fd, TCSAFLUSH | TCSASOFT, &newt) == 0);
    }
    rd_wr_retry(fd, prompt, strlen(prompt), 1);
    p = get_FD_pass(fd);
    if(p) rd_wr_retry(fd, "\n", 1, 1);
    if(changed) tcsetattr(fd, TCSAFLUSH | TCSASOFT, &oldt);
    close(fd);
    return(p);
}

char *sGetPass(int minLen)
{
    char *p, *s, *seed;
    int i, ask2, close_psw_fd = 0;

    if(passFDnumber < 0) {
        if(clearTextKeyFile) {
            if((passFDnumber = open(clearTextKeyFile, O_RDONLY)) == -1) {
                fprintf(stderr, "Error: unable to open %s for reading\n", clearTextKeyFile);
                return NULL;
            }
            close_psw_fd = 1;
            goto contReadFrom_psw;
        }
        p = getPass("Password: ");
        ask2 = passAskTwice;
    } else {
        contReadFrom_psw:
        if(gpgKeyFile && gpgKeyFile[0]) {
            p = get_FD_pass(passFDnumber);
            if(close_psw_fd) close(passFDnumber);
        } else {
            int x = 0;
            while(x < 66) {
                multiKeyPass[x] = get_FD_pass(passFDnumber);
                if(!multiKeyPass[x]) goto nomem;
                if(strlen(multiKeyPass[x]) < AESPIPE_PASSWORD_MIN_LENGTH) break;
                x++;
            }
            if(close_psw_fd) close(passFDnumber);
            warnAboutBadKeyData(x);
            if(x >= 65) {
                multiKeyMode = 65;
                return multiKeyPass[0];
            }
            if(x == 64) {
                multiKeyMode = 64;
                return multiKeyPass[0];
            }
            p = multiKeyPass[0];
        }
        ask2 = 0;
    }
    if(!p) goto nomem;
    if(gpgKeyFile && gpgKeyFile[0]) {
        if(ask2) {
            i = strlen(p);
            s = malloc(i + 1);
            if(!s) goto nomem;
            strcpy(s, p);
            p = getPass("Retype password: ");
            if(!p) goto nomem;
            if(strcmp(s, p)) goto compareErr;
            memset(s, 0, i);
            free(s);
            ask2 = 0;
        }
        p = do_GPG_pipe(p);
        if(!p) return(NULL);
        if(!p[0]) {
            fprintf(stderr, "Error: gpg key file decryption failed\n");
            return(NULL);
        }
        if(multiKeyMode) return(p);
    }
    i = strlen(p);
    if(i < minLen) {
        fprintf(stderr, "Error: Password must be at least %d characters.\n", minLen);
        return(NULL);
    }
    seed = passSeedString;
    if(!seed) seed = "";
    s = malloc(i + strlen(seed) + 1);
    if(!s) {
        nomem:
        fprintf(stderr, "Error: Unable to allocate memory\n");
        return(NULL);
    }
    strcpy(s, p);
    memset(p, 0, i);
    if(ask2) {
        p = getPass("Retype password: ");
        if(!p) goto nomem;
        if(strcmp(s, p)) {
            compareErr:
            fprintf(stderr, "Error: Passwords are not identical\n");
            return(NULL);
        }
        memset(p, 0, i);
    }
    strcat(s, seed);
    return(s);
}

/* obsolete */
void unhashed1_hash_buffer(unsigned char *keyStr, int ile, unsigned char *keyBuf, int bufSize) {
    int x, y, z, cnt = ile;
    unsigned char *kp;

    memset(keyBuf, 0, bufSize);
    kp = keyStr;

    for (x = 0; x < (bufSize * 8); x += 6) {
        y = *kp++;

        if (--cnt <= 0) {
            kp = keyStr;
            cnt = ile;
        }

        if ((y >= '0') && (y <= '9')) y -= '0';
        else if((y >= 'A') && (y <= 'Z')) y -= ('A' - 10);
        else if((y >= 'a') && (y <= 'z')) y -= ('a' - 36);
        else if((y == '.') || (y == '/')) y += (62 - '.');
        else y &= 63;

        z = x >> 3;

        if (z < bufSize) {
            keyBuf[z] |= y << (x & 7);
        }

        z++;

        if (z < bufSize) {
            keyBuf[z] |= y >> (8 - (x & 7));
        }
    }
}

/* obsolete */
void unhashed2_hash_buffer(unsigned char *keyStr, int ile, unsigned char *keyBuf, int bufSize) {
    memset(keyBuf, 0, bufSize);
    strncpy((char *)keyBuf, (char *)keyStr, bufSize - 1);
    keyBuf[bufSize - 1] = 0;
}

void rmd160HashTwiceWithA(unsigned char *ib, int ile, unsigned char *ob, int ole)
{
    unsigned char tmpBuf[20 + 20];
    unsigned char pwdCopy[130];

    if(ole < 1) return;
    memset(ob, 0, ole);
    if(ole > 40) ole = 40;
    rmd160_hash_buffer(&tmpBuf[0], ib, ile);
    pwdCopy[0] = 'A';
    if(ile > sizeof(pwdCopy) - 1) ile = sizeof(pwdCopy) - 1;
    memcpy(pwdCopy + 1, ib, ile);
    rmd160_hash_buffer(&tmpBuf[20], pwdCopy, ile + 1);
    memcpy(ob, tmpBuf, ole);
    memset(tmpBuf, 0, sizeof(tmpBuf));
    memset(pwdCopy, 0, sizeof(pwdCopy));
}

void compute_md5_iv_v3(u_int32_t *data)
{
    int         x, y, e;
    u_int32_t   buf[16];

    y = 7;
    e = 16;
    do {
        if (!y) {
            e = 12;
            /* md5_transform_CPUbyteorder wants data in CPU byte order */
            /* devSect{0,1} are already in CPU byte order -- no need to convert */
            /* use only 56 bits of sector number */
            buf[12] = devSect0;
            buf[13] = (devSect1 & 0xFFFFFF) | 0x80000000;
            /* 4024 bits == 31 * 128 bit plaintext blocks + 56 bits of sector number */
            /* For version 3 on-disk format this really should be 4536 bits, but can't be */
            /* changed without breaking compatibility. V3 uses MD5-with-wrong-length IV */
            buf[14] = 4024;
            buf[15] = 0;
        }
        x = 0;
        do {
            buf[x    ] = xcpu_to_le32(data[0]);
            buf[x + 1] = xcpu_to_le32(data[1]);
            buf[x + 2] = xcpu_to_le32(data[2]);
            buf[x + 3] = xcpu_to_le32(data[3]);
            x += 4;
            data += 4;
        } while (x < e);
        md5_transform_CPUbyteorder(&iv[0], &buf[0]);
    } while (--y >= 0);

#if WORDS_BIGENDIAN
    iv[0] = xcpu_to_le32(iv[0]);
    iv[1] = xcpu_to_le32(iv[1]);
    iv[2] = xcpu_to_le32(iv[2]);
    iv[3] = xcpu_to_le32(iv[3]);
#endif
}

void doEncryptMD5ivCBC(int cnt)
{
    unsigned char *datap = &buf.b[0];
    aes_context *a;
    int x;

    do {
        a = &multiKeyCtx[devSect0 & 0x3F];
        memcpy(&iv[0], &partialMD5[0], 16);
        compute_md5_iv_v3((u_int32_t *)(datap+16));
        x = 32;
        do {
            iv[0] ^= *((u_int32_t *)(&datap[ 0]));
            iv[1] ^= *((u_int32_t *)(&datap[ 4]));
            iv[2] ^= *((u_int32_t *)(&datap[ 8]));
            iv[3] ^= *((u_int32_t *)(&datap[12]));
            aes_encrypt(a, (unsigned char *)(&iv[0]), datap);
            memcpy(&iv[0], datap, 16);
            datap += 16;
        } while(--x);
        if(!++devSect0) devSect1++;
    } while(--cnt);
}

void doDecryptMD5ivCBC(int cnt)
{
    unsigned char *datap = &buf.b[0];
    aes_context *a;
    int x;

    do {
        a = &multiKeyCtx[devSect0 & 0x3F];
        memcpy(&iv[0], datap, 16);
        datap += 16;
        x = 31;
        do {
            memcpy(&iv[4], datap, 16);
            aes_decrypt(a, datap, datap);
            *((u_int32_t *)(&datap[ 0])) ^= iv[0];
            *((u_int32_t *)(&datap[ 4])) ^= iv[1];
            *((u_int32_t *)(&datap[ 8])) ^= iv[2];
            *((u_int32_t *)(&datap[12])) ^= iv[3];
            memcpy(&iv[0], &iv[4], 16);
            datap += 16;
        } while(--x);
        datap -= 512;
        memcpy(&iv[0], &partialMD5[0], 16);
        compute_md5_iv_v3((u_int32_t *)(datap+16));
        aes_decrypt(a, datap, datap);
        *((u_int32_t *)(&datap[ 0])) ^= iv[0];
        *((u_int32_t *)(&datap[ 4])) ^= iv[1];
        *((u_int32_t *)(&datap[ 8])) ^= iv[2];
        *((u_int32_t *)(&datap[12])) ^= iv[3];
        datap += 512;
        if(!++devSect0) devSect1++;
    } while(--cnt);
}

void doEncryptCBC(int cnt)
{
    unsigned char *datap = &buf.b[0];

    do {
        if(!ivCounter) {
            iv[0] = xcpu_to_le32(devSect0);
            iv[1] = xcpu_to_le32(devSect1);
            iv[2] = xcpu_to_le32(devSect2);
            iv[3] = xcpu_to_le32(devSect3);
            if(!++devSect0 && !++devSect1 && !++devSect2) devSect3++;
        }
        ivCounter++;
        ivCounter &= 31;
        iv[0] ^= *((u_int32_t *)(&datap[ 0]));
        iv[1] ^= *((u_int32_t *)(&datap[ 4]));
        iv[2] ^= *((u_int32_t *)(&datap[ 8]));
        iv[3] ^= *((u_int32_t *)(&datap[12]));
        aes_encrypt(&ctx, (unsigned char *)(&iv[0]), datap);
        memcpy(&iv[0], datap, 16);
        datap += 16;
    } while(--cnt);
}

void doDecryptCBC(int cnt)
{
    unsigned char *datap = &buf.b[0];

    do {
        if(!ivCounter) {
            iv[0] = xcpu_to_le32(devSect0);
            iv[1] = xcpu_to_le32(devSect1);
            iv[2] = xcpu_to_le32(devSect2);
            iv[3] = xcpu_to_le32(devSect3);
            if(!++devSect0 && !++devSect1 && !++devSect2) devSect3++;
        }
        ivCounter++;
        ivCounter &= 31;
        memcpy(&iv[4], datap, 16);
        aes_decrypt(&ctx, datap, datap);
        *((u_int32_t *)(&datap[ 0])) ^= iv[0];
        *((u_int32_t *)(&datap[ 4])) ^= iv[1];
        *((u_int32_t *)(&datap[ 8])) ^= iv[2];
        *((u_int32_t *)(&datap[12])) ^= iv[3];
        memcpy(&iv[0], &iv[4], 16);
        datap += 16;
    } while(--cnt);
}

int main(int argc, char **argv)
{
    int x, encrypt = 1, bits, ret;
    void (*hf)(unsigned char *, int, unsigned char *, int);
    union {
        u_int32_t     hw[16];
        unsigned char hb[64];
    } hbu;
    char *pass, *hfn = (char *)0, *efn = (char *)0;
    unsigned int y;

#if defined(MCL_CURRENT) && defined(MCL_FUTURE) && HAVE_MLOCKALL
    /* try to lock all memory to prevent key leak to swap */
    mlockall(MCL_CURRENT | MCL_FUTURE);
    /* drop possible suid-root privileges */
    if(getuid() != geteuid()) setuid(getuid());
#endif

    progName = *argv;

    for(argc--, argv++; argc > 0; argc--, argv++) {
        if(!strcmp(*argv, "-") || (**argv != '-')) {
            usage:
            fprintf(stderr, "usage: %s [options] <inputfile >outputfile\n"
                            "version 2.3d  Copyright (c) 2002-2007 Jari Ruusu, (c) 2001 Dr Brian Gladman\n"
                            "options:  -e aes128|aes192|aes256          =  set key length\n"
                            "          -H sha256|sha384|sha512|rmd160   =  set password hash function\n"
                            "          -d         =  decrypt\n"
                            "          -p num     =  read password from file descriptor num\n"
                            "          -P file    =  read password from file\n"
                            "          -S pseed   =  set password seed\n"
                            "          -T         =  ask password twice\n"
                            "          -q         =  don't complain about write errors\n"
                            "          -w num     =  wait num seconds before asking password\n"
                            "          -O num     =  set IV offset (value 1 == 512 byte offset)\n"
                            "          -K file    =  file contains gpg encrypted keys\n"
                            "          -G dir     =  home directory for gpg\n"
                            "          -C num     =  iterate key num thousand times through AES-256\n"
                            , progName);
            exit(1);
        } else {
            while(*++(*argv)) {
                switch(**argv) {
                case 'e':
                    if(!(*++(*argv) || (--argc && *++argv))) goto usage;
                    efn = *argv;
                    goto nextArg;
                case 'H':
                    if(!(*++(*argv) || (--argc && *++argv))) goto usage;
                    hfn = *argv;
                    goto nextArg;
                case 'd':
                    encrypt = 0;
                    break;
                case 'p':
                    if(!(*++(*argv) || (--argc && *++argv))) goto usage;
                    if(sscanf(*argv, "%d", &passFDnumber) != 1) goto usage;
                    goto nextArg;
                case 'P':
                    if(!(*++(*argv) || (--argc && *++argv))) goto usage;
                    clearTextKeyFile = *argv;
                    goto nextArg;
                case 'S':
                    if(!(*++(*argv) || (--argc && *++argv))) goto usage;
                    passSeedString = *argv;
                    goto nextArg;
                case 'T':
                    passAskTwice = 1;
                    break;
                case 'q':
                    complainWriteErr = 0;
                    break;
                case 'w':
                    if(!(*++(*argv) || (--argc && *++argv))) goto usage;
                    if(sscanf(*argv, "%u", &waitSeconds) != 1) goto usage;
                    goto nextArg;
                case 'O':
                    if(!(*++(*argv) || (--argc && *++argv))) goto usage;
                    if(sscanf(*argv, "%u", &y) != 1) goto usage;
                    devSect0 = y;
                    goto nextArg;
                case 'K':
                    if(!(*++(*argv) || (--argc && *++argv))) goto usage;
                    gpgKeyFile = *argv;
                    goto nextArg;
                case 'G':
                    if(!(*++(*argv) || (--argc && *++argv))) goto usage;
                    gpgHomeDir = *argv;
                    goto nextArg;
                case 'C':
                    if(!(*++(*argv) || (--argc && *++argv))) goto usage;
                    passIterThousands = *argv;
                    goto nextArg;
                default:
                    goto usage;
                }
            }
        }
        nextArg: continue;
    }

    bits = 128;
    hf = sha256_hash_buffer;
    if(efn) {
        if(!strcasecmp(efn, "aes256")) {
            bits = 256;
            hf = sha512_hash_buffer;
        } else if(!strcasecmp(efn, "aes192")) {
            bits = 192;
            hf = sha384_hash_buffer;
        } else if(strcasecmp(efn, "aes128") && strcasecmp(efn, "aes")) {
            goto usage;
        }
    }
    if(hfn) {
        if(!strcasecmp(hfn, "sha256")) hf = sha256_hash_buffer;
        else if(!strcasecmp(hfn, "sha384")) hf = sha384_hash_buffer;
        else if(!strcasecmp(hfn, "sha512")) hf = sha512_hash_buffer;
        else if(!strcasecmp(hfn, "rmd160")) hf = rmd160HashTwiceWithA;
        else if(!strcasecmp(hfn, "unhashed1")) hf = unhashed1_hash_buffer;
        else if(!strcasecmp(hfn, "unhashed2")) hf = unhashed2_hash_buffer;
        else goto usage;
    }

    if(waitSeconds) sleep(waitSeconds);
    pass = sGetPass((hf == rmd160HashTwiceWithA) ? 1 : AESPIPE_PASSWORD_MIN_LENGTH);
    if(!pass) exit(1);
    x = strlen(pass);
    if(hf == unhashed1_hash_buffer) { /* obsolete compat */
        bits = 128;
        if(x >= 32) bits = 192;
        if(x >= 43) bits = 256;
    }
    (*hf)((unsigned char *)pass, x, &hbu.hb[0], 32);
    if(multiKeyMode) {
        int r = 0, t;
        partialMD5[0] = 0x67452301;
        partialMD5[1] = 0xefcdab89;
        partialMD5[2] = 0x98badcfe;
        partialMD5[3] = 0x10325476;
        while(r < multiKeyMode) {
            t = strlen(multiKeyPass[r]);
            (*hf)(multiKeyPass[r], t, &hbu.hb[0], 32);
            memset(multiKeyPass[r], 0, t);
            /*
             * MultiKeyMode uses md5 IV. One key mode uses sector IV. Sector IV
             * and md5 IV v2 and v3 are all computed differently. This first key
             * byte XOR with 0x55/0xF4 is needed to cause complete decrypt failure
             * in cases where data is encrypted with one type of IV and decrypted
             * with another type IV. If identical key was used but only IV was
             * computed differently, only first plaintext block of 512 byte CBC
             * chain would decrypt incorrectly and rest would decrypt correctly.
             * Partially correct decryption is dangerous. Decrypting all blocks
             * incorrectly is safer because file system mount will simply fail.
             */
            if(multiKeyMode == 65) {
                hbu.hb[0] ^= 0xF4; /* version 3 */
            } else {
                hbu.hb[0] ^= 0x55; /* version 2 */
            }
            if(r < 64) {
                aes_set_key(&multiKeyCtx[r], &hbu.hb[0], bits, 0);
            } else {
                /* only first 128 bits of iv-key is used */
#if WORDS_BIGENDIAN
                hbu.hw[0] = xcpu_to_le32(hbu.hw[0]);
                hbu.hw[1] = xcpu_to_le32(hbu.hw[1]);
                hbu.hw[2] = xcpu_to_le32(hbu.hw[2]);
                hbu.hw[3] = xcpu_to_le32(hbu.hw[3]);
#endif
                memset(&hbu.hb[16], 0, 48);
                md5_transform_CPUbyteorder(&partialMD5[0], &hbu.hw[0]);
            }
            r++;
        }
    } else if(passIterThousands) {
        unsigned long iter = 0;
        union {
            u_int32_t     w[8]; /* needed for 4 byte alignment of tempkey[] */
            unsigned char tempkey[32];
        } tku;
        /*
         * Set up AES-256 encryption key using same password and hash function
         * as before but with password bit 0 flipped before hashing. That key
         * is then used to encrypt actual encryption key N thousand times.
         */
        pass[0] ^= 1;
        (*hf)((unsigned char *)pass, x, &tku.tempkey[0], 32);
        aes_set_key(&ctx, &tku.tempkey[0], 256, 0);
        sscanf(passIterThousands, "%lu", &iter);
        iter *= 1000;
        while(iter > 0) {
            /* encrypt both 128bit blocks with AES-256 */
            aes_encrypt(&ctx, &hbu.hb[ 0], &hbu.hb[ 0]);
            aes_encrypt(&ctx, &hbu.hb[16], &hbu.hb[16]);
            /* exchange upper half of first block with lower half of second block */
            memcpy(&tku.tempkey[0], &hbu.hb[8], 8);
            memcpy(&hbu.hb[8], &hbu.hb[16], 8);
            memcpy(&hbu.hb[16], &tku.tempkey[0], 8);
            iter--;
        }
        memset(&tku.tempkey[0], 0, sizeof(tku.tempkey));
    }
    aes_set_key(&ctx, &hbu.hb[0], bits, 0);
    memset(&hbu.hb[0], 0, sizeof(hbu.hb));
    memset(pass, 0, x);

    ret = 0;
    while(1) {
        x = rd_wr_retry(0, (char *)(&buf.b[0]), sizeof(buf.b), 0);
        if(x < 1) break;
        if(multiKeyMode) {
            while(x & 511) buf.b[x++] = 0;
            if(encrypt) {
                doEncryptMD5ivCBC(x >> 9);
            } else {
                doDecryptMD5ivCBC(x >> 9);
            }
        } else {
            while(x & 15) buf.b[x++] = 0;
            if(encrypt) {
                doEncryptCBC(x >> 4);
            } else {
                doDecryptCBC(x >> 4);
            }
        }
        if(rd_wr_retry(1, (char *)(&buf.b[0]), x, 1) != x) {
            if(complainWriteErr) fprintf(stderr, "%s: write failed\n", progName);
            ret = 1;
            break;
        }
    }
    memset(&ctx, 0, sizeof(ctx));
    memset(&multiKeyCtx[0], 0, sizeof(multiKeyCtx));
    memset(&iv[0], 0, sizeof(iv));
    memset(&buf.b[0], 0, sizeof(buf));
    memset(&partialMD5[0], 0, sizeof(partialMD5));
    exit(ret);
}

Generated by  Doxygen 1.6.0   Back to index