/** * @file shred.cpp * @brief shred drive * @author hendrik schutter * @date 03.05.2020 */ #include "../../include/reHDD.h" #ifndef DRYRUN static char *randsrc = (char*) "/dev/urandom"; static int force = 0, rmf = 0, zrf = 0, noround = 0, verbose = 0, syncio = 0, alwaysrand = 0, reqrand = 0; static char sfbuf[PATH_MAX*2]; struct tfnge_stream { TFNG_UNIT_TYPE key[TFNG_NR_KEY_UNITS]; TFNG_UNIT_TYPE iv[TFNG_NR_BLOCK_UNITS]; TFNG_BYTE_TYPE carry_block[TFNG_BLOCK_SIZE]; size_t carry_bytes; }; static struct tfnge_stream tfnge; #endif Shred::Shred() { } Shred::~Shred() { } /** * \brief shred drive with shred * \param pointer of Drive instance * \return void */ void Shred::shredDrive(Drive* drive, int* ipSignalFd) { #ifdef DRYRUN for(int i = 0; i<=100; i++) { if(drive->state != Drive::SHRED_ACTIVE) { return; } drive->setTaskPercentage(i+0.05); write(*ipSignalFd, "A",1); usleep(20000); } #endif #ifndef DRYRUN struct stat st; char *buf, *s, *d, rc = 0; int f, rsf; int xret = 0, pat = 0, last = 0, special = 0, iIteration = 0; size_t blksz = 0, x, y; size_t l, ll = NOSIZE; const char *cpDrivePath = drive->getPath().c_str(); blockcount_max = SHRED_ITERATIONS*(drive->getCapacity()/4096); #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("Shred-Task: Max-BlockCount: " + to_string(blockcount_max) + " - Drive: " + drive->getSerial()); #endif d32Percent = 0U; rsf = open(randsrc, O_RDONLY | O_LARGEFILE); if (rsf == -1) { perror(randsrc); exit(1); } special = pat = 0; iIteration = SHRED_ITERATIONS; if (verbose) fprintf(stderr, "destroying %s ...\n", cpDrivePath); if (stat(cpDrivePath, &st) == -1) { perror(cpDrivePath); XRET(1); goto _return; } if (!blksz) blksz = (size_t)st.st_blksize; else l = ll = st.st_size; if (l == 0 && !S_ISREG(st.st_mode)) special = 1; memset(&st, 0, sizeof(struct stat)); if (force) if (chmod(cpDrivePath, S_IRUSR|S_IWUSR) == -1) { perror(cpDrivePath); XRET(1); } f = open(cpDrivePath, O_WRONLY | O_LARGEFILE | O_NOCTTY | syncio); if (f == -1) { XRET(1); perror(cpDrivePath); goto _return; } buf = (char*) malloc(blksz); if (!buf) { perror("malloc"); XRET(2); fprintf(stderr, "Continuing with fixed buffer (%zu bytes long)\n", sizeof(sfbuf)); buf = sfbuf; blksz = sizeof(sfbuf); } memset(buf, 0, blksz); #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("Shred-Task: RandomRead: " + to_string(blksz) + " - Drive: " + drive->getSerial()); #endif if (read(rsf, buf, blksz) <= 0) fprintf(stderr, "%s: read 0 bytes (wanted %zu)\n", randsrc, blksz); tfnge_init(&tfnge, buf); //iteration loop while (iIteration) { lseek(f, 0L, SEEK_SET); if (iIteration <= 1 && zrf) { pat = 1; rc = 0; } else if (iIteration == SHRED_ITERATIONS && reqrand) { pat = 0; } else if (!alwaysrand) { #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("Shred-Task: alwaysrand"); #endif if (read(rsf, &rc, 1) <= 0) fprintf(stderr, "%s: read 0 bytes (wanted 1)\n", randsrc); pat = rc%2; if (read(rsf, &rc, 1) <= 0) fprintf(stderr, "%s: read 0 bytes (wanted 1)\n", randsrc); } else pat = 0; if (verbose) { if (pat) fprintf(stderr, "iteration (pat) %d (%02hhx%02hhx%02hhx) ...\n", SHRED_ITERATIONS-iIteration+1, rc, rc, rc); else fprintf(stderr, "iteration (!pat) %d (random) ...\n", SHRED_ITERATIONS-iIteration+1); } // write block loop while (1) { if(drive->state != Drive::SHRED_ACTIVE) { drive->setTaskPercentage(0); d32Percent = 0.00; blockcount = 0; blockcount_max = 0; Logger::logThis()->info("Aborted shred for: " + drive->getModelName() + "-" + drive->getSerial()); goto _return; } double d32TmpPercent = calcProgress(); if((d32TmpPercent-d32Percent) >= 0.09) { drive->setTaskPercentage(d32TmpPercent); d32Percent = d32TmpPercent; #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("Shred-Task: BlockCount: " + to_string(blockcount) + " - progress: " + to_string(d32Percent) + " - Drive: " + drive->getSerial()); #endif write(*ipSignalFd, "A",1); } if (!pat) { tfnge_emit(buf, blksz, &tfnge); } else { memset(buf, rc, blksz); } if (l <= blksz && !special) { last = 1; } errno = 0; #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("Shred-Task: DriveWrite: " + to_string((noround && last) ? l : blksz) + " - Drive: " + drive->getSerial()); #endif l -= write(f, buf, (noround && last) ? l : blksz); if (errno) { perror(cpDrivePath); errno = 0; break; } if (last) { last = 0; break; } } // write block loop end l = ll; fdatasync(f); iIteration--; } //iteration loop end if (rmf) { close(f); f = open(cpDrivePath, O_WRONLY | O_TRUNC | O_LARGEFILE | O_NOCTTY | syncio); if (verbose) fprintf(stderr, "removing %s ...\n", cpDrivePath); x = strnlen(cpDrivePath, sizeof(sfbuf)/2); s = sfbuf+(sizeof(sfbuf)/2); memcpy(sfbuf, cpDrivePath, x); *(sfbuf+x) = 0; d = strrchr(sfbuf, '/'); if (d) { d++; y = d-sfbuf; memset(d, '0', x-(d-sfbuf)); } else { y = 0; memset(sfbuf, '0', x); } memcpy(s, sfbuf, x); *(s+x) = 0; /* Somehow I need to rename original to destination */ if (access(s, R_OK) != -1) { fprintf(stderr, "%s already exists!\n", s); unlink(cpDrivePath); goto _return; } if (verbose) fprintf(stderr, "%s -> %s\n", cpDrivePath, s); if (rename(cpDrivePath, s) == -1) { perror(s); goto _return; } while (x > y+1) { *(sfbuf+x) = 0; x--; *(s+x) = 0; if (access(s, R_OK) != -1) { fprintf(stderr, "%s already exists!\n", s); unlink(sfbuf); goto _return; } if (verbose) fprintf(stderr, "%s -> %s\n", sfbuf, s); if (rename(sfbuf, s) == -1) { perror(s); goto _return; } } if (verbose) fprintf(stderr, "remove %s\n", s); unlink(s); if (verbose) fprintf(stderr, "done away with %s.\n", cpDrivePath); } tfnge_emit(NULL, 0, &tfnge); if (buf && buf != sfbuf) free(buf); if (f != -1) close(f); _return: optind++; close(rsf); #endif if(drive->state == Drive::SHRED_ACTIVE) { drive->bWasShredded = true; drive->state= Drive::NONE; drive->setTaskPercentage(0); Logger::logThis()->info("Finished shred for: " + drive->getModelName() + "-" + drive->getSerial()); } } #ifndef DRYRUN double Shred::calcProgress() { blockcount++; return ((((double)blockcount/(double)blockcount_max))*100); } void Shred::tfnge_init_iv(struct tfnge_stream *tfe, const void *key, const void *iv) { memset(tfe, 0, sizeof(struct tfnge_stream)); memcpy(tfe->key, key, TFNG_KEY_SIZE); if (iv) memcpy(tfe->iv, iv, TFNG_BLOCK_SIZE); tfe->carry_bytes = 0; } void Shred::tfnge_init(struct tfnge_stream *tfe, const void *key) { tfnge_init_iv(tfe, key, NULL); } void Shred::tfng_encrypt_rawblk(TFNG_UNIT_TYPE *O, const TFNG_UNIT_TYPE *I, const TFNG_UNIT_TYPE *K) { TFNG_UNIT_TYPE X, Y, Z, T; TFNG_UNIT_TYPE K0, K1, K2, K3; TFNG_UNIT_TYPE K4, T0, T1, T2; X = I[0]; Y = I[1]; Z = I[2]; T = I[3]; K0 = K[0]; K1 = K[1]; K2 = K[2]; K3 = K[3]; K4 = K[4]; T0 = K[5]; T1 = K[6]; T2 = K[7]; PROCESS_BLOCKP( 1,K1,T0,K0,K3,K2,T1); PROCESS_BLOCKN( 2,K2,T1,K1,K4,K3,T2); PROCESS_BLOCKP( 3,K3,T2,K2,K0,K4,T0); PROCESS_BLOCKN( 4,K4,T0,K3,K1,K0,T1); PROCESS_BLOCKP( 5,K0,T1,K4,K2,K1,T2); PROCESS_BLOCKN( 6,K1,T2,K0,K3,K2,T0); PROCESS_BLOCKP( 7,K2,T0,K1,K4,K3,T1); PROCESS_BLOCKN( 8,K3,T1,K2,K0,K4,T2); PROCESS_BLOCKP( 9,K4,T2,K3,K1,K0,T0); PROCESS_BLOCKN(10,K0,T0,K4,K2,K1,T1); PROCESS_BLOCKP(11,K1,T1,K0,K3,K2,T2); PROCESS_BLOCKN(12,K2,T2,K1,K4,K3,T0); O[0] = X + K3; O[1] = Y + K4 + T0; O[2] = Z + K0 + T1; O[3] = T + K1 + 18; } void Shred::tfnge_emit(void *dst, size_t szdst, struct tfnge_stream *tfe) { TFNG_BYTE_TYPE *udst = (uint8_t*) dst; size_t sz = szdst; if (!dst && szdst == 0) { memset(tfe, 0, sizeof(struct tfnge_stream)); return; } if (tfe->carry_bytes > 0) { if (tfe->carry_bytes > szdst) { memcpy(udst, tfe->carry_block, szdst); memmove(tfe->carry_block, tfe->carry_block+szdst, tfe->carry_bytes-szdst); tfe->carry_bytes -= szdst; return; } memcpy(udst, tfe->carry_block, tfe->carry_bytes); udst += tfe->carry_bytes; sz -= tfe->carry_bytes; tfe->carry_bytes = 0; } if (sz >= TFNG_BLOCK_SIZE) { do { tfng_encrypt_rawblk(tfe->iv, tfe->iv, tfe->key); memcpy(udst, tfe->iv, TFNG_BLOCK_SIZE); udst += TFNG_BLOCK_SIZE; } while ((sz -= TFNG_BLOCK_SIZE) >= TFNG_BLOCK_SIZE); } if (sz) { tfng_encrypt_rawblk(tfe->iv, tfe->iv, tfe->key); memcpy(udst, tfe->iv, sz); udst = (TFNG_BYTE_TYPE *)tfe->iv; tfe->carry_bytes = TFNG_BLOCK_SIZE-sz; memcpy(tfe->carry_block, udst+sz, tfe->carry_bytes); } } #endif