Praktikumsaufgaben und Lunhbox voidb@x group let unhbox voidb

Transcrição

Praktikumsaufgaben und Lunhbox voidb@x group let unhbox voidb
Informationssicherheit
– Lösung der Praktikumsaufgabe 3 –
Thema: Kryptografische Hashes
1.
• Die Anzahl der möglichen Hashes beträgt 28 = 256, ist somit viel zu klein.
• Es ist trivial, eine Datei so zu manipulieren, dass sie einen bestimmten Hash
erzeugt.
2. Da wir die Implementierung von MD5 nicht kennengelernt haben und die OpenSSLBibliothek, die eine entsprechende Implementierung enthält, ebenfalls noch nicht behandelt wurde, ist es günstig, das Kommandozeilenprogramm md5sum zu nutzen, das
den MD5-Hash der als Parameter übergebenen Datei ermittelt.
Das Programm muss dann im einzelnen die folgenden Dinge tun:
• 2 Klartext-Dateien generieren, beide dürfen sich nur in einem Bit unterscheiden
• 2 Hashes bilden ({md5sum <dateiname>) Ergebnis wieder in Datei schreiben
• Hashes aus Datei einlesen
• Hash 1 XOR Hash 2 = Bits, die sich geändert haben
• Zählen der gesetzten Bits im Ergebnis der XOR-Operation
• Anzahl geänderter Bits akkumulieren und schließlich durch Anzahl Iterationen
dividieren
Um eine statistisch relevante Aussage zu treffen, sollte der Ablauf viele Male wiederholt
werden.
Problematisch ist die Tatsache, dass unter C maximal 64bittige Datentypen existieren
(long long), der Hash aber eine Länge von 128 Bit aufweist. Es bietet sich an, jeweils
2 long long- Variablen für die Hashes und das XOR-Ergebnis zu nutzen.
Listing 1 zeigt eine mögliche Lösung.
Listing 1: Ein Programm zur statistischen Prüfung von MD5
#include
#include
#include
#include
<stdio.h>
<stdlib.h>
<unistd.h>
<math.h>
#define N 50000
#define DATFILE "hashes.dat"
#define FILE1 "file1.in"
#define FILE2 "file2.in"
c Robert Baumgartl, 2009-16
–1–
18. Mai 2016
Informationssicherheit
#define CMD1 "md5sum < "FILE1" | cut -f1 -d ’ ’> "DATFILE
#define CMD2 "md5sum < "FILE2" | cut -f1 -d ’ ’>> "DATFILE
int main(void)
{
FILE *f1, *f2, *f3;
unsigned long long llhi1, lllo1, llhi2, lllo2, xor1, xor2;
int bits_changed, sum_bits_changed, iterations;
char buf[17];
double chbits_per_iteration = 0.0;
/* statistics init */
sum_bits_changed = 0;
/* generate test files which differ in exactly one bit */
if ((f1 = fopen(FILE1, "w")) == NULL) {
perror("fopen, f1");
exit(EXIT_FAILURE);
}
if ((f2 = fopen(FILE2, "w")) == NULL) {
perror("fopen, f2");
exit(EXIT_FAILURE);
}
fputc(’b’, f1);
fputc(’c’, f2);
fclose(f1);
fclose(f2);
/*
Master Loop
*/
for (iterations=0; iterations<N; iterations++) {
/* call md5sum (write result into DATFILE) */
system(CMD1);
system(CMD2);
/* analyze DATFILE */
if ((f3 = fopen(DATFILE, "r")) == NULL) {
perror("fopen, datfile");
exit(EXIT_FAILURE);
}
/* Hash of file1 */
buf[16] = 0;
fread(buf, 16, 1, f3);
llhi1 = strtoull(buf, NULL, 16);
fread(buf, 16, 1, f3);
lllo1 = strtoull(buf, NULL, 16);
printf("Hash of file1: %016llx:%016llx\n", llhi1, lllo1);
getc(f3);
/* read \n from DATFILE */
c Robert Baumgartl, 2009-16
–2–
18. Mai 2016
Informationssicherheit
/* Hash of file2 */
fread(buf, 16, 1, f3);
llhi2 = strtoull(buf, NULL, 16);
fread(buf, 16, 1, f3);
lllo2 = strtoull(buf, NULL, 16);
printf("Hash of file2: %016Lx:%016Lx\n", llhi2, lllo2);
fclose(f3);
xor1 = llhi1 ˆ llhi2;
xor2 = lllo1 ˆ lllo2;
/* count set bits in xor1 and xor2 */
bits_changed = 0;
while (xor1 != 0) {
if (xor1 & 0x01) {
bits_changed++;
}
xor1 >>= 1;
}
while (xor2 != 0) {
if (xor2 & 0x01) {
bits_changed++;
}
xor2 >>= 1;
}
printf("Bits changed: %d\n", bits_changed);
/* update statistics */
sum_bits_changed += bits_changed;
/* generate two new files which differ in exactly one bit
by appending the same char to both files */
if ((f1 = fopen(FILE1, "a")) == NULL) {
perror("fopen, f1");
exit(EXIT_FAILURE);
}
if ((f2 = fopen(FILE2, "a")) == NULL) {
perror("fopen, f2");
exit(EXIT_FAILURE);
}
fputc(’a’, f1);
fputc(’a’, f2);
fclose(f1);
fclose(f2);
} /* Master Loop */
/* Results */
chbits_per_iteration = (double) sum_bits_changed / (double) N;
printf("On average, %.2f bits have changed for %i test cases.",
chbits_per_iteration, N);
printf(" That are %.2f%%.\n", chbits_per_iteration/1.28);
/* clean up */
unlink(DATFILE);
c Robert Baumgartl, 2009-16
–3–
18. Mai 2016
Informationssicherheit
unlink(FILE1);
unlink(FILE2);
return(EXIT_SUCCESS);
}
Ein Testlauf mit 10.000 Iterationen ergibt:
[...]
Hash of file1: 7973ca41d7c467b8:0b392a931fe416ea
Hash of file2: 90326469ed4d00fc:ed1ac9f34a733181
Bits changed: 61
Hash of file1: 2bb394f3f4533007:0b1ea152b1da3437
Hash of file2: 449f7741f16f2b2d:c6004cb5862d8765
Bits changed: 72
On average, 64.02 bits have changed for 10000 test cases. That are 50.01%.
3.∗
a) Das in Listing 2 detaillierte Programm misst die Zeit, die die Kommandozeilenprogramme für die Verarbeitung einer 4 GiB großen Datei benötigen und errechnet
daraus den Durchsatz.
Listing 2: Performancevergleich verschiedener Hashverfahren
/*
hashperf.c
if test file size > 2 GiB is req’d, compile with
gcc -D_FILE_OFFSET_BITS=64 -o hashperf hashperf.c -lm
*/
#include
#include
#include
#include
#include
<stdio.h>
<stdlib.h>
<unistd.h>
<string.h>
<math.h>
#define N 1000
/* use a single test file of size 4 GiB */
#define TESTFILE "test.dat"
unsigned long long testfilesize = 0x01LL<<32;
/* compare five different hash programs */
#define NUMHASHES 5
char *hashes[NUMHASHES] =
{"sha1sum", "sha224sum", "sha512sum", "md5sum", "cksum"};
int hashlength[NUMHASHES] = {160, 224, 512, 128, 32};
#define LOGFILE "hashperf.txt"
int main(void)
{
FILE *fout, *fin;
unsigned long long filepos;
int c, ret;
c Robert Baumgartl, 2009-16
–4–
18. Mai 2016
Informationssicherheit
char buf[128];
double dur;
/* generate file, if it doesn’t exist already */
if ((fout=fopen(TESTFILE, "r")) == NULL) {
if ((fout = fopen(TESTFILE, "w")) == NULL) {
printf("Could not fopen() %s\n", TESTFILE);
perror("fopen");
exit(EXIT_FAILURE);
}
printf("Size of test file: %llu\n", testfilesize);
for (filepos=0; filepos<testfilesize; filepos++) {
ret = putc(random(), fout);
if (ret == EOF) {
perror("putc");
}
}
fclose(fout);
}
unlink(LOGFILE);
/* measure timing of generating the hash over TESTfile for all
hash programs */
for (c=0; c<NUMHASHES; c++) {
sprintf(buf, "/usr/bin/time -f%%e -o %s -a %s %s >/dev/null",
LOGFILE, hashes[c], TESTFILE);
printf("executing %s\n", buf, LOGFILE);
system(buf);
}
/* analyze timing */
if ((fin=fopen(LOGFILE, "r")) == NULL) {
perror("fopen() LOGFILE.\n");
exit(EXIT_FAILURE);
}
printf("Throughput of\n");
for (c=0; c<NUMHASHES; c++) {
ret = fscanf(fin, "%lf", &dur);
if (ret != 1) {
printf("fscanf failed in %ith entry of %s\n", c, LOGFILE);
exit(EXIT_FAILURE);
}
printf("dur = %lf\n", dur);
fgets(buf, 2, fin); /* read away ’ \n’ */
printf("%s: %0.2lf MiB/s.\n", hashes[c],
(double) testfilesize/dur/1024.0/1024.0);
}
fclose(fin);
/* do not delete the TESTFILE on purpose, because it is hard to
generate */
return(EXIT_SUCCESS);
}
c Robert Baumgartl, 2009-16
–5–
18. Mai 2016
Informationssicherheit
Auf einem mehrere Jahre alten Notebook mit Intel Core 2 Duo und einer Taktfrequenz von 2.53 GHz wurden damit die folgenden Ergebnisse ermittelt:
MiB · s−1
sha1sum
43.05
sha224sum
45.36
sha512sum
17.49
md5sum
39.54
cksum
47.86
Tabelle 1: Durchsätze für 5 verschiedene Hashverfahren
b) Das Programm in Listing 3 ermittelt die Dauer der vollständigen Enumeration des
Schlüsselraums für die betreffenden Hashverfahrens unter der Annahme, dass in
einer Sekunde eine Milliarde Hashes generiert werden in Sekunden sowie in einem
geeigneten größeren Zeitmaßstab. Das Akronym ‘BBP’ steht für die Lebensdauer
des Universums (Big Bang Periods ≈ 13.7 Mrd. Jahre; ca. 4.32 · 1017 s).
Listing 3: Dauer der Enumeration des Schlüsselraums von Hashverfahren
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/* One billion hashes per second */
#define HASHES_PER_SEC 1.0E9
/* We compare NUMHASHES different hash programs */
#define NUMHASHES 5
char *hashes[NUMHASHES] =
{"sha1sum", "sha224sum", "sha512sum", "md5sum", "cksum"};
int hashlength[NUMHASHES] = {160, 224, 512, 128, 32};
struct uentry {
char *name;
double secsperunit;
};
/* We have NUMUNITS units of time for illustration */
#define NUMUNITS 5
struct uentry units[NUMUNITS] = {
{ "BBP", 13.7*1.0E9*365.0*86400.0 }, /* Big Bang Periods (13.7
Billion Years) */
{ "Ma", 1.0E6*86400.0*365.0 },
/* MegaYears (1 Million
Years) */
{ "a", 86400.0*365.0 },
/* 1 Year */
{ "m", 86400.0*30.0 },
/* 1 Month */
{ "d", 86400.0 }
/* 1 Day */
};
int main(void)
{
int c, d;
double seconds, result;
for (c=0; c<NUMHASHES; c++) {
printf("%s: ", hashes[c]);
c Robert Baumgartl, 2009-16
–6–
18. Mai 2016
Informationssicherheit
seconds = pow(2.0, (double) hashlength[c]) / HASHES_PER_SEC;
printf("%0.2lE s ", seconds);
for (d=0; d<5; d++){
result = seconds / units[d].secsperunit;
if (result > 1.0){
printf("= %0.2lE %s\n", result, units[d].name);
break;
}
}
if (result < 1.0) {
/* no dividing factor has been found */
printf("\n");
}
}
return(EXIT_SUCCESS);
}
Eine Ausgabe des Programms (gleiche Plattform wie bei a)):
robge@ipaetz2:˜/txt/job/htw/is/src/prak03/hashperf$ ./bruteforce
sha1sum: 1.46E+39 s = 3.38E+21 BBP
sha224sum: 2.70E+58 s = 6.24E+40 BBP
sha512sum: 1.34E+145 s = 3.10E+127 BBP
md5sum: 3.40E+29 s = 7.88E+11 BBP
cksum: 4.29E+00 s
4.∗ Die angegebene Lösung wurde durch Herrn Marco Ziebell entwickelt, dem an dieser
Stelle recht herzlich gedankt sei.
Listing 4: Implementierung von MD2
#include
#include
#include
#include
<stdlib.h>
<stdio.h>
<string.h>
<inttypes.h>
uint8_t* md2(const uint8_t const *message, unsigned int messagelength)
;
static unsigned char S[256] = {
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
c Robert Baumgartl, 2009-16
–7–
18. Mai 2016
Informationssicherheit
129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
31, 26, 219, 153, 141, 51, 159, 17, 131, 20
};
uint8_t* md2(const uint8_t const *message, unsigned int messagelength)
{
unsigned int paddinglength = 0;
/* length of message + padding (to be computed later) */
unsigned int N = 0;
/* buffer for (msg + padding + checksum) */
uint8_t *M = NULL;
/* pointer to checksum within M */
uint8_t *C = NULL;
/* buffer for hash computation */
static uint8_t X[48] = {0};
/* temporary variables */
size_t i, j, k;
unsigned int c = 0, L = 0, t = 0;
/*
* Step 1 of the MD2 Algorithm
* P A D D I N G
*/
/* padding is always between 1 and 16 bytes */
paddinglength = 16 - (messagelength % 16u);
/* compute buffer length */
N = messagelength + paddinglength;
/* alloc space for (message + padding + checksum) */
M = (uint8_t*) calloc(1, N + 16);
if(M == NULL){
fputs("calloc failed\n",stderr);
exit(EXIT_FAILURE);
}
C = (M + N);
/* write buffer content */
memcpy(M, message, messagelength);
memset(M+messagelength , (int) paddinglength, paddinglength);
/*
* Step 2 of 5 of the MD2 Algorithm
* C H E C K S U M
*/
memset(C, 0, 16);
L = 0;
for(i = 0; i <= ((N / 16) - 1); ++i) {
for(j = 0; j < 16; ++j) {
c = M[i * 16 + j];
C[j] ˆ= S[c ˆ L];
L = C[j];
}
}
c Robert Baumgartl, 2009-16
–8–
18. Mai 2016
Informationssicherheit
/*
* Step 3 of 5 of the MD2 Algorithm
* I N I T I A L I Z E
*/
memset(X, 0, 48);
/*
* Step 4 of 5 of the MD2 Algorithm
* P R O C E S S M E S S A G E
*/
for(i = 0u; i <= ( ( (N + 16) / 16) - 1); ++i) {
for( j = 0; j < 16; ++j){
X[16+j] = M[i * 16 + j];
X[32+j] = (X[16+j] ˆ X[j]);
}
t = 0;
for(j = 0; j < 18; ++j) {
for(k = 0; k < 48; ++k) {
t = X[k] ˆ= S[t];
}
t = (t+j) & 0xff;
}
}
if(M != NULL){
free(M);
}
return X;
}
int main(void)
{
int i = 0 ;
int j = 0;
/* some reference results */
char *buf[] = {
"" ,
"abc",
"abcdefghijklmnopqrstuvwxyz"
};
char *sol[] = {
"8350e5a3e24c153df2275c9f80692773",
"da853b0d3f88d99b30283a69e6ded6bb",
"4e8ddff3650292ab5a4108c3aa47940b"
};
uint8_t *md = NULL;
for(j = 0; j < 3; ++j){
printf("Eingabe: %s\n", buf[j]);
md = md2((const uint8_t *) buf[j], (unsigned int) strlen(buf[j]));
printf("LSG: %s\n", sol[j]);
c Robert Baumgartl, 2009-16
–9–
18. Mai 2016
Informationssicherheit
fputs("OUT: ", stdout);
for(i = 0; i < 16; ++i) {
printf("%02x",md[i]);
}
printf("\n-------------------------------------\n\n");
}
return EXIT_SUCCESS;
}
5. Das Programm ermittelt den SHA1-Hash der als Kommandozeilenparameter übergebenen
Datei und gibt diesen als Hexadezimalzahl nach stdout aus. Die Implementierung
erfolgt mittels der OpenSSL-Bibliothek, die wir auch in der Belegaufgabe einsetzen
werden.
Um das Hashverfahren auszutauschen, muss man nur 4 Elemente verändern. Das Headerfile muss ausgetauscht werden, die Länge des Hashes ist eine andere und das Verfahren selbst muss im Aufruf von EVP DigestInit() ersetzt werden. Die notwendigen
Änderungen enthält md5.diff. Er wird folgendermaßen angewandt, vorausgesetzt,
die Quelldatei hash.c befindet sich im gleichen Verzeichnis:
robge@ipaetz2:˜/tmp$ patch
c Robert Baumgartl, 2009-16
< md5.diff
– 10 –
18. Mai 2016

Documentos relacionados