Вы находитесь на странице: 1из 156

Chapter 01

1.11 1.1 1.1 1.1 1.1 1.3 1.4 1.5 1.6 1.7 1.8 1.10 te0111.c (a) ex0101a.c (b) ex0101b.c (c) ex0101c.c (d) ex0101d.c ex0103.c ex0104a.c ex0105.c ex0106.c ex0107.c ex0108.c ex0110.c

Classical Cryptography
(6,063) (4,623) (3,190) (3,518) (4,944) (2,813) (7,231) (8,887) (7,709) (3,002) (1,169) (1,031)

Example

1.11
/* Author:

te0111.c

(6,063)

Pate Williams (c) 1997

Example 1.11 "Ciphertext obtained from a Vigenere Cipher: CHREEVOAHMAERATBIAXXWTNXBEEOPHBSBQMQEQERBW RVXUOAKXAOSXXWEAHBWGJMMQMNKGRFVGXWTRZXWAIK LXFPSKAUTEMNDCMGTSXMXBTUIADNGMGPSRELXNJELX VRVPRTULHDNQWTWDTYGBPHXTFALJHASVBFXNGLLCHR ZBWELEKMSJIKNBHWRJGNMGJSGLXFEYPHAGNRBIEQJT

*/

AMRVLCRREMNDGLXRRIMGNSNRWCHRQHAEYEVTAQEBBI PEEWEVKAKOEWADREMXMTBHHCHRTKDNVRZCHRCLQOHP WQAIIWXNRMGWOIIFKEE" -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 33.

#include <ctype.h> #include <stdio.h> #include <string.h> long gcd(long a, long b) { long r; while (b > 0) r = a % b, a = b, b = r; return a; } int main(void) { char cipher[15][25] = {"CHREEVOAHMAERATBIAXXWTN", "XBEEOPHBSBQMQEQERBW", "RVXUOAKXAOSXXWEAHBWGJMM", "QMNKGRFVGXWTRZXWAIK", "LXFPSKAUTEMNDCMGTSXMXBT", "UIADNGMGPSRELXNJELX", "VRVPRTULHDNQWTWDTYGBPHX", "TFALJHASVBFXNGLLCHR", "ZBWELEKMSJIKNBHWRJGNMGJ", "SGLXFEYPHAGNRBIEQJT", "AMRVLCRREMNDGLXRRIMGNSN", "RWCHRQHAEYEVTAQEBBI", "PEEWEVKAKOEWADREMXMTBHH", "CHRTKDNVRZCHRCLQOHP", "WQAIIWXNRMGWOIIFKEE"}; char answer[256], ciphert[500], ciphertext[500]; char keyword[16], word_1[4], word_2[4]; double Ic, MIc[16][16][26], sum; int found = 0; long count = 0, i, j, k, o_count = 0, occur[16]; long col, frequency[26] = {0}, key_length, m, n; long f[26], fp[26], g, l, plaintext; /* tabulate ciphertext frequencies */ for (i = 0; i < 15; i++) for (j = 0; j < strlen(cipher[i]); j++) ciphertext[count] = ciphert[count++] = cipher[i][j]; /* perform the Kasiski test */ for (i = 0; !found && i < count - 2; i++) { word_1[0] = ciphertext[i]; word_1[1] = ciphertext[i + 1]; word_1[2] = ciphertext[i + 2]; o_count = 1; occur[0] = i; for (j = i + 3; j < count - 2; j++) {

} key_length = gcd(occur[0], occur[1]); for (i = 2; i < o_count; i++) key_length = gcd(key_length, occur[i]); printf("total number of ciphertext characters = %ld\n", count); printf("keyword length = %ld\n", key_length); /* compute indices of coincidence */ found = 0; n = count; for (m = 2; !found && m < 15; m++) { col = n / m; i = 0; for (j = 0; j < col; j++) for (k = 0; k < m; k++) ciphertext[k * col + j] = ciphert[i++]; i = 0; for (j = 0; j < m; j++) { for (k = 0; k < 26; k++) frequency[k] = 0; for (k = 0; k < col; k++) frequency[ciphertext[i++] - 'A']++; sum = 0.0; for (k = 0; k < 26; k++) sum += frequency[k] * (frequency[k] - 1); Ic = sum / (col * (col - 1)); printf("Ic = %4.3lf\n", Ic); } printf("another value of m = %ld (n or y)? ", m); scanf("%s", answer); found = tolower(answer[0]) == 'n'; if (found) key_length = m; } printf("keyword length = %ld\n", key_length); col = count / key_length; i = 0; for (j = 0; j < col; j++) for (k = 0; k < key_length; k++) ciphertext[k * col + j] = ciphert[i++]; for (i = 0; i < key_length - 1; i++) { for (j = 0; j < 26; j++) f[j] = 0; for (j = 0; j < col; j++) f[ciphertext[i * col + j] - 'A']++; for (j = i + 1; j < key_length; j++) { for (k = 0; k < 26; k++) fp[k] = 0; for (k = 0; k < col; k++) fp[ciphertext[j * col + k] - 'A']++; sum = 0.0; for (k = 0; k < 26; k++) sum += f[k] * fp[k];

} found = o_count >= 3;

word_2[0] = ciphertext[j]; word_2[1] = ciphertext[j + 1]; word_2[2] = ciphertext[j + 2]; if (strcmp(word_1, word_2) == 0) { occur[o_count] = j; o_count++; }

sum /= (col * col); printf("%4.3lf ", sum); } printf("\n"); } for (i = 0; i < key_length - 1; i++) { for (j = 0; j < 26; j++) f[j] = 0; for (j = 0; j < col; j++) f[ciphertext[i * col + j] - 'A']++; for (j = i + 1; j < key_length; j++) { printf("%ld %ld ", i + 1, j + 1); for (k = 0; k < 26; k++) fp[k] = 0; for (k = 0; k < col; k++) fp[ciphertext[j * col + k] - 'A']++; for (g = 0; g < 26; g++) { sum = 0.0; for (l = 0; l < 26; l++) { m = (l - g) % 26; if (m < 0) m += 26; sum += f[l] * fp[m]; } MIc[i][j][g] = sum / (col * col); printf("%4.3lf ", MIc[i][j][g]); if ((g + 1) % 9 == 0) printf("\n "); } printf("\n"); } } for (i = 0; i < key_length - 1; i++) { for (j = i + 1; j < key_length; j++) { for (g = 0; g < 26; g++) { if (MIc[i][j][g] > 0.061) printf("%ld %ld %2ld %4.3lf\n", i, j, g, MIc[i][j][g]); } } } keyword[0] = 'A'; keyword[1] = 'R'; keyword[2] = 'E'; keyword[3] = 'V'; keyword[4] = 'K'; keyword[5] = '\0'; found = 0; for (i = 0; !found && i < 26; i++) { j = 0; for (k = 0; k < count; k++) { plaintext = ciphert[k] - keyword[j++]; if (j == key_length) j = 0; if (plaintext < 0) plaintext += 26; printf("%c", plaintext + 'A'); if ((k + 1) % 25 == 0) printf("\n"); } printf("\ncorrect decryption (n or y)? "); scanf("%s", answer); found = tolower(answer[0]) == 'y'; if (!found) { for (j = 0; j < key_length; j++) {

keyword[j] -= (char) 'A'; keyword[j]++; if (keyword[j] == 26) keyword[j] -= (char) 26; keyword[j] += (char) 'A'; } } } printf("keyword = "); for (i = 0; i < key_length; i++) printf("%c", keyword[i]); printf("\n"); return 0; }

Exercises

1.1
/*

(a) ex0101a.c

(4,623)

Author:

Pate Williams (c) 1997

*/

Exercise 1.1 "Below are given four examples of cipehertext, one obtained from a Substitution Cipher, one from a Vigenere Cipher, one from an Affine Cipher, and one unspecified. In each case, the task is to determine the plaintext." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 40. <ctype.h> <stdio.h> <stdlib.h> <string.h>

#include #include #include #include

struct code {double rfreq; long alpha, count;}; struct digram {double rfreq; long alpha1, alpha2, count;}; int main(void) { char cipher[11][24] = {"EMGLOSUDCGDNCUSWYSFHNSF", "CYKDPUMLWGYICOXYSIPJCK", "QPKUGKMGOLICGINCGACKSNI", "SACYKZSCKXECJCKSHYSXCG", "OIDPKZCNKSHICGIWYGKKGKG", "OLDSILKGOIUSIGLEDSPWZU", "GFZCCNDGYYSFUSZCNXEOJNC", "GYEOWEUPXEZGACGNFGLKNS", "ACIGOIYCKXCJUCIUZCFZCCN", "DGYYSFEUEKUZCSOCFZCCNC", "IACZEJNCSHFZEJZEGMXCYHC"}; char ch, ciphertext[500], lt_ch, rt_ch; char c_a, c_e, c_o, c_r, c_s, c_w;

char answer[256], plaintext[500]; long frequency[26] = {0}, count = 0, i, j, k = 0, l; long di_freq[26][26] = {{0}}, d_count = 0, line; struct code t, c[26]; struct digram *d, dt; for (i = 0; i < 11; i++) { for (j = 0; j < strlen(cipher[i]); j++) { ch = ciphertext[count++] = cipher[i][j]; frequency[ch - 'A']++; } } for (i = 0; i < 26; i++) { c[i].alpha = i + 'A'; c[i].count = frequency[i]; } for (i = 0; i < 25; i++) for (j = i + 1; j < 26; j++) if (c[i].count < c[j].count) t = c[i], c[i] = c[j], c[j] = t; for (i = 0; i < 26; i++) { c[i].rfreq = c[i].count / (double) count; if (c[i].rfreq != 0.0) printf("%c %lf\n", c[i].alpha, c[i].rfreq); } lt_ch = ciphertext[0]; for (i = 1; i < count - 1; i++) { rt_ch = ciphertext[i]; di_freq[lt_ch - 'A'][rt_ch - 'A']++; lt_ch = rt_ch; } for (i = 0; i < 26; i++) for (j = 0; j < 26; j++) if (di_freq[i][j] != 0) d_count++; d = calloc(d_count, sizeof(struct digram)); for (i = 0; i < 26; i++) { for (j = 0; j < 26; j++) { if (di_freq[i][j] != 0) { d[k].alpha1 = i + 'A'; d[k].alpha2 = j + 'A'; d[k++].count = di_freq[i][j]; } } } for (i = 0; i < d_count - 1; i++) for (j = i + 1; j < d_count; j++) if (d[i].count < d[j].count) dt = d[i], d[i] = d[j], d[j] = dt; for (i = 0; i < d_count; i++) if (d[i].count != 0) printf("%3ld %c %c %ld\n", i, d[i].alpha1, d[i].alpha2, d[i].count); c_e = (char) c[0].alpha; c_a = (char) c[1].alpha; c_o = (char) c[2].alpha; c_r = (char) c[5].alpha; c_s = (char) c[3].alpha;

c_w = 'F'; for (i = 0; i < count; i++) { ch = ciphertext[i]; if (ch == c_a) plaintext[i] = 'A'; else if (ch == c_e) plaintext[i] = 'E'; else if (ch == c_o) plaintext[i] = 'O'; else if (ch == c_r) plaintext[i] = 'R'; else if (ch == c_s) plaintext[i] = 'S'; else if (ch == c_w) plaintext[i] = 'W'; else plaintext[i] = '-'; } line = count / 25; if (line % 25 != 0) line++; do { i = j = 0; for (k = 0; k < line; k++) { printf("P: "); for (l = 0; l < 25; l++) if (i < count) printf("%c", plaintext[i++]); printf("\nC: "); for (l = 0; l < 25; l++) if (j < count) printf("%c", ciphertext[j++]); printf("\n"); } do { printf("Command: M(ap) Q(uit) U(nmap)? "); scanf("%s", answer); ch = (char) tolower(answer[0]); } while (ch != 'm' && ch != 'q' && ch != 'u'); if (ch != 'q') { if (ch == 'm') { printf("Ciphertext character: "); scanf("%s", answer); c_a = (char) toupper(answer[0]); printf("Plaintext character: "); scanf("%s", answer); c_e = (char) toupper(answer[0]); for (i = 0; i < count; i++) if (c_a == ciphertext[i]) plaintext[i] = c_e; } if (ch == 'u') { printf("Plaintext character: "); scanf("%s", answer); c_a = (char) toupper(answer[0]); for (i = 0; i < count; i++) if (c_a == plaintext[i]) plaintext[i] = '-'; } } } while (ch != 'q'); return 0; }

1.1
/*

(b) ex0101b.c

(3,190)

Author:

Pate Williams (c) 1997

Exercise 1.1 (b) "Below are given four examples of cipehertext, one obtained from a Substitution Cipher, one from a Vigenere Cipher, one from an Affine Cipher, and one unspecified. In each case, the task is to determine the plaintext." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 40. */ #include <ctype.h> #include <stdio.h> #include <string.h> long gcd(long a, long b) { long r; while (b > 0) r = a % b, a = b, b = r; return a;

int main(void) { char cipher[15][25] = {"KCCPKBGUFDPHQTYAVINRRTMV", "GRKDNBVFDETDGILTXRGUD", "DKOTFMBPVGEGLTGCKQRACQCW", "DNAWCRXIZAKFTLEWRPTYC", "QKYVXCHKFTPONCQQRHJVAJUW", "ETMCMSPKQDYHJVDAHCTRL", "SVSKCGCZQQDZXGSFRLSWCWSJ", "TBHAFSIASPRJAHKJRJUMV", "GKMITZHFPDISPZLVLGWTFPLK", "KEBDPGCEBSHCTJRWXBAFS", "PEZQNRWXCVYCGAONWDDKACKA", "WBBIKFTIOVKCGGHJVLNHI", "FFSQESVYCLACNVRWBBIREPBB", "VFEXOSCDYGZWPFDTKFQIY", "CWHJVLNHIQIBTKHJVNPIST"}; char ciphertext[500], plaintext[500]; char keyword[16], known[] = "ILEARNEDHOWTO"; char word_1[4], word_2[4]; int found = 0; long count = 0, i, j, o_count, occur[50]; long distance[50], key_length; for (i = 0; i < 15; i++) for (j = 0; j < strlen(cipher[i]); j++) ciphertext[count++] = cipher[i][j];

/* perform the Kasiski test */ for (i = 0; !found && i < count - 2; i++) { word_1[0] = ciphertext[i]; word_1[1] = ciphertext[i + 1]; word_1[2] = ciphertext[i + 2]; word_1[3] = '\0'; occur[0] = i; o_count = 1; for (j = i + 3; j < count - 2; j++) { word_2[0] = ciphertext[j]; word_2[1] = ciphertext[j + 1]; word_2[2] = ciphertext[j + 2]; word_2[3] = '\0'; if (strcmp(word_1, word_2) == 0) occur[o_count++] = j; } found = o_count > 3; } for (i = 0; i < o_count - 1; i++) distance[i] = occur[i + 1] - occur[i]; key_length = gcd(distance[0], distance[1]); for (i = 2; i < o_count - 1; i++) key_length = gcd(key_length, distance[i]); printf("keyword length = %ld\n", key_length); for (i = 0; i < key_length; i++) { keyword[i] = (char) (ciphertext[i] - known[i]); if (keyword[i] < 0) keyword[i] += (char) 26; keyword[i] += (char) 'A'; } keyword[key_length] = '\0'; printf("keyword = %s\n", keyword); for (i = 0; i < count - key_length; i += key_length) { for (j = 0; j < key_length; j++) { plaintext[i + j] = (char) (ciphertext[i + j] - keyword[j]); if (plaintext[i + j] < 0) plaintext[i + j] += (char) 26; plaintext[i + j] += (char) 'A'; } } for (i = 0; i < count - key_length; i++) { printf("%c", plaintext[i]); if ((i + 1) % 25 == 0) printf("\n"); } printf("\n"); return 0; }

1.1
/*

(c) ex0101c.c

(3,518)

Author:

Pate Williams (c) 1997

Exercise 1.1 (c) "Below are given four examples of cipehertext, one obtained from a Substitution Cipher, one from a Vigenere Cipher, one from an Affine Cipher, and one unspecified. In each case, the

task is to determine the plaintext." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 40. */ #include <ctype.h> #include <stdio.h> #include <string.h> struct code {long alpha, count;}; long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

long gcd(long a, long b) { long r; while (b > 0) r = a % b, a = b, b = r; return a; } int main(void) { char cipher[9][25] = {"KQEREJEBCPPCJCRKIEACUZ", "BKRVPKRBCIBQCARBJCVFCUP", "KRIOFKPACUZQEPBKRXPEII", "EABDKPBCPFCDCCAFIEABDKP", "BCPFEQPKAZBKRHAIBKAPCC", "IBURCCDKDCCJCIDFUIXPAFF", "ERBICZDFKABICBBENEFCUP", "JCVKABPCYDCCDPKBCOCPERK", "IVKSCPICBRKIJPKABI"}; char answer[256], ciphertext[500]; long a[2][2], iv[2][2], b[2], det, x[2]; long count = 0, i, j; long frequency[26] = {0};

struct code c[26], temp; for (i = 0; i < 9; i++) { for (j = 0; j < strlen(cipher[i]); j++) { ciphertext[count] = cipher[i][j]; frequency[ciphertext[count] - 'A']++; count++; } } for (i = 0; i < 26; i++) { c[i].alpha = i + 'A'; c[i].count = frequency[i]; } /* sort the code array into descending order */ for (i = 0; i < 25; i++) for (j = i + 1; j < 26; j++) if (c[i].count < c[j].count) temp = c[i], c[i] = c[j], c[j] = temp; for (i = 0; i < 26; i++) if (c[i].count != 0) printf("%c %ld\n", c[i].alpha, c[i].count); do { printf("guess for second most frequently"); printf(" occurring character %c = ", c[1].alpha); scanf("%s", answer); a[0][0] = 'E' - 'A'; a[0][1] = 1; a[1][0] = toupper(answer[0]) - 'A'; a[1][1] = 1; b[0] = c[0].alpha - 'A'; b[1] = c[1].alpha - 'A'; iv[0][0] = a[1][1]; iv[0][1] = - a[0][1]; iv[1][0] = - a[1][0]; iv[1][1] = a[0][0]; det = (a[0][0] * a[1][1] - a[0][1] * a[1][0]) % 26; if (det < 0) det += 26; det = Extended_Euclidean(det, 26); x[0] = (det * (iv[0][0] * b[0] + iv[0][1] * b[1])) % 26; x[1] = (det * (iv[1][0] * b[0] + iv[1][1] * b[1])) % 26; if (x[0] < 0) x[0] += 26; if (x[1] < 0) x[1] += 26; printf("a = %ld\n", x[0]); printf("b = %ld\n", x[1]); printf("gcd(a, 26) = %ld\n", gcd(x[0], 26)); if (x[0] != 0) { x[1] += 'A'; det = Extended_Euclidean(x[0], 26); for (i = 0; i < count; i++) { b[0] = (det * (ciphertext[i] - x[1])) % 26; if (b[0] < 0) b[0] += 26; b[0] += 'A'; printf("%c", b[0]); if ((i + 1) % 25 == 0) printf("\n"); } } printf("\nanother guess (n or y)? ");

scanf("%s", answer); } while (tolower(answer[0]) == 'y'); return 0;

1.1
/*

(d) ex0101d.c

(4,944)

Author:

Pate Williams (c) 1997

Exercise 1.1 (d) "Below are given four examples of cipehertext, one obtained from a Substitution Cipher, one from a Vigenere Cipher, one from an Affine Cipher, and one unspecified. In each case, the task is to determine the plaintext." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson pages 40 - 41. */ #include <ctype.h> #include <stdio.h> #include <string.h> struct code {long alpha, count;}; long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

long gcd(long a, long b) { long r; while (b > 0) r = a % b, a = b, b = r; return a; }

int main (void) { char cipher[17][26] = {"BNVSNSIHQCEELSSKKYERIFJKX", "UMBGYKAMQLJTYAVFBKVT", "DVBPVVRJYYLAOKYMPQSCGDLFS", "RLLPROYGESEBUUALRWXM", "MASAZLGLEDFJBZAVVPXWICGJX", "ASCBYEHOSNMULKCEAHTQ", "OKMFLEBKFXLRRFDTZXCIWBJSI", "CBGAWDVYDHAVFJXZIBKC", "GJIWEAHTTOEWTUHKRQVVRGZBX", "YIREMMASCSPBNLHJMBLR", "FFJELHWEYLWISTFVVYFJCMHYU", "YRUFSFMGESIGRLWALSWM", "NUHSIMYYITCCQPZSICEHBCCMZ", "FEGVJYOCDEMMPGHVAAUM", "HYGGCKTMBLRX"}; char ciphertext[500], plaintext[500]; char keyword[16], known[] = "IGREWUPAMONGSLOW"; char word_1[4], word_2[4]; int found = 0; long count = 0, frequency[26] = {0}, i, j; long distance[16], o_count = 0, occur[16]; long key_length; struct code c[26], temp; /* tabulate character frequencies */ for (i = 0; i < 17; i++) { for (j = 0; j < strlen(cipher[i]); j++) { ciphertext[count] = cipher[i][j]; frequency[ciphertext[i] - 'A']++; count++; } } for (i = 0; i < 26; i++) { c[i].alpha = i + 'A'; c[i].count = frequency[i]; } /* sort the code array into descending order */ for (i = 0; i < 25; i++) for (j = i + 1; j < 26; j++) if (c[i].count < c[j].count) temp = c[i], c[i] = c[j], c[j] = temp; for (i = 0; i < 26; i++) if (c[i].count != 0) printf("%c %ld\n", c[i].alpha, c[i].count); /* perform the Kasiski test */ for (i = 0; !found && i < count - 2; i++) { word_1[0] = ciphertext[i]; word_1[1] = ciphertext[i + 1]; word_1[2] = ciphertext[i + 2]; word_1[3] = '\0'; occur[0] = i; o_count = 1; for (j = i + 3; j < count - 2; j++) { word_2[0] = ciphertext[j];

} if (found) { for (i = 0; i < o_count - 1; i++) distance[i] = occur[i + 1] - occur[i]; key_length = gcd(distance[0], distance[1]); for (i = 2; i < o_count - 1; i++) key_length = gcd(key_length, distance[i]); } else { double IC, sum; char answer[256]; long col, k, m, n; found = 0; n = count; for (m = 1; !found && m < 15; m++) { col = n / m; i = 0; for (j = 0; j < m; j++) { for (k = 0; k < 26; k++) frequency[k] = 0; for (k = 0; k < col; k++) frequency[ciphertext[i++] - 'A']++; sum = 0.0; for (k = 0; k < 26; k++) sum += frequency[k] * (frequency[k] - 1); IC = sum / (col * (col - 1)); printf("Ic = %lf\n", IC); } printf("another value of m = %ld (n or y)? ", m); scanf("%s", answer); found = tolower(answer[0]) == 'n'; if (found) key_length = m; } } printf("keyword length = %ld\n", key_length); for (i = 0; i < key_length; i++) { keyword[i] = (char) (ciphertext[i] - known[i]); if (keyword[i] < 0) keyword[i] += (char) 26; keyword[i] += (char) 'A'; } keyword[key_length] = '\0'; printf("keyword = %s\n", keyword); for (i = 0; i < count - key_length; i += key_length) { for (j = 0; j < key_length; j++) { plaintext[i + j] = (char) (ciphertext[i + j] - keyword[j]); if (plaintext[i + j] < 0) plaintext[i + j] += (char) 26; plaintext[i + j] += (char) 'A'; } } for (i = 0; i < count - key_length; i++) { printf("%c", plaintext[i]);

} found = o_count > 3;

word_2[1] = ciphertext[j + 1]; word_2[2] = ciphertext[j + 2]; word_2[3] = '\0'; if (strcmp(word_1, word_2) == 0) occur[o_count++] = j;

if ((i + 1) % 25 == 0) printf("\n"); } printf("\n"); return 0; }

1.3
/*

ex0103.c

(2,813)

Author: James Pate Williams (c) 2001 Exercise 1.3 from _Cryprography Theory and Practice_ by Douglas R. Stinson p. 41. Originally, I had this problem incorrect. Martin Manscher of the Technical University of Denmark suggested the following solution.

*/

#include <math.h> #include <stdio.h> void extendedEuclid(long a, long b, long *x, long *y, long *d) { /* calculates a * *x + b * *y = gcd(a, b) = *d */ long q, r, x1, x2, y1, y2; if (b == 0) { *d = a, *x = 1, *y = return; } x2 = 1, x1 = 0, y2 = 0, y1 = while (b > 0) { q = a / b, r = a - q *x = x2 - q * x1, *y a = b, b = r; x2 = x1, x1 = *x, y2 } *d = a, *x = x2, *y = y2; } long inverse(long a, long n) { /* computes the inverse of a modulo n */ long d, x, y; extendedEuclid(a, n, &x, &y, &d); if (d == 1) return x; return 0; 0; 1; * b; = y2 - q * y1; = y1, y1 = *y;

int main(void) { int As11, As12, As21, As22, Asqr, detA, i, j, k, l, n = 26; int count1 = 0, count2 = 0, count3 = 0, count4 = 0; int count5 = 0, count6 = 0, count7 = 0, count8 = 0; long a11, m = 26;

/*

A = | i | k

j | l |

det(A) = (i * l - j * k) mod 26 We want to find A such that det(A) = -+ 1 and A * A = 1 */ for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { for (k = 0; k < n; k++) { for (l = 0; l < n; l++) { detA = (i * l - j * k) % n; if (detA < 0) detA += 26; As11 = (i * i + j * k) % n; As12 = (i * j + j * l) % n; As21 = (k * i + l * k) % n; As22 = (k * j + l * l) % n; Asqr = As11 == 1 && As12 == 0 && As21 == 0 && As22 == 1; if (Asqr) { count1++; if (detA == n - 1) { count2++; a11 = (1 - i * i) % m; if (a11 < 0) a11 += m; if (inverse(a11, m) != 0) count3++; else if (a11 == 0) count4++; else if (a11 == 13) count5++; else if (a11 % 2 == 0) count6++; } else if (detA == 1) count7++; } else count8++; } } } } printf("number of involutary matrices with det(A) = + 1: %3d\n", count7); printf("number of involutary matrices with det(A) = - 1: %3d\n", count2); printf("det(A) = - 1, (1 - a[1][1] ^ 2) %% 26 in Z_26 : %3d\n", count3); printf("det(A) = - 1, (1 - a[1][1] ^ 2) %% 26 = 0 : %3d\n", count4); printf("det(A) = - 1, (1 - a[1][1] ^ 2) %% 26 = 13 : %3d\n", count5);

printf("det(A) = - 1, (1 - a[1][1] ^ 2) %% 26 even count6); printf("total number of involutary matrices count1); printf("total number of matrices tested count1 + count8); printf("26 ** 4 = 26 ^ 4 = %f\n", pow(n, 4)); return 0; }

: %3d\n", : %3d\n", : %3d\n",

1.4
/*

ex0104a.c

(7,231)

Author:

Pate Williams (c) 1997

*/

Exercise 1.4 "Suppose we are told that the plaintext conversation yields the ciphertext hiarrtnuytus where the Hill Cipher is used (but m is not specified). Determine the encryption matrix." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 41. This is for m = 2, I had to change the ciphertext to "YUDOALQSRHYB".

#include <stdio.h> #include <stdlib.h> #include <string.h> long **create_square_matrix(long n) { long i, **matrix = calloc(n, sizeof(long *)); if (!matrix) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from create_matrix\n"); exit(1); } for (i = 0; i < n; i++) { matrix[i] = calloc(n, sizeof(long)); if (!matrix[i]) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from create_matrix\n"); exit(1); } } return matrix;

void delete_square_matrix(long n, long **matrix) { long i;

for (i = 0; i < n; i++) free(matrix[i]); free(matrix); } void Euclid_extended(long a, long b, long *u, long *v, long *d) { long q, t1, t3, v1, v3; *u = 1, *d = a; if (b == 0) { *v = 0; return; } v1 = 0, v3 = b; #ifdef DEBUG printf("----------------------------------\n"); printf(" q t3 *u *d t1 v1 v3\n"); printf("----------------------------------\n"); #endif while (v3 != 0) { q = *d / v3; t3 = *d - q * v3; t1 = *u - q * v1; *u = v1, *d = v3; #ifdef DEBUG printf("%4ld %4ld %4ld ", q, t3, *u); printf("%4ld %4ld %4ld %4ld\n", *d, t1, v1, v3); #endif v1 = t1, v3 = t3; } *v = (*d - a * *u) / b; #ifdef DEBUG printf("----------------------------------\n"); #endif

long inv(long number, long modulus) { long d, u, v; Euclid_extended(number, modulus, &u, &v, &d); if (d == 1) return u; return 0; } void gaussian_elimination(long n, long p, long *b, long *x, long **m) { int found; long *d = calloc(n, sizeof(long)), ck, dj; long i, j, k, l, sum, t; if (!d) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from gaussian_elimination\n");

exit(1); } for (j = 0; j < n; j++) { found = 0, i = j; while (!found && i < n) { found = m[i][j] != 0 && inv(m[i][j], p) != 0; if (!found) i++; } if (!found) { fprintf(stderr, "fatal error\nnon-invertible matrix\n"); fprintf(stderr, "from gaussian_elimination\n"); fprintf(stderr, "j = %ld\n", j); for (k = 0; k < n; k++) { for (l = 0; l < n; l++) printf("%2ld ", m[k][l]); printf("\n"); } exit(1); } if (i > j) { /* swap elements */ for (l = j; l < n; l++) t = m[i][l], m[i][l] = m[j][l], m[j][l] = t; t = b[i], b[i] = b[j], b[j] = t; } dj = d[j] = inv(m[j][j], p); if (dj == 0) { fprintf(stderr, "fatal error\nnon-invertible element\n"); fprintf(stderr, "from gaussian elimination\n"); fprintf(stderr, "element %ld mod %ld\n", m[j][j], p); exit(1); } for (k = j + 1; k < n; k++) { ck = (dj * m[k][j]) % p; for (l = j + 1; l < n; l++) { m[k][l] = (m[k][l] - ck * m[j][l]) % p; if (m[k][l] < 0) m[k][l] += p; } b[k] = (b[k] - ck * b[j]) % p; if (b[k] < 0) b[k] += p; } } for (i = n - 1; i >= 0; i--) { sum = 0; for (j = i + 1; j < n; j++) sum += (m[i][j] * x[j]) % p; if (sum < 0) sum += p; x[i] = (d[i] * (b[i] - sum)) % p; if (x[i] < 0) x[i] += p; } } void inverse(long n, long p, long **m, long **X) { int found; long d, i, j, k, l, sum, temp; long **B = create_square_matrix(n);

long *c = calloc(n, sizeof(long)); if (!c) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from inverse\n"); exit(1); } for (i = 0; i < n; i++) B[i][i] = 1; for (j = 0; j < n; j++) { found = 0; for (i = j; i < n && !found;) { found = m[i][j] != 0 && inv(m[i][j], p) != 0; if (!found) i++; } if (!found) { fprintf(stderr, "fatal error\nnon-invertible matrix\n"); fprintf(stderr, "from inverse\n", j); exit(1); } if (i > j) { for (l = j; l < n; l++) { temp = m[i][l]; m[i][l] = m[j][l]; m[j][l] = temp; } for (l = 0; l < n; l++) { temp = B[i][l]; B[i][l] = B[j][l]; B[j][l] = temp; } } d = inv(m[j][j], p); for (k = j + 1; k < n; k++) c[k] = (d * m[k][j]) % p; for (k = j + 1; k < n; k++) { for (l = j + 1; l < n; l++) { m[k][l] -= (c[k] * m[j][l]) % p; m[k][l] %= p; if (m[k][l] < 0) m[k][l] += p; } } for (k = j + 1; k < n; k++) { for (l = 0; l < n; l++) { B[k][l] -= (c[k] * B[j][l]) % p; B[k][l] %= p; if (B[k][l] < 0) B[k][l] += p; } } } for (i = n - 1; i >= 0; i--) { for (j = 0; j < n; j++) { sum = 0; for (k = i + 1; k < n; k++) sum += m[i][k] * X[k][j]; X[i][j] = inv(m[i][i], p) * (B[i][j] - sum); X[i][j] %= p; if (X[i][j] < 0) X[i][j] += p; }

} delete_square_matrix(n, B); free(c);

int main(void) { char ciphertext[16] = "YUDOALQSRHYB"; char plaintext[16] = "CONVERSATION"; long i, j, n = 4, p = 26, b[4], x[4]; long length = strlen(plaintext); long **K = create_square_matrix(n); long **k = create_square_matrix(n); printf("plaintext: %s\n", plaintext); printf("ciphertext: %s\n", ciphertext); for (i = 0; i < length; i++) { plaintext[i] -= (char) 'A'; ciphertext[i] -= (char) 'A'; } K[0][0] = plaintext[4]; K[0][2] = plaintext[5]; K[1][1] = plaintext[4]; K[1][3] = plaintext[5]; K[2][0] = plaintext[8]; K[2][2] = plaintext[9]; K[3][1] = plaintext[8]; K[3][3] = plaintext[9]; b[0] = ciphertext[4]; b[1] = ciphertext[5]; b[2] = ciphertext[8]; b[3] = ciphertext[9]; gaussian_elimination(n, p, b, x, K); K[0][0] = x[0]; K[0][1] = x[1]; K[1][0] = x[2]; K[1][1] = x[3]; printf("the key matrix is:\n"); for (i = 0; i < 2; i++) { for (j = 0; j < 2; j++) printf("%2ld ", K[i][j]); printf("\n"); } for (i = 0; i < length / 2; i++) { b[0] = K[0][0] * plaintext[2 * i] + K[1][0] * plaintext[2 * i + 1]; b[1] = K[0][1] * plaintext[2 * i] + K[1][1] * plaintext[2 * i + 1]; b[0] = b[0] % p; b[1] = b[1] % p; if (b[0] != ciphertext[2 * i] || b[1] != ciphertext[2 * i + 1]) printf("i = %ld no solution for m = 2\n", i); } inverse(2, p, K, k); printf("the inverse key matrix is:\n"); for (i = 0; i < 2; i++) {

} delete_square_matrix(n, k); delete_square_matrix(n, K); return 0;

for (j = 0; j < 2; j++) printf("%2ld ", k[i][j]); printf("\n");

1.5
/*

ex0105.c

(8,887)

Author:

Pate Williams (c) 1997

Exercise 1.5 "An Affine-Hill Cipher is the following modification of the Hill Cipher...Suppose Oscar has learned that the plaintext adisplayedequation is encrypted to give the ciphertext DSRMSIOPLXLJBZULLM and Oscar also knows m = 3. Compute the key." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 42. */ #include <stdio.h> #include <stdlib.h> #include <string.h> long **create_square_matrix(long n) { long i, **matrix = calloc(n, sizeof(long *)); if (!matrix) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from create_matrix\n"); exit(1); } for (i = 0; i < n; i++) { matrix[i] = calloc(n, sizeof(long)); if (!matrix[i]) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from create_matrix\n"); exit(1); } } return matrix; } void delete_square_matrix(long n, long **matrix) { long i; for (i = 0; i < n; i++) free(matrix[i]);

free(matrix);

void Euclid_extended(long a, long b, long *u, long *v, long *d) { long q, t1, t3, v1, v3; *u = 1, *d = a; if (b == 0) { *v = 0; return; } v1 = 0, v3 = b; #ifdef DEBUG printf("----------------------------------\n"); printf(" q t3 *u *d t1 v1 v3\n"); printf("----------------------------------\n"); #endif while (v3 != 0) { q = *d / v3; t3 = *d - q * v3; t1 = *u - q * v1; *u = v1, *d = v3; #ifdef DEBUG printf("%4ld %4ld %4ld ", q, t3, *u); printf("%4ld %4ld %4ld %4ld\n", *d, t1, v1, v3); #endif v1 = t1, v3 = t3; } *v = (*d - a * *u) / b; #ifdef DEBUG printf("----------------------------------\n"); #endif } long inv(long number, long modulus) { long d, u, v; Euclid_extended(number, modulus, &u, &v, &d); if (d == 1) return u; return 0;

void gaussian_elimination(long n, long p, long *b, long *x, long **m) { int found; long *d = calloc(n, sizeof(long)), ck, dj; long i, j, k, l, sum, t; if (!d) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from gaussian_elimination\n"); exit(1);

} for (j = 0; j < n; j++) { found = 0, i = j; while (!found && i < n) { found = m[i][j] != 0 && inv(m[i][j], p) != 0; if (!found) i++; } if (!found) { fprintf(stderr, "fatal error\nnon-invertible matrix\n"); fprintf(stderr, "from gaussian_elimination\n"); fprintf(stderr, "j = %ld\n", j); for (k = 0; k < n; k++) { for (l = 0; l < n; l++) printf("%2ld ", m[k][l]); printf("\n"); } exit(1); } if (i > j) { /* swap elements */ for (l = j; l < n; l++) t = m[i][l], m[i][l] = m[j][l], m[j][l] = t; t = b[i], b[i] = b[j], b[j] = t; } dj = d[j] = inv(m[j][j], p); if (dj == 0) { fprintf(stderr, "fatal error\nnon-invertible element\n"); fprintf(stderr, "from gaussian elimination\n"); fprintf(stderr, "element %ld mod %ld\n", m[j][j], p); exit(1); } for (k = j + 1; k < n; k++) { ck = (dj * m[k][j]) % p; for (l = j + 1; l < n; l++) { m[k][l] = (m[k][l] - ck * m[j][l]) % p; if (m[k][l] < 0) m[k][l] += p; } b[k] = (b[k] - ck * b[j]) % p; if (b[k] < 0) b[k] += p; } } for (i = n - 1; i >= 0; i--) { sum = 0; for (j = i + 1; j < n; j++) sum += (m[i][j] * x[j]) % p; if (sum < 0) sum += p; x[i] = (d[i] * (b[i] - sum)) % p; if (x[i] < 0) x[i] += p; }

void inverse(long n, long p, long **m, long **X) { int found; long d, i, j, k, l, sum, temp; long **B = create_square_matrix(n); long *c = calloc(n, sizeof(long));

if (!c) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from inverse\n"); exit(1); } for (i = 0; i < n; i++) B[i][i] = 1; for (j = 0; j < n; j++) { found = 0; for (i = j; i < n && !found;) { found = m[i][j] != 0 && inv(m[i][j], p) != 0; if (!found) i++; } if (!found) { fprintf(stderr, "fatal error\nnon-invertible matrix\n"); fprintf(stderr, "from inverse\n", j); exit(1); } if (i > j) { for (l = j; l < n; l++) { temp = m[i][l]; m[i][l] = m[j][l]; m[j][l] = temp; } for (l = 0; l < n; l++) { temp = B[i][l]; B[i][l] = B[j][l]; B[j][l] = temp; } } d = inv(m[j][j], p); for (k = j + 1; k < n; k++) c[k] = (d * m[k][j]) % p; for (k = j + 1; k < n; k++) { for (l = j + 1; l < n; l++) { m[k][l] -= (c[k] * m[j][l]) % p; m[k][l] %= p; if (m[k][l] < 0) m[k][l] += p; } } for (k = j + 1; k < n; k++) { for (l = 0; l < n; l++) { B[k][l] -= (c[k] * B[j][l]) % p; B[k][l] %= p; if (B[k][l] < 0) B[k][l] += p; } } } for (i = n - 1; i >= 0; i--) { for (j = 0; j < n; j++) { sum = 0; for (k = i + 1; k < n; k++) sum += m[i][k] * X[k][j]; X[i][j] = inv(m[i][i], p) * (B[i][j] - sum); X[i][j] %= p; if (X[i][j] < 0) X[i][j] += p; } }

delete_square_matrix(n, B); free(c); } int main(void) { char ciphertext[32] = "DSRMSIOPLXLJBZULLM"; char plaintext[32] = "ADISPLAYEDEQUATION"; long i, j, l = 0, n = 12, p = 26, b[12], x[12]; long length = strlen(plaintext); long **K = create_square_matrix(n); long **k = create_square_matrix(n); printf("plaintext: %s\n", plaintext); printf("ciphertext: %s\n", ciphertext); for (i = 0; i < length; i++) { ciphertext[i] -= (char) 'A'; plaintext[i] -= (char) 'A'; } K[0][0] = plaintext[0]; K[0][3] = plaintext[1]; K[0][6] = plaintext[2]; K[0][9] = 1; K[1][1] = plaintext[0]; K[1][4] = plaintext[1]; K[1][7] = plaintext[2]; K[1][10] = 1; K[2][2] = plaintext[0]; K[2][5] = plaintext[1]; K[2][8] = plaintext[2]; K[2][11] = 1; K[3][0] = plaintext[6]; K[3][3] = plaintext[7]; K[3][6] = plaintext[8]; K[3][9] = 1; K[4][1] = plaintext[6]; K[4][4] = plaintext[7]; K[4][7] = plaintext[8]; K[4][10] = 1; K[5][2] = plaintext[6]; K[5][5] = plaintext[7]; K[5][8] = plaintext[8]; K[5][11] = 1; K[6][0] = plaintext[9]; K[6][3] = plaintext[10]; K[6][6] = plaintext[11]; K[6][9] = 1; K[7][1] = plaintext[9]; K[7][4] = plaintext[10]; K[7][7] = plaintext[11]; K[7][10] = 1; K[8][2] = plaintext[9]; K[8][5] = plaintext[10]; K[8][8] = plaintext[11]; K[8][11] = 1; K[9][0] = plaintext[15]; K[9][3] = plaintext[16];

K[9][6] = plaintext[17]; K[9][9] = 1; K[10][1] = plaintext[15]; K[10][4] = plaintext[16]; K[10][7] = plaintext[17]; K[10][10] = 1; K[11][2] = plaintext[15]; K[11][5] = plaintext[16]; K[11][8] = plaintext[17]; K[11][11] = 1; b[0] = ciphertext[0]; b[1] = ciphertext[1]; b[2] = ciphertext[2]; b[3] = ciphertext[6]; b[4] = ciphertext[7]; b[5] = ciphertext[8]; b[6] = ciphertext[9]; b[7] = ciphertext[10]; b[8] = ciphertext[11]; b[9] = ciphertext[15]; b[10] = ciphertext[16]; b[11] = ciphertext[17]; gaussian_elimination(n, p, b, x, K); for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) K[i][j] = x[l++]; for (i = 0; i < 3; i++) b[i] = x[9 + i]; printf("the key matrix is:\n"); for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) printf("%2ld ", K[i][j]); printf("\n"); } printf("the b vector is:\n"); for (i = 0; i < 3; i++) printf("%2ld\n", b[i]); for (i = 0; i < length / 3; i++) { x[0] = K[0][0] * plaintext[3 * i] + K[1][0] * plaintext[3 * i + 1] + K[2][0] * plaintext[3 * i + 2] + b[0]; x[1] = K[0][1] * plaintext[3 * i] + K[1][1] * plaintext[3 * i + 1] + K[2][1] * plaintext[3 * i + 2] + b[1]; x[2] = K[0][2] * plaintext[3 * i] + K[1][2] * plaintext[3 * i + 1] + K[2][2] * plaintext[3 * i + 2] + b[2]; x[0] %= p; x[1] %= p; x[2] %= p; if (x[0] != ciphertext[3 * i] || x[1] != ciphertext[3 * i + 1] || x[2] != ciphertext[3 * i + 2]) printf("i = %ld no solution for m = 3\n", i);

} inverse(3, p, K, k); printf("the inverse key matrix is:\n"); for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) printf("%2ld ", k[i][j]); printf("\n"); } delete_square_matrix(n, k); delete_square_matrix(n, K); return 0;

1.6
/*

ex0106.c

(7,709)

Author:

Pate Williams (c) 1997

Exercise 1.6 "Here is how we might cryptanalyze the Hill Cipher using a ciphertext-only attack. Suppose we know that m = 2. Break the ciphertext into blocks of length two letters (digrams). Each such digram is the encryption of a plaintext digram using the unknown encryption matrix. Pick out the most frequent ciphertext digram and assume it is the encryption of a common digram in the list following Table 1.1 (for example TH or ST). For each such guess, proceed as in the known-plaintext attack, until the correct encryption matrix is found. Here is a sample of ciphertext for you to decrypt using this method: LMQETXYEAGTXCTUIEWNCTXLZEWUAISPZYVAPEWLMGQWYA XFTCJMSQCADAGTXLMDXNXSNPJQSYVAPRIQSMHNOCVAXFV" -Douglas R. StinsonSee "Cryptography: Theory and Practice by Douglas R. Stinson page 42. */ #include <stdio.h> #include <stdlib.h> #include <string.h> struct digram {long alpha1, alpha2, count;}; long **create_square_matrix(long n) { long i, **matrix = calloc(n, sizeof(long *)); if (!matrix) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from create_matrix\n"); exit(1); } for (i = 0; i < n; i++) { matrix[i] = calloc(n, sizeof(long));

} return matrix;

if (!matrix[i]) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from create_matrix\n"); exit(1); }

void delete_square_matrix(long n, long **matrix) { long i; for (i = 0; i < n; i++) free(matrix[i]); free(matrix);

void Euclid_extended(long a, long b, long *u, long *v, long *d) { long q, t1, t3, v1, v3; *u = 1, *d = a; if (b == 0) { *v = 0; return; } v1 = 0, v3 = b; #ifdef DEBUG printf("----------------------------------\n"); printf(" q t3 *u *d t1 v1 v3\n"); printf("----------------------------------\n"); #endif while (v3 != 0) { q = *d / v3; t3 = *d - q * v3; t1 = *u - q * v1; *u = v1, *d = v3; #ifdef DEBUG printf("%4ld %4ld %4ld ", q, t3, *u); printf("%4ld %4ld %4ld %4ld\n", *d, t1, v1, v3); #endif v1 = t1, v3 = t3; } *v = (*d - a * *u) / b; #ifdef DEBUG printf("----------------------------------\n"); #endif } long inv(long number, long modulus) { long d, u, v; Euclid_extended(number, modulus, &u, &v, &d); if (d == 1) return u; return 0;

} void gaussian_elimination(long n, long p, long *b, long *x, long **m) { int found; long *d = calloc(n, sizeof(long)), ck, dj; long i, j, k, l, sum, t; if (!d) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from gaussian_elimination\n"); exit(1); } for (j = 0; j < n; j++) { found = 0, i = j; while (!found && i < n) { found = m[i][j] != 0 && inv(m[i][j], p) != 0; if (!found) i++; } if (!found) { fprintf(stderr, "fatal error\nnon-invertible matrix\n"); fprintf(stderr, "from gaussian_elimination\n"); fprintf(stderr, "j = %ld\n", j); for (k = 0; k < n; k++) { for (l = 0; l < n; l++) printf("%2ld ", m[k][l]); printf("\n"); } exit(1); } if (i > j) { /* swap elements */ for (l = j; l < n; l++) t = m[i][l], m[i][l] = m[j][l], m[j][l] = t; t = b[i], b[i] = b[j], b[j] = t; } dj = d[j] = inv(m[j][j], p); if (dj == 0) { fprintf(stderr, "fatal error\nnon-invertible element\n"); fprintf(stderr, "from gaussian elimination\n"); fprintf(stderr, "element %ld mod %ld\n", m[j][j], p); exit(1); } for (k = j + 1; k < n; k++) { ck = (dj * m[k][j]) % p; for (l = j + 1; l < n; l++) { m[k][l] = (m[k][l] - ck * m[j][l]) % p; if (m[k][l] < 0) m[k][l] += p; } b[k] = (b[k] - ck * b[j]) % p; if (b[k] < 0) b[k] += p; } } for (i = n - 1; i >= 0; i--) { sum = 0;

} }

for (j = i + 1; j < n; j++) sum += (m[i][j] * x[j]) % p; if (sum < 0) sum += p; x[i] = (d[i] * (b[i] - sum)) % p; if (x[i] < 0) x[i] += p;

int main(void) { char cipher[5][25] = {"LMQETXYEAGTXCTUIEWNC", /*THEKINGWASINTHECOUNT*/ "TXLZEWUAISPZYVAPEWLM", /*INGHOUSECOUNTINGOUTH*/ "GQWYAXFTCJMSQCADAGTX", /*ISMONEYTHEQUEENWASIN*/ "LMDXNXSNPJQSYVAPRIQS", /*THEPARLOUREATINGBREA*/ "MHNOCVAXFV"}; /*DANDHONEY*/ char ciphertext[100], lt_ch, rt_ch; long count = 0, d_count = 0, i, j, k, n = 4, p = 26; long di_freq[26][26] = {{0}}; long det, b[4], x[4]; long **I = create_square_matrix(n); long **K = create_square_matrix(n); struct digram *d, temp; for (i = 0; i < 5; i++) for (j = 0; j < strlen(cipher[i]); j++) ciphertext[count++] = cipher[i][j]; for (i = 0; i < count; i++) ciphertext[i] -= (char) 'A'; for (i = 0; i < count; i += 2) { lt_ch = ciphertext[i]; rt_ch = ciphertext[i + 1]; di_freq[lt_ch][rt_ch]++; } for (i = 0; i < 26; i++) for (j = 0; j < 26; j++) if (di_freq[i][j] != 0) d_count++; d = calloc(d_count, sizeof(struct digram)); if (!d) { fprintf(stderr, "*error*\ninsufficient memory\n"); exit(1); } i = 0; for (j = 0; j < 26; j++) { for (k = 0; k < 26; k++) { if (di_freq[j][k] != 0) { d[i].alpha1 = j + 'A'; d[i].alpha2 = k + 'A'; d[i++].count = di_freq[j][k]; } } } /* sort the digrams using the selection sort */

for (i = 0; i < d_count - 1; i++) for (j = i + 1; j < d_count; j++) if (d[i].count < d[j].count) temp = d[i], d[i] = d[j], d[j] = temp; for (i = 0; i < d_count; i++) printf("%2ld %c %c %ld\n", i, d[i].alpha1, d[i].alpha2, d[i].count); K[0][0] = 'I' - 'A'; K[0][2] = 'N' - 'A'; K[1][1] = K[0][0]; K[1][3] = K[0][2]; K[2][0] = 'T' - 'A'; K[2][2] = 'H' - 'A'; K[3][1] = K[2][0]; K[3][3] = K[2][2]; b[0] = d[0].alpha1 - 'A'; b[1] = d[0].alpha2 - 'A'; b[2] = d[1].alpha1 - 'A'; b[3] = d[1].alpha2 - 'A'; gaussian_elimination(n, p, b, x, K); K[0][0] = x[0]; K[0][1] = x[1]; K[1][0] = x[2]; K[1][1] = x[3]; printf("the key matrix is:\n"); for (i = 0; i < 2; i++) { for (j = 0; j < 2; j++) printf("%2ld ", K[i][j]); printf("\n"); } det = (K[0][0] * K[1][1] - K[0][1] * K[1][0]) % p; if (det < 0) det += p; det = inv(det, p); if (det < 0) det += p; printf("det(K) = %ld\n", det); I[0][0] = (det * K[1][1]) % p; I[1][1] = (det * K[0][0]) % p; I[0][1] = - (det * K[0][1]) % p; I[1][0] = - (det * K[1][0]) % p; if (I[0][1] < 0) I[0][1] += p; if (I[1][0] < 0) I[1][0] += p; printf("the inverse key matrix is:\n"); for (i = 0; i < 2; i++) { for (j = 0; j < 2; j++) printf("%2ld ", I[i][j]); printf("\n"); } for (i = 0; i < count / 2; i++) { b[0] = I[0][0] * ciphertext[2 * i] + I[1][0] * ciphertext[2 * i + 1]; b[1] = I[0][1] * ciphertext[2 * i] + I[1][1] * ciphertext[2 * i + 1]; b[0] = b[0] % p + 'A'; b[1] = b[1] % p + 'A'; printf("%c%c", b[0], b[1]); if ((i + 1) % 10 == 0) printf("\n"); } delete_square_matrix(n, I);

delete_square_matrix(n, K); return 0; }

1.7
/* Author:

ex0107.c

(3,002)

Pate Williams (c) 1997

*/

Exercise 1.7 "We describe a special case of a Permutation Cipher. Let m, n be positive integers. Write out the plain text, by rows in m by n rectangles. Then form the ciphertext by taking columns of these retangles. For example if m = 4, n = 3, then we would encrypt the plaintext "cryptography" by forming the following rectangle: cryp togr aphy The ciphertext would be "CTAROPYGHPRY". (a) Describe how Bob would decrypt a ciphertext (given values for m and n). (b) Decrypt the following ciphertext, which was obtained by using this method of encryption: MYAMRARUYIQTENCTORAHROYWDSOYEOUARRGDERNOGW" -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 42.

#include <ctype.h> #include <stdio.h> #include <string.h> int main(void) { char cipher[2][24] = {"MYAMRARUYIQTENCTORAH", "ROYWDSOYEOUARRGDERNOGW"}; char answer[256], ciphertext[50], grid[50][50]; char test_ciphertext[16] = "CTAROPYGHPRY"; char plain[2][24] = {"MARYMARYQUITECONTRARY", "HOWDOESYOURGARDENGROW"}; char plaintext[50]; int found = 0; long count = 0, i, j, k = 0, m = 4, n = 3; for (j = 0; j < m; j++) for (i = 0; i < n; i++) grid[i][j] = test_ciphertext[k++]; for (i = 0; i < n; i++) for (j = 0; j < m; j++) printf("%c", grid[i][j]); printf("\n"); for (i = 0; i < 2; i++)

for (j = 0; j < strlen(cipher[i]); j++) ciphertext[count++] = cipher[i][j]; printf("count = %ld\n", count); for (n = 2; !found && n < count; n++) { if (count % n == 0) { k = 0; m = count / n; for (j = 0; j < m; j++) for (i = 0; i < n; i++) grid[i][j] = ciphertext[k++]; for (i = 0; i < n; i++) { for (j = 0; j < m; j++) printf("%c", grid[i][j]); printf("\n"); } for (i = 0; i < n; i++) for (j = 0; j < m; j++) printf("%c", grid[i][j]); printf("\n"); printf("another value of n = %ld (n or y)? ", n); scanf("%s", answer); found = tolower(answer[0] == 'n'); } } count = 0; for (i = 0; i < 2; i++) for (j = 0; j < strlen(plain[i]); j++) plaintext[count++] = plain[i][j]; found = 0; for (n = 2; !found && n < count; n++) { if (count % n == 0) { k = 0; m = count / n; for (i = 0; i < n; i++) for (j = 0; j < m; j++) grid[i][j] = plaintext[k++]; for (i = 0; i < n; i++) { for (j = 0; j < m; j++) printf("%c", grid[i][j]); printf("\n"); } for (j = 0; j < m; j++) for (i = 0; i < n; i++) printf("%c", grid[i][j]); printf("\n"); printf("next value of n = %ld (n or y)? ", n); scanf("%s", answer); found = tolower(answer[0]) == 'n'; } } return 0;

1.8
/*

ex0108.c

(1,169)

Author:

Pate Williams (c) 1997

Exercise 1.8 "There are eight different linear recurrences over Z_2 of degree four having c0 = 1. Determine which of these recurrences give rise to a keystream of period 15 (given a non-zero initialization vector)." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 42. */ #include <stdio.h> int main(void) { int found; long c0 = 1, c1, c2, c3, i, j, z[50]; z[0] = 1, z[1] = z[2] = z[3] = 0; for (c1 = 0; c1 < 2; c1++) { for (c2 = 0; c2 < 2; c2++) { for (c3 = 0; c3 < 2; c3++) { printf("1%ld%ld%ld 1000", c1, c2, c3); for (i = 0; i < 32; i++) { z[i + 4] = (c0 * z[i] + c1 * z[i + 1] + c2 * z[i + 2] + c3 * z[i + 3]) % 2; printf("%ld", z[i + 4]); } for (i = 2; i <= 15; i++) { found = 1; for (j = 0; found && j < i; j++) found = z[j] == z[j + i]; if (found) { j = i; break; } } if (found) printf(" %ld\n", j); } } } return 0; }

1.10
/* Author:

ex0110.c

(1,031)

Pate Williams (c) 1997

Exercise 1.10 "Decrypt the following ciphertext, obtained from the Autokey Cipher, by using exhaustive key search: MALVVMAFBHBUQPTSOXALTGVWWRG" -Douglas R. Stinson-

See "Cryptography: Theory and Practice" by Douglas R. Stinson page 43. */ #include <ctype.h> #include <stdio.h> #include <string.h> int main(void) { char answer[256]; char ciphertext[] = "MALVVMAFBHBUQPTSOXALTGVWWRG"; int found = 0; long count = strlen(ciphertext), i, j, x, y; for (i = 0; i < count; i++) ciphertext[i] -= (char) 'A'; for (i = 0; !found && i < 26; i++) { x = (ciphertext[0] - i) % 26; if (x < 0) x += 26; printf("%c", x + 'A'); for (j = 1; j < count; j++) { y = (ciphertext[j] - x) % 26; if (y < 0) y += 26; printf("%c", y + 'A'); x = y; } printf("\n"); printf("another key = %c (n or y)? ", i + 'A'); scanf("%s", answer); found = tolower(answer[0]) == 'n'; } return 0;

Chapter 03

The Data Encryption Standard

If you are a citizen of the United States and are currently residing in the United States with an e-mail address that is verifiably in the United States, then e-mail me to get a copy of Example 3.3 and/or Exercise 3.8 by e-mailing me at pate@wplag.mindspring.com Please specify the either dc3.c and/or dc4.c and MIME or UUencoded format for the return e-mail attachment, or request that I put the source code in an e-mail if your system does not handle attachments. You must confirm in writing that are a citizen of the U.S. and currently residing in the U.S. Don't forget to include your e-mail address. The current versions of these differential cryptanalytic programs work on little-endian processors such as the Intel family of processors.

Chapter 04 4.1 4.2 4.6 4.7 4.8 4.12 4.13 4.16 4.18 4.19
Exercises

The RSA System and Factoring (1,157) (1,349) (3,999) (2,613) (2,798) (1,157) (1,363) (1,684) (4,454) (1,420)

ex0401.c ex0402.c ex0406.c ex0407.c ex0408.c ex0412.c ex0413.c ex0416.c ex0418.c ex0419.c

4.1
/* Author:

ex0401.c

(1,157)

Pate Williams (c) 1997

*/

Exercise "4.1 Use the Extended Euclidean algorithm to compute the following multiplicative inverses: (a) 17 ^ - 1 mod 101 (b) 357 ^ - 1 mod 1234 (c) 3125 ^ - 1 mod 9987." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 157.

#include <stdio.h> long Extended_Euclidean(long b, long n) {

long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

int main(void) { long b[3] = {17, 357, 3125}; long n[3] = {101, 1234, 9987}; long i, x, y; for (i = 0; i < 3; i++) { x = Extended_Euclidean(b[i], n[i]); printf("the inverse of %4ld modulo %4ld = %4ld\n", b[i], n[i], x); y = (x * b[i]) % n[i]; if (y != 1) printf("*error*\nin inverse calculation\n"); } return 0; }

4.2
/* Author:

ex0402.c

(1,349)

Pate Williams (c) 1997

*/

Exercise "4.2 Solve the following system of congruences: x = 12 (mod 25) x = 9 (mod 26) x = 23 (mod 27)." -Douglas R. SinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 157.

#include <stdio.h> #define SIZE 256l long Extended_Euclidean(long b, long n) {

long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

long Chinese_Remainder(long r, long *a, long *m) { long i, N = 1, M[SIZE], y[SIZE], x = 0; for (i for (i M[i] y[i] x += } return } int main(void) { long a[3] = {12, 9, 23}; long m[3] = {25, 26, 27}; long i, r = 3, x; x = Chinese_Remainder(r, a, m); for (i = 0; i < 3; i++) { printf("x = %2ld mod %2ld\n", a[i], m[i]); if (x % m[i] != a[i]) printf("*error\nin CRT calculation\n"); } printf("x = %ld\n", x); return 0; } = 0; i < r; i++) N *= m[i]; = 0; i < r; i++) { = N / m[i]; = Extended_Euclidean(M[i], m[i]); (a[i] * M[i] * y[i]) % N; x;

4.6
/* Author:

ex0406.c

(3,999)

Pate Williams (c) 1997

Exercise "4.6 Two samples of RSA cipertext are presented in Tables 4.1 and 4.2. Your task is to decrypt them. The public parameters of the system are n = 18923 and b = 1261 (for Table 4.1) and n = 31313 and b = 4913 (for Table 4.2)." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson pages 157-158. */ #include <math.h> #include <stdio.h> #define BITS_PER_LONG 32l #define BITS_PER_LONG_1 31l long get_bit(long i, long *sieve) { long b = i % BITS_PER_LONG; long c = i / BITS_PER_LONG; return (sieve[c] >> (BITS_PER_LONG_1 - b)) & 1; } void set_bit(long i, long v, long *sieve) { long b = i % BITS_PER_LONG; long c = i / BITS_PER_LONG; long mask = 1 << (BITS_PER_LONG_1 - b); if (v == 1) sieve[c] |= mask; else sieve[c] &= ~mask; } void Sieve(long n, long *sieve) { long c, i, inc; set_bit(0l, 0l, sieve); set_bit(1l, 0l, sieve); set_bit(2l, 1l, sieve); for (i = 3; i <= n; i++) set_bit(i, i & 1, sieve); c = 3; do { i = c * c, inc = c + c; while (i <= n) { set_bit(i, 0l, sieve); i += inc; } c += 2; while (!get_bit(c, sieve)) c++; } while (c * c <= n);

long factor(long n, long *sieve) /* factor using trial division */ { int found = 0; long p = 0, s = sqrt(n); while (!found && p <= s) { while (!get_bit(p, sieve)) p++; found = n % p == 0; if (!found) p++; } return p; } long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a; } int main(void) { char abc[26] = "abcdefghijklmnopqrstuvwxyz"; long b[2] = {1261l, 4913l}; long n[2] = {18923l, 31313l}; long c[2][32] = {{12423l, 11524l, 7243l, 7459l, 14303l, 6127l, 10964l, 16399l, 9792l, 13629l, 14407l, 18817l, 18830l, 13556l, 3159l, 16647l,

5300l, 13951l, 8007l, 13167l, 2264l, 961l, 2999l, 14569l, { 6340l, 8309l, 27358l, 25023l, 23614l, 7135l, 27570l, 26486l, 27584l, 14999l, 29421l, 26439l, 25774l, 7647l, 25774l, 18436l, long i, j, sieve[10000], m, p, q; long a, bi, ni, phi, t;

81l, 10022l, 17459l, 17183l, 14010l, 16481l, 24996l, 30388l, 4517l, 1606l, 23901l, 12056l,

8986l, 17213l, 4101l, 15827l}, 8936l, 25809l, 30590l, 9395l, 12146l, 17881l, 7372l, 13547l}};

Sieve(n[1], sieve); for (i = 0; i < 2; i++) { bi = b[i]; ni = n[i]; p = factor(ni, sieve); q = ni / p; printf("p = %5ld q = %5ld n = %5ld\n", p, q, ni); phi = (p - 1) * (q - 1); a = Extended_Euclidean(bi, phi); printf("a = %5ld b = %5ld\n", a, bi); printf("%5ld * %5ld mod %5ld = %5ld\n", a, bi, phi, (a * bi) % phi); for (j = 0; j < 32; j++) { m = exp_mod(c[i][j], a, ni); if (c[i][j] != exp_mod(m, bi, ni)) printf("*error*\in RSA\n"); t = m / 676l; m = m % 676l; printf("%c", abc[t]); t = m / 26l; m = m % 26l; printf("%c", abc[t]); printf("%c", abc[m]); if ((j + 1) % 8 == 0) printf("\n"); } printf("\n"); } return 0;

4.7
/* Author:

ex0407.c

(2,613)

Pate Williams (c) 1997

Exercise "4.7 This exercise exhibits what is called a protocol failure. Suppose Bob has an RSA Cyyptosystem with a large modulus n for which the factorization cannot be found in a reasonable amount of time. Suppose Alice sends

a message to Bob represnting each alphabetic character as an integer between 0 and 25, and then encrypting each residue modulo 26 as a separate plaintext character. (a) describe how Oscar can easily decrypt a message that is encrypted in this way. (b) Illustrate the attack by decrypting the following ciphertext (which was encrypted using a RSA Cryptosystem with n = 18721 and b = 25) without factoring the modulus: 365, 0, 4845, 14930, 2608, 2608, 0." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson pages 158-159. */ #include <stdio.h> #include <stdlib.h> struct data {long c; long l;}; long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a; } int fcmp(const void *d1, const void *d2) { struct data *data1 = (struct data *) d1; struct data *data2 = (struct data *) d2; if (data1->l < data2->l) return - 1; if (data1->l > data2->l) return + 1; return 0; } void decrypt(long b, long length, long n, long *c) { long i; struct data d[26], e, *p; /* create a table of possible ciphertext values */ for (i = 0; i < 26; i++) { d[i].c = 'a' + i; d[i].l = exp_mod(i, b, n); } /* sort the table on the ciphertext value */ qsort(d, 26, sizeof(struct data), fcmp);

printf("the possible cipher characters are as follows:\n"); for (i = 0; i < 26; i++) { printf("%c %5ld ", d[i].c, d[i].l); if ((i + 1) % 4 == 0) printf("\n"); } printf("\nthe decrypted message is as follows:\n"); for (i = 0; i < length; i++) { e.c = 'a'; e.l = c[i]; p = bsearch(&e, d, 26, sizeof(struct data), fcmp); if (!p) { fprintf(stderr, "fatal error\n"); fprintf(stderr, "binary search failed\n"); exit(1); } printf("%c", p->c); }

int main(void) { long b = 25, i, n = 18721; long c[7] = {365, 0, 4845, 14930, 2608, 2608, 0}; long length = 7; printf("b = %5ld n = %5ld\n", b, n); printf("the ciphertext is as follows:\n"); for (i = 0; i < length; i++) printf("%5ld ", c[i]); printf("\n"); decrypt(b, length, n, c); return 0; }

4.8
/* Author:

ex0408.c

(2,798)

Pate Williams (c) 1997

Exercise "4.8 This exercise illustrates another example of a protocol failure (due to Simmons) involving RSA; it is called the common modulus protocol failure. Suppose Bob has an RSA Cryptosystem with modulus n and encryption exponent b1, and Charlie has an RSA Cryptosystem with (the same) modulus n and encryption exponent b2. Suppose that gcd(b1, b2) = 1. Now, consisder the situation that arises if Alice encrypts the same plaintext x to send to both Bob and Charlie. Thus, she computes y1 = x ^ b1 mod n and y2 = x ^ b2 mod n, and sends y1 to Bob and y2 to Charlie. Suppose Oscar intercepts y1 and y2, and performs the following computations: 1. compute c1 = b1 ^ - 1 mod b2 2. compute c2 = (c1 * b1 - 1) / b2

*/

3. compute x1 = y1 ^ c1 * (y2 ^ c2) ^ - 1 mod n (b) Illustrate this attack by computing x by this method if n = 18721, b1 = 945, b2 = 7717, y1 = 10510, and y2 = 14702." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 159.

#include <stdio.h> #include <stdlib.h> long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n; } long gcd(long a, long b) /* Euclid's algorithm */ { long r; while (b != 0) r = a % b, a = b, b = r; return a; } int main(void)

long b1 = 945l, b2 = 7717l, n = 18721l; long y1 = 10510l, y2 = 14702l; long c1, c2, x, x1, x2; if (gcd(b1, b2) != 1) { fprintf(stderr, "fatal error\n"); fprintf(stderr, "gcd(b1, b2) != 1\n"); exit(1); } c1 = Extended_Euclidean(b1, b2); c2 = (c1 * b1 - 1) / b2; x1 = exp_mod(y1, c1, n); x2 = exp_mod(y2, c2, n); x2 = Extended_Euclidean(x2, n); x = (x1 * x2) % n; x1 = exp_mod(x, b1, n); x2 = exp_mod(x, b2, n); printf("b1 = %5ld b2 = %5ld n = %ld\n", b1, b2, n); printf("c1 = %5ld c2 = %5ld\n", c1, c2); printf("y1 = %5ld y2 = %5ld x = %ld\n", y1, y2, x); printf("y1 = %5ld y2 = %5ld\n", x1, x2); if (x1 != y1 || x2 != y2) { fprintf(stderr, "fatal error\n"); fprintf(stderr, "can't compute y1 and y2 from x\n"); fprintf(stderr, "y1 %5ld y2 %5ld\n", x1, x2); exit(1); } return 0;

4.12
/* Author:

ex0412.c

(1,157)

Pate Williams (c) 1997

Exercise "4.12 Write a program to evaluate Jacobi symbols using the four properties presented in Section 4.5. The program should not do any factoring, other than dividing out powers of two. Test your program by computing the following Jacobi symbols: (610 / 987), (20964 / 1987), (1234567 / 11111111)." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 160. */ #include <stdio.h> int JACOBI(long a, long n) { int s; long a1, b = a, e = 0, m, n1;

if (a == 0) return 0; if (a == 1) return 1; while ((b & 1) == 0) b >>= 1, e++; a1 = b; m = n % 8; if (!(e & 1)) s = 1; else if (m == 1 || m == 7) s = else if (m == 3 || m == 5) s = if (n % 4 == 3 && a1 % 4 == 3) if (a1 != 1) n1 = n % a1; else return s * JACOBI(n1, a1); }

+ 1; - 1; s = - s; n1 = 1;

int main(void) { long a[3] = {610l, 20964l, 1234567l}; long n[3] = {987l, 1987l, 11111111l}; long i; for (i = 0; i < 3; i++) printf("(%8ld / %8ld) = %+d\n", a[i], n[i], JACOBI(a[i], n[i])); return 0; }

4.13
/* Author:

ex0413.c

(1,363)

Pate Williams (c) 1997

Exercise "4.13 Write a program that computes the number of Euler pseudo-primes to the bases 837, 851, and 1189." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 160. */ #include <stdio.h> int JACOBI(long a, long n) { int s; long a1, b = a, e = 0, m, n1; if (a == 0) return 0; if (a == 1) return 1; while ((b & 1) == 0) b >>= 1, e++; a1 = b; m = n % 8; if (!(e & 1)) s = 1; else if (m == 1 || m == 7) s = + 1; else if (m == 3 || m == 5) s = - 1; if (n % 4 == 3 && a1 % 4 == 3) s = - s;

if (a1 != 1) n1 = n % a1; else n1 = 1; return s * JACOBI(n1, a1); } long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

int main(void) { int j; long a, c, i, n[3] = {837l, 851l, 1189l}; long n1, n2, ni, x; for (i = 0; i < 3; i++) { c = 0; ni = n[i]; n1 = ni - 1; n2 = n1 >> 1; for (a = 2l; a < ni; a++) { x = exp_mod(a, n2, ni); j = JACOBI(a, ni); if (j == - 1 && x == n1) c++; else if (j == x) c++; } printf("%ld Euler pseudo-prime(s) base %4ld\n", c, ni); } return 0;

4.16
/* Author:

ex0416.c

(1,684)

Pate Williams (c) 1997

Exercise "4.16 Suppose Bob has carelessly revealed his decryption exponent to be a = 14039 in an RSA Cryptosystem with public key n = 36581 and b = 4679. Implement the probabilistic algorithm to factor n given this information. Test your algorithm with the "random" choices w = 9983 and w = 13641. Show all computations." -Douglas R. StinsonSee "Cryptography: Theory and Practice by Douglas R. Stinson page 161. */

#include <stdio.h> long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a; } long gcd(long a, long b) /* Euclid's algorithm */ { long r; while (b != 0) r = a % b, a = b, b = r; return a; } int factor(long a, long b, long n, long w, long *x) { long r = a * b - 1, s = 0, v, v0; *x = gcd(w, n); printf("x = %ld\n", *x); if (*x > 1 && *x < n) return 1; while (!(r & 1)) r >>= 1, s++; v = exp_mod(w, r, n); printf("w = %ld\n", w); printf("r = %ld\n", r); printf("s = %ld\n", s); printf("v = %ld\n", v); if (v == 1) return 0; while (v != 1) { v0 = v; v = (v * v) % n; printf("v0 = %ld\n", v0); printf("v = %ld\n", v); } if (v0 == n - 1) return 0; *x = gcd(v0 + 1, n); printf("x = %ld\n", *x); return 1; } int main(void) { long a = 14039l, b = 4679l, n = 36581l; long w1 = 9983l, w2 = 13461l, x;

printf("success = %d\n", factor(a, b, n, w1, &x)); printf("success = %d\n", factor(a, b, n, w2, &x)); return 0;

4.18
/* Author:

ex0418.c

(4,454)

Pate Williams (c) 1997

*/

Exercise "4.18 Suppose p = 199, q = 211, and B = 1357 in the Rabin Cryptosystem. Perform the following computations. (a) Determine the four square roots of 1 modulo n, where n = pq. (b) Compute the encryption y = ek(32767). (c) Determine the four possible decryptions of this given ciphetext." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 161.

#include <math.h> #include <stdio.h> #include <stdlib.h> int JACOBI(long a, long n) { int s; long a1, b = a, e = 0, m, n1; if (a == 0) return 0; if (a == 1) return 1; while ((b & 1) == 0) b >>= 1, e++; a1 = b; m = n % 8; if (!(e & 1)) s = 1; else if (m == 1 || m == 7) s = else if (m == 3 || m == 5) s = if (n % 4 == 3 && a1 % 4 == 3) if (a1 != 1) n1 = n % a1; else return s * JACOBI(n1, a1); } long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; if (b == 0) return 1; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1;

+ 1; - 1; s = - s; n1 = 1;

if (b != 0) s = (s * s) % n; } return a;

void extended_euclid(long a, long b, long *x, long *y, long *d) /* calculates a * *x + b * *y = gcd(a, b) = *d */ { long q, r, x1, x2, y1, y2; if (b == 0) { *d = a, *x = 1, *y = 0; return; } x2 = 1, x1 = 0, y2 = 0, y1 = 1; while (b > 0) { q = a / b, r = a - q * b; *x = x2 - q * x1, *y = y2 - q * y1; a = b, b = r; x2 = x1, x1 = *x, y2 = y1, y1 = *y; } *d = a, *x = x2, *y = y2;

long inverse(long a, long n) /* computes the inverse of a modulo n */ { long d, x, y; extended_euclid(a, n, &x, &y, &d); if (x < 0) x += n; if (d == 1) return x; return 0; } void sqrt_mod(long a, long p, long *r) { long ai, b, c, d, i, p1 = p - 1, s = 0, t = p1; long x, y; if (JACOBI(a, p) == - 1) { *r = 0; return; } do do b = rand() % p; while (b == 0); while (JACOBI(b, p) != - 1); while (!(t & 1)) t >>= 1, s++; ai = inverse(a, p); if ((ai * a) % p != 1) { printf("*error*\nin inverse\n"); printf("a = %ld a ^ - 1 = %ld\n", a, ai); } c = exp_mod(b, t, p); *r = exp_mod(a, (t + 1) >> 1, p); x = pow(2l, s);

for (i = 1; i < s; i++) { y = (((*r * *r) % p) * ai) % p; d = exp_mod(y, x, p); if (d < 0) d += p; if (d == p1) *r = (*r * c) % p; c = (c * c) % p; x *= 2; } if (*r < 0) *r += p; a, long p, long q, *x1, long *x2, *y1, long *y2) p * q, r, s, x, y;

void square_roots(long long long { long b, c, d, e, n =

if (a < p) b = a; else b = a % p; sqrt_mod(b, p, &r); if ((r * r) % p != b) { printf("*error*\nin sqrt_mod p\n"); printf("(a / p) = %d\n", JACOBI(a, p)); printf("%ld * %ld %% %ld = %ld\n", r, r, p, (r * r) % p); } if (a < q) b = a; else b = a % q; sqrt_mod(b, q, &s); if ((s * s) % q != b) { printf("*error*\nin sqrt_mod q\n"); printf("(a / q) = %d\n", JACOBI(a, q)); printf("%ld * %ld %% %ld = %ld\n", s, s, q, (s * s) % q); } extended_euclid(p, q, &c, &d, &e); x = (r * d * q + s * c * p) % n; y = (r * d * q - s * c * p) % n; *x1 = x; *x2 = - x % n; if (*x1 < 0) *x1 += n; if (*x2 < 0) *x2 += n; *y1 = y; *y2 = - y % n; if (*y1 < 0) *y1 += n; if (*y2 < 0) *y2 += n; } int main(void) { long B = 1357l, B2, B4; long a = 1l, p = 199l, q = 211l; long i, n = p * q, s[4]; long x = 32767l, y, z; square_roots(a, p, q, &s[0], &s[1], &s[2], &s[3]); printf("the four square roots of %ld mod %ld are:\n", a, n); for (i = 0; i < 4; i++) { printf("%5ld ", s[i]);

if ((s[i] * s[i]) % n != a) printf("*error*\nin square_roots\n"); } B2 = (B * inverse(2l, n)) % n; B4 = (((B * B) % n) * inverse(4l, n)) % n; y = (x * ((x + B) % n)) % n; printf("\nek(%5ld) = %5ld\n", x, y); z = (B4 + y) % n; square_roots(z, p, q, &s[0], &s[1], &s[2], &s[3]); printf("the four possible plaintexts are:\n"); for (i = 0; i < 4; i++) { z = (s[i] - B2) % n; if (z < 0) z += n; printf("%5ld ", z); if ((z * (z + B)) % n != y) printf("*error*\nin square_roots\n"); } return 0; }

4.19
/* Author:

ex0419.c

(1,420)

Pate Williams (c) 1997

Exercise "4.19 Factor 262063 and 9420457 using the p - 1 method. How big does B have to be each case to be successful." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 161. */ #include <stdio.h> #define B_MAX 10000l long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

long gcd(long a, long b) /* Euclid's algorithm */ { long r; while (b != 0)

r = a % b, a = b, b = r; return a; } int p_1(long B, long n, long *d) { long a = 2, j; for (j = 2; j <= B; j++) a = exp_mod(a, j, n); *d = gcd(a - 1, n); if (*d > 1 && *d < n) return 1; return 0;

int main(void) { int found; long B, d, e, i, ni; long n[2] = {262063l, 9420457l}; for (i = 0; i < 2; i++) { ni = n[i]; found = 0; for (B = 2; !found && B <= B_MAX; B++) found = p_1(B, ni, &d); if (found) { e = ni / d; if (d * e != ni) printf("*error*\in p_1\n"); else { printf("%7ld = %4ld * %4ld ", ni, d, e); printf("B = %3ld\n", B); } } else printf("%ld not factored in %ld attempts\n", ni, B_MAX - 1); } return 0; }

Chapter 05 5.1 5.7 5.1 5.2

Other Public-key Cryptosystems (3,315) (5,097) freelip (3,062) (4,680)

te0501.c te0507.c ex0501.c ex0502.c

5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.10 5.11

ex0503.c ex0504.c ex0505.txt ex0506.c ex0507.c ex0508.c ex0509.c ex0510.c ex0511.c

(2,138) (3,154) (517) (5,773) (7,323) (4,813) (6,316) (2,658) (4,425)

5.1
/* Author:

te0501.c

(3,315)

Pate Williams (c) 1997

*/

"Example 5.2. Suppose p = 809, and we wish to find log(3, 525)." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson pages 165 - 166.

#include <math.h> #include <stdio.h> #include <stdlib.h> struct data {long j, alpha_j;}; long exp_mod(long x, long b, long n) /* returns x ^ b mod n */

long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } if (a < 0) a += n; return a;

} long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

int fcmp(const void *d1, const void *d2) { struct data *data1 = (struct data *) d1; struct data *data2 = (struct data *) d2; if (data1->alpha_j < data2->alpha_j) return - 1; if (data1->alpha_j > data2->alpha_j) return + 1; return 0;

long Shanks(long alpha, long beta, long p) /* returns log(alpha, beta) in Z_p where alpha is a generator and beta is in Z_p */ { long a, alpha_i, i, log, m = ceil(sqrt(p - 1)); struct data *table1 = calloc(m, sizeof(struct data)); struct data *table2 = calloc(m, sizeof(struct data)); struct data *d, key; if (!table1 || !table2) { fprintf(stderr, "*error*\nin sufficient memory\n"); fprintf(stderr, "from Shanks\n"); exit(1); }

/* create a logarithm table */ alpha_i = exp_mod(alpha, m, p); for (i = 0; i < m; i++) { table1[i].j = i; table1[i].alpha_j = exp_mod(alpha_i, i, p); } alpha_i = Extended_Euclidean(alpha, p); if ((alpha_i * alpha) % p != 1) { fprintf(stderr, "*error*\nin Extended_Euclidean\n"); exit(1); } for (i = 0; i < m; i++) { table2[i].j = i; a = exp_mod(alpha_i, i, p); table2[i].alpha_j = (beta * a) % p; } for (i = 0; i < m; i++) { printf("(%3ld, %3ld) ", table1[i].j, table1[i].alpha_j); if ((i + 1) % 5 == 0) printf("\n"); } printf("\n"); for (i = 0; i < m; i++) { printf("(%3ld, %3ld) ", table2[i].j, table2[i].alpha_j); if ((i + 1) % 5 == 0) printf("\n"); } printf("\n"); /* sort the tables by second data item */ qsort(table1, m, sizeof(struct data), fcmp); qsort(table2, m, sizeof(struct data), fcmp); for (i = 0; i < m; i++) { key.j = table1[i].j; key.alpha_j = table1[i].alpha_j; d = bsearch(&key, table2, m, sizeof(struct data), fcmp); if (d) { log = (key.j * m + d->j) % (p - 1); printf("log(%3ld, %3ld) = %2ld * %2ld + %ld\n", alpha, beta, m, key.j, d->j); printf(" = %3ld in Z_p%3ld\n", log, p); if (exp_mod(alpha, log, p) == beta) return log; } } return 0;

int main(void) { long alpha = 3, beta = 525l, p = 809l, log; log = Shanks(alpha, beta, p); if (log == 0) printf("log(%3ld, %3ld) in Z_%3ld does not exist\n", alpha, beta, p); return 0; }

5.7
/* Author:

te0507.c

(5,097)

Pate Williams (c) 1997

*/

Example 5.7 "Let E be the elliptic curve y ^ 2 = x ^ 3 + x + 6 defined over Z_11. (a) Determine the number of points on E. (b) Show that E a cyclic group. -Dougals R. StinsonSee "Cryptography: Theory and Practice by Douglas R. Stinson pages 185-186.

#include <math.h> #include <stdio.h> #include <stdlib.h> #define MAX_POINTS 142l struct point {long x, y;}; int JACOBI(long a, long n) { int s; long a1, b = a, e = 0, m, n1; if (a == 0) return 0; if (a == 1) return 1; while ((b & 1) == 0) b >>= 1, e++; a1 = b; m = n % 8; if (!(e & 1)) s = 1; else if (m == 1 || m == 7) s = else if (m == 3 || m == 5) s = if (n % 4 == 3 && a1 % 4 == 3) if (a1 != 1) n1 = n % a1; else return s * JACOBI(n1, a1); } long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; if (b == 0) return 1; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } if (a < 0) a += n; return a;

+ 1; - 1; s = - s; n1 = 1;

long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n; } long E_points(long a, long b, long p, struct point *e) /* returns the number of points on the elliptic curve y ^ 2 = x ^ 3 + ax + b mod p */ { long count = 0, m = (p + 1) / 4, x, y; if (p % 4 == 3) { for (x = 0; x < p; x++) { y = x * x * x + a * x + b; if (JACOBI(y, p) != - 1) { y = exp_mod(y, m, p); printf("(%2ld, %2ld) ", x, y); e[count].x = x; e[count].y = y; y = - y % p; if (y < 0) y += p; printf("(%2ld, %2ld) ", x, y); e[count + 1].x = x; e[count + 1].y = y; count += 2; if (count % 4 == 0) printf("\n"); } } if (count % 4 != 0) printf("\n"); } return count;

void add(long a, long p, struct point P, struct point Q, struct point *R) /* elliptic curve point partial addition */ { long i, lambda;

if (P.x == Q.x && P.y == 0 && Q.y == R->x = 0; R->y = 1; return; } if (P.x == Q.x && P.y == p - Q.y) { R->x = 0; R->y = 1; return; } if (P.x == 0 && P.y == 1) { *R = Q; return; } if (Q.x == 0 && Q.y == 1) { *R = P; return; } if (P.x != Q.x) { i = Q.x - P.x; if (i < 0) i += p; i = Extended_Euclidean(i, p); lambda = ((Q.y - P.y) * i) % p; } else { i = Extended_Euclidean((2 * P.y) % lambda = ((3 * P.x * P.x + a) * i) } if (lambda < 0) lambda += p; R->x = (lambda * lambda - P.x - Q.x) R->y = (lambda * (P.x - R->x) - P.y) if (R->x < 0) R->x += p; if (R->y < 0) R->y += p;

0) {

p, p); % p; % p; % p;

void multiply(long a, long k, long p, struct point P, struct point *R) { struct point S; R->x = 0; R->y = 1; S = P; while (k != 0) { if (k & 1) add(a, p, *R, S, R); k >>= 1; if (k != 0) add(a, p, S, S, &S); }

long order(long a, long p, struct point P) { long ord = 1; struct point Q = P, R; do {

ord++; add(a, p, P, Q, &R); Q = R; } while (R.x != 0 && R.y != 1); return ord;

int main(void) { long a = 1, b = 6, h, i, j, p = 11; long max_order, min_order, noncyclic, ord; struct point e[MAX_POINTS], P, Q, R; printf("E: y ^ 2 = x ^ 3 + %ld * x + %ld mod %ld\n", a, b, p); printf("the the non-origin points (x, y) on E are:\n"); h = E_points(a, b, p, e) + 1; printf("the number of non-origin points on E is: %ld\n", h - 1); max_order = 0; min_order = h + 1; printf("the non-origin points (x, y) on E "); printf("and their orders are:\n"); for (i = 0; i < h - 1; i++) { ord = order(a, p, e[i]); if (ord < h) noncyclic = 1; if (ord < min_order) { P = e[i]; min_order = ord; } if (ord > max_order) { Q = e[i]; max_order = ord; } printf("(%2ld, %2ld) %2ld ", e[i].x, e[i].y, ord); if ((i + 1) % 3 == 0) printf("\n"); } if (noncyclic) printf("E is not a cyclic group\n"); printf("minimum order: %ld\n", min_order); printf("maximum order: %ld\n", max_order); printf("minimum (%2ld, %2ld)\n", P.x, P.y); printf("maximum (%2ld, %2ld)\n", Q.x, Q.y); P.x = 2; P.y = 7; Q = P; for (i = 0, j = 1; i < 12; i++) { add(a, p, P, Q, &R); if (i == 0) printf("%2ld alpha = (%2ld, %2ld) ", j, P.x, P.y); j++; if (j <= h) printf("%2ld alpha = (%2ld, %2ld) ", j, R.x, R.y); Q = R; if (j % 3 == 0) printf("\n"); } return 0;

Exercises

5.1
/* Author:

ex0501.c freelip (3,062)


Pate Williams (c) 1997

*/

Exercise "5.1 Implement Shanks's algorithm for finding discrete logarithms in Z_p, where p is prime and alpha is a primitive element. Use your program to find log(106, 12375) in Z_24691 and log(6, 248388) in Z_458009." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 200. <math.h> <stdio.h> <stdlib.h> "lip.h"

#include #include #include #include

struct data {long j, alpha_j;}; int fcmp(const void *d1, const void *d2) { struct data *data1 = (struct data *) d1; struct data *data2 = (struct data *) d2; if (data1->alpha_j < data2->alpha_j) return - 1; if (data1->alpha_j > data2->alpha_j) return + 1; return 0; } long Shanks(long alpha, long beta, long p) /* returns log(alpha, beta) in Z_p where alpha is a generator and beta is in Z_p */ { int found = 0; long i, log, m = ceil(sqrt(p - 1)); struct data *table1 = calloc(m, sizeof(struct data)); struct data *table2 = calloc(m, sizeof(struct data)); struct data *d, key; verylong za = 0, zb = 0, zc = 0, zp = 0; verylong zalpha = 0, zalpha_i = 0; verylong zbeta = 0, zp1 = 0; if (!table1 || !table2) { fprintf(stderr, "*error*\nin sufficient memory\n"); fprintf(stderr, "from Shanks\n"); exit(1); }

/* create a logarithm table */ zintoz(alpha, &zalpha); zintoz(beta, &zbeta); zintoz(p, &zp); zintoz(p - 1, &zp1); zsexpmod(zalpha, m, zp, &zalpha_i); for (i = 0; i < m; i++) { table1[i].j = i; zsexpmod(zalpha_i, i, zp, &za); table1[i].alpha_j = ztoint(za); } zinvmod(zalpha, zp, &zalpha_i); for (i = 0; i < m; i++) { table2[i].j = i; zsexpmod(zalpha_i, i, zp, &za); zmulmod(zbeta, za, zp, &zb); table2[i].alpha_j = ztoint(zb); } /* sort the tables by second data item */ qsort(table1, m, sizeof(struct data), fcmp); qsort(table2, m, sizeof(struct data), fcmp); for (i = 0; !found && i < m; i++) { key.j = table1[i].j; key.alpha_j = table1[i].alpha_j; d = bsearch(&key, table2, m, sizeof(struct data), fcmp); if (d) { zintoz(key.j, &za); zintoz(m, &zb); zmulmod(za, zb, zp1, &zc); zintoz(d->j, &za); zaddmod(za, zc, zp1, &zb); log = ztoint(zb); zsexpmod(zalpha, log, zp, &za); found = zcompare(za, zbeta) == 0; } } if (!found) log = 0; zfree(&za); zfree(&zb); zfree(&zc); zfree(&zp); zfree(&zalpha); zfree(&zalpha_i); zfree(&zbeta); zfree(&zp1); return log; } int main(void) { long alpha[2] = {106l, 6l}; long beta[2] = {12375l, 248388l}; long p[2] = {24691l, 458009l}; long i, log; for (i = 0; i < 2; i++) { log = Shanks(alpha[i], beta[i], p[i]);

if (log != 0) printf("log(%6ld, %6ld) = %6ld in Z_%6ld\n", alpha[i], beta[i], log, p[i]); else printf("log(%6ld, %6ld) in Z_%6ld does not exist\n", alpha[i], beta[i], p[i]); } return 0; }

5.2
/* Author:

ex0502.c

(4,680)

Pate Williams (c) 1997

Exercise "5.2 Implement Pohlig-Hellman algorithm for finding discrete logarithms in Z_p, where p is prime and alpha is a primitive element. Use your program to find log(5, 8563) in Z_28703 and log(10, 12611) in Z_31153." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 200. */ #include <math.h> #include <stdio.h> #include <stdlib.h> #define BITS_PER_LONG 32l #define BITS_PER_LONG_1 31l #define SIZE 32l struct factor {long expon, prime;}; long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

long get_bit(long i, long *sieve) { long b = i % BITS_PER_LONG; long c = i / BITS_PER_LONG; } return (sieve[c] >> (BITS_PER_LONG_1 - b)) & 1;

void set_bit(long i, long v, long *sieve) { long b = i % BITS_PER_LONG; long c = i / BITS_PER_LONG; long mask = 1 << (BITS_PER_LONG_1 - b); if (v == 1) sieve[c] |= mask; else sieve[c] &= ~mask;

void Sieve(long n, long *sieve) { long c, i, inc; set_bit(0l, 0l, sieve); set_bit(1l, 0l, sieve); set_bit(2l, 1l, sieve); for (i = 3; i <= n; i++) set_bit(i, i & 1, sieve); c = 3; do { i = c * c, inc = c + c; while (i <= n) { set_bit(i, 0l, sieve); i += inc; } c += 2; while (!get_bit(c, sieve)) c++; } while (c * c <= n); } void factor(long n, long *sieve, struct factor *f, long *count) /* factor using trial division */

int one = 0; long e, p = 2, s = sqrt(n); *count = 0; while (!one && p <= s) { while (!get_bit(p, sieve)) p++; if (n % p == 0) { e = 0; do { e++; n = n / p; } while (n % p == 0); f[*count].expon = e; f[*count].prime = p; *count = *count + 1; one = n == 1; } p++; }

} long Chinese_Remainder(long r, long *a, long *m) { long i, N = 1, M[SIZE], y[SIZE], x = 0; for (i for (i M[i] y[i] x += } return = 0; i < r; i++) N *= m[i]; = 0; i < r; i++) { = N / m[i]; = Extended_Euclidean(M[i], m[i]); (a[i] * M[i] * y[i]) % N; x;

long Pohlig_Hellman(long alpha, long beta, long c, long p, long q) { long ai, delta, e1, s = 0; long i, j, p1 = p - 1, p2 = p1 / q, q1 = q; long *a = calloc(c, sizeof(long)); long *betaj = calloc(c, sizeof(long)); long *gamma = calloc(q, sizeof(long)); if (!betaj || !gamma) { fprintf(stderr, "*error*\ninsufficient memory\n"); fprintf(stderr, "from Pohlig_Hellman\n"); exit(1); } ai = Extended_Euclidean(alpha, p); for (i = 0; i < q; i++) gamma[i] = exp_mod(alpha, i * p2, p); j = 0; betaj[j] = beta; while (j < c) { delta = exp_mod(betaj[j], p1 / q1, p); i = 0;

while (gamma[i] != delta) i++; a[j] = i; e1 = exp_mod(ai, i * q1 / q, p); betaj[j + 1] = (betaj[j] * e1) % p; q1 *= q; j++; } q1 = 1; for (i = 0; i < c; i++) { s += a[i] * q1; q1 *= q; } return s % exp_mod(q, c, p);

int main(void) { long alpha[2] = {5l, 10l}; long beta[2] = {8563l, 12611l}; long p[2] = {28703l, 31153l}; long a[32], m[32]; long count, i, j, log, q, sieve[10000]; struct factor f[32]; Sieve(p[1], sieve); for (i = 0; i < 2; i++) { q = p[i] - 1; factor(q, sieve, f, &count); printf("the factorization of %ld is:\n", q); for (j = 0; j < count; j++) { printf("%ld", f[j].prime); if (f[j].expon > 1) printf(" ^ %ld\n", f[j].expon); else printf("\n"); } for (j = 0; j < count; j++) { a[j] = Pohlig_Hellman(alpha[i], beta[i], f[j].expon, p[i], f[j].prime); m[j] = exp_mod(f[j].prime, f[j].expon, p[i]); } log = Chinese_Remainder(count, a, m) % q; printf("log(%5ld, %5ld) = %5ld in Z%5ld\n", alpha[i], beta[i], log, p[i]); if (exp_mod(alpha[i], log, p[i]) != beta[i]) printf("*error*\nin calculation\n"); } return 0;

5.3
/* Author:

ex0503.c

(2,138)

Pate Williams (c) 1997

Exercise "5.3 Find log(5, 896) in Z_1103 using the algorithm

*/

presented in Figure 5.6, given that L_2(beta) = 1 for beta = 25, 219, 841, and L_2(beta) = 0 for beta = 163, 532, 625, and 656." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 200.

#include <stdio.h> #define BITS_PER_LONG 32l long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n; } long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

long L_1(long beta, long p2, long p) { return exp_mod(beta, p2, p) != 1; } long L_2(long beta) { if (beta == 25 || beta == 219 || beta == 841) return 1; return 0; }

long discrete_log(long alpha, long beta, long p) { long ai = Extended_Euclidean(alpha, p); long gamma; long p2 = (p - 1) >> 1, p4 = (p + 1) >> 2; long s, t = 2, x; s = x = L_1(beta, p2, p); beta = (beta * exp_mod(ai, x, p)) % p; printf("beta = %4ld gamma = 0 x = %ld\n", beta, x); while (beta != 1) { x = L_2(beta); gamma = exp_mod(beta, p4, p); printf("beta = %4ld gamma = %4ld x = %ld\n", beta, gamma, x); if (L_1(gamma, p2, p) == x) beta = gamma; else beta = p - gamma; beta = (beta * exp_mod(ai, x, p)) % p; s += t * x; t *= 2; } return s;

int main(void) { long alpha = 5l, beta = 896l, p = 1103l, log; log = discrete_log(alpha, beta, p); printf("log(%ld, %ld) = %ld in Z_%ld\n", alpha, beta, log, p); if (exp_mod(alpha, log, p) != beta) printf("*error*\nin computation\n"); return 0;

5.4
/* Author:

ex0504.c (3,154)
Pate Williams (c) 1997

Exercise "5.4 Decrypt the ElGamal ciphertext presented in Table 5.3. The parameters of the system are p = 31847, alpha = 5, a = 7899, beta = 18074. Each element of Z_n represents three alphabetic characters as in Exercise 4.6." -Douglas R. StinsonSee "Cryptography: Theory and Practice by Douglas R. Stinson page 200. */ #include <stdio.h> struct ciphertext {long y1, y2;};

long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n; } long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

long decrypt(long a, long p, struct ciphertext y) { long z = exp_mod(y.y1, a, p); z = Extended_Euclidean(z, p); return (y.y2 * z) % p;

int main(void) { char abc[26] = "abcdefghijklmnopqrstuvwxyz"; long a = 7899l, p = 31847l, i, t, x; struct ciphertext y[40] = {{ 3781l, 14409l}, {27214l, 15442l}, { 5400l, 31486l}, {27765l, 29284l}, {31590l, 26470l}, {15898l, 30844l}, {16160l, 3129l}, {24689l, 7776l}, {30555l, 24611l}, {13659l, 5015l},

{31552l, { 5809l, {19936l, {29820l, { 3781l, {19048l, { 301l, {28856l, {20501l, { 5740l,

3930l}, 30274l}, 721l}, 7710l}, 14409l}, 12914l}, 17252l}, 15720l}, 2922l}, 31233l},

{ 1616l, { 2320l, {14130l, {19557l, {26004l, { 9526l, {29538l, { 9396l, { 1777l, { 7129l,

14170l}, 29174l}, 22010l}, 10145l}, 25056l}, 3019l}, 5408l}, 3058l}, 8737l}, 18195l},

{ 4294l, { 3036l, {25910l, {18899l, { 5400l, {12962l, { 3149l, {27149l, {26117l, {25302l,

2307l}, 20132l}, 19663l}, 27609l}, 31486l}, 15189l}, 7400l}, 20535l}, 14251l}, 10248l}};

printf("the ciphertext pairs (y1,y2) are:\n"); for (i = 0; i < 40; i++) { printf("(%5ld, %5ld) ", y[i].y1, y[i].y2); if ((i + 1) % 4 == 0) printf("\n"); } printf("the corresponding plaintext is:\n"); for (i = 0; i < 40; i++) { x = decrypt(a, p, y[i]); t = x / 676l; x = x % 676l; printf("%c", abc[t]); t = x / 26l; x = x % 26l; printf("%c", abc[t]); printf("%c", abc[x]); if ((i + 1) % 8 == 0) printf("\n"); } return 0;

5.5

ex0505.txt

(517)

Exercise "5.5 Determine which of the following polynomials are irreducible over Z_2[x]: x ^ 5 + x ^ 4 + 1, x ^ 5 + x ^ 3 + 1, x ^ 5 + x ^ 4 + x ^ 2 + 1." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson pages 200-201. x ^ 5 + x ^ 4 + 1 = (x ^ 2 + x + 1) * (x ^ 3 + x + 1) mod 2 hence, it is not irreducible over Z_2[x] x ^ 5 + x ^ 3 + 1 is irreducible over Z_2[x] x ^ 5 + x ^ 4 + x ^ 2 + 1 = (x + 1) * (x ^ 4 + x + 1) mod 2 hence, it is not irreducible over Z_2[x]

5.6
/*

ex0506.c

(5,773)

Author:

Pate Williams (c) 1997

*/

Exercise "5.6 The field GF(2 ^ 5) can be constructed as Z_2[x]/(x ^ 5 + x ^ 2 + 1). Perform the following computations in this field. (a) Compute (x ^ 4 + x ^ 2) * (x ^ 3 + x + 1). (b) Using the Extended Euclidean algorithm compute (x ^ 3 + x ^ 2) ^ - 1. (c) Using the square and multiply algorithm, compute x ^ 25." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 201.

#include <math.h> #include <stdio.h> #define SIZE 32l void poly_mul(long m, long n, long *a, long *b, long *c, long *p) { long ai, bj, i, j, k, sum; *p = m + n; for (k = 0; k <= *p; k++) { sum = 0; for (i = 0; i <= k; i++) { j = k - i; if (i > m) ai = 0; else ai = a[i]; if (j > n) bj = 0; else bj = b[j]; sum += ai * bj; } c[k] = sum; }

void poly_div(long m, long n, long *u, long *v, long *q, long *r, long *p, long *s) { long j, jk, k, nk, vn = v[n]; for (j = 0; j <= m; j++) r[j] = u[j]; if (m < n) { *p = 0, *s = m; q[0] = 0; } else { *p = m - n, *s = n - 1; for (k = *p; k >= 0; k--) { nk = n + k; q[k] = r[nk] * pow(vn, k); for (j = nk - 1; j >= 0; j--) { jk = j - k; if (jk >= 0) r[j] = vn * r[j] - r[nk] * v[j - k]; else

r[j] = vn * r[j];

} }

} while (*p > 0 && q[*p] == 0) *p = *p - 1; while (*s > 0 && r[*s] == 0) *s = *s - 1;

void poly_exp_mod(long degreeA, long degreem, long n, long modulus, long *A, long *m, long *s, long *ds) { int zero; long dp, dq, dx = degreeA, i; long p[SIZE], q[SIZE], x[SIZE]; *ds = 0, s[0] = 1; for (i = 0; i <= dx; i++) x[i] = A[i]; while (n > 0) { if ((n & 1) == 1) { /* s = (s * x) % m; */ poly_mul(*ds, dx, s, x, p, &dp); poly_div(dp, degreem, p, m, q, s, &dq, ds); for (i = 0; i <= *ds; i++) s[i] %= modulus; zero = s[*ds] == 0, i = *ds; while (i > 0 && zero) { if (zero) *ds = *ds - 1; zero = s[--i] == 0; } } n >>= 1; /* x = (x * x) % m; */ poly_mul(dx, dx, x, x, p, &dp); poly_div(dp, degreem, p, m, q, x, &dq, &dx); for (i = 0; i <= dx; i++) x[i] %= modulus; zero = x[dx] == 0, i = dx; while (i > 0 && zero) { if (zero) dx--; zero = x[--i] == 0; } }

void poly_copy(long db, long *a, long *b, long *da) /* a = b */ { long i; *da = db; for (i = 0; i <= db; i++) a[i] = b[i]; } int poly_Extended_Euclidean(long db, long dn, long *b, long *n, long *t, long *dt) { int nonzero;

long db0, dn0, dq, dr, dt0 = 0, dtemp, du, i; long b0[SIZE], n0[SIZE], q[SIZE]; long r[SIZE], t0[SIZE], temp[SIZE], u[SIZE]; *dt = 0; poly_copy(dn, n0, n, &dn0); poly_copy(db, b0, b, &db0); t0[0] = 0; t[0] = 1; poly_div(dn0, db0, n0, b0, q, r, &dq, &dr); nonzero = r[0] != 0; for (i = 1; !nonzero && i <= dr; i++) nonzero = r[i] != 0; while (nonzero) { poly_mul(dq, *dt, q, t, u, &du); if (dt0 < du) for (i = dt0 + 1; i <= du; i++) t0[i] = 0; for (i = 0; i <= du; i++) temp[i] = t0[i] - u[i]; dtemp = du; poly_copy(*dt, t0, t, &dt0); poly_copy(dtemp, t, temp, dt); poly_copy(db0, n0, b0, &dn0); poly_copy(dr, b0, r, &db0); poly_div(dn0, db0, n0, b0, q, r, &dq, &dr); nonzero = r[0] != 0; for (i = 1; !nonzero && i <= dr; i++) nonzero = r[i] != 0; } if (db0 != 0 && b0[0] != 1) return 0; return 1;

void poly_mod(long da, long p, long *a, long *new_da) { int zero; long i; for (i = 0; i <= da; i++) { a[i] %= p; if (a[i] < 0) a[i] += p; } zero = a[da] == 0; for (i = da - 1; zero && i >= 0; i--) { da--; zero = a[i] == 0; } *new_da = da; } void poly_write(char *label, long da, long *a) { long i; printf("%s", label); for (i = da; i >= 0; i--) printf("%ld ", a[i]);

printf("\n");

int main(void) { long da = 4, db = 3, dc = 3, dd = 5; long de, dq, dr, ds, p = 2; long a[5] = {0, 0, 1, 0, 1}; long b[4] = {1, 1, 0, 1}; long c[4] = {0, 0, 1, 1}; long d[6] = {1, 0, 1, 0, 0, 1}; long e[SIZE], q[SIZE], r[SIZE], s[SIZE]; poly_write("A = ", da, a); poly_write("B = ", db, b); poly_write("C = ", dc, c); poly_write("D = ", dd, d); poly_mul(da, db, a, b, e, &de); poly_mod(de, p, e, &de); poly_div(de, dd, e, d, q, r, &dq, &dr); poly_mod(dq, p, q, &dq); poly_mod(dr, p, r, &dr); poly_write("A * B mod 2 = ", de, e); poly_write("A * B / D mod 2 = ", dq, q); poly_write("A * B mod D, 2 = ", dr, r); if (!poly_Extended_Euclidean(dc, dd, c, d, e, &de)) printf("*error*\in poly_Extended_Euclidean\n"); poly_mod(de, p, e, &de); poly_write("C ^ - 1 mod D = ", de, e); poly_mul(dc, de, c, e, s, &ds); poly_mod(ds, p, s, &ds); poly_div(ds, dd, s, d, q, r, &dq, &dr); poly_mod(dr, p, r, &dr); poly_write("C * C ^ - 1 mod D, 2 = ", dr, r); dq = 1; q[0] = 0; q[1] = 1; poly_exp_mod(dq, dd, 7, p, q, d, s, &ds); poly_mod(ds, p, s, &ds); poly_write("x ^ 7 mod D, 2 = ", ds, s); poly_exp_mod(dq, dd, 9, p, q, d, s, &ds); poly_mod(ds, p, s, &ds); poly_write("x ^ 9 mod D, 2 = ", ds, s); poly_exp_mod(dq, dd, 10, p, q, d, s, &ds); poly_mod(ds, p, s, &ds); poly_write("x ^ 10 mod D, 2 = ", ds, s); poly_exp_mod(dq, dd, 25, p, q, d, s, &ds); poly_mod(ds, p, s, &ds); poly_write("x ^ 25 mod D, 2 = ", ds, s); return 0; }

5.7
/* Author:

ex0507.c

(7,323)

Pate Williams (c) 1997

Exercise "5.7 We give an example of ElGamal Cryptosystem implemented in GF(3 ^ 3). The polynomial x ^ 3 + 2x ^ 2 + 1 is irreducible over Z_3[x] and hence Z_3[x]/(x ^ 3 + 2x ^ 2 + 1) is the field GF(3 ^ 3). We can associate the 26 letters of the alphabet with the 26 nonzero field elements, and thus encrypt ordinary text in a convenient way. We will use the lexicographic ordering of the (nonzero) polynomials to set up the correspondence. The correspondence is as follows: A = 1, B = 2, C = x, D = x + 1, E = x + 2, F = 2x, G = 2x + 1, H = 2x + 2, I = x ^ 2, J = x ^ 2 + 1, K = x ^ 2 + 2, L = x ^ 2 + x, M = x ^ 2 + x + 1, N = x ^ 2 + x + 2, O = x ^ 2 + 2x, P = x ^ 2 + 2x + 1, Q = x ^ 2 + 2x + 2, R = 2x, S = 2x ^ 2 + 1, T = 2x ^ 2 + 2, U = 2x ^ x + x, V = 2x ^ 2 + x + 1, W = 2x ^ 2 + x + 2, X = 2x ^ 2 + 2x, Y = 2x ^ 2 + 2x + 1, Z = 2x ^ 2 + 2x + 2 Suppose Bob uses alpha = x and a = 11 in an ElGamal system; then beta = x + 2. Show how Bob will decrypt the following string of ciphertext: (K, H), (P, X), (N, K), (H, R), (T, F), (V, Y), (E, H), (F, A), (T, W), (J, D), (U, J)" -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 201. */ #include <math.h> #include <stdio.h> #include <stdlib.h> #define SIZE 32l struct ciphertext {long y1, y2;}; void poly_mul(long m, long n, long *a, long *b, long *c, long *p) { long ai, bj, i, j, k, sum; *p = m + n; for (k = 0; k <= *p; k++) { sum = 0; for (i = 0; i <= k; i++) { j = k - i; if (i > m) ai = 0; else ai = a[i]; if (j > n) bj = 0; else bj = b[j]; sum += ai * bj; } c[k] = sum; }

} void poly_div(long m, long n, long *u, long *v, long *q, long *r, long *p, long *s) { long j, jk, k, nk, vn = v[n]; for (j = 0; j <= m; j++) r[j] = u[j]; if (m < n) { *p = 0, *s = m; q[0] = 0; } else { *p = m - n, *s = n - 1; for (k = *p; k >= 0; k--) { nk = n + k; if (k != 0) q[k] = r[nk] * pow(vn, k); else if (vn != 0) q[k] = r[nk]; else q[k] = 0; for (j = nk - 1; j >= 0; j--) { jk = j - k; if (jk >= 0) r[j] = vn * r[j] - r[nk] * v[j - k]; else r[j] = vn * r[j]; } } while (*p > 0 && q[*p] == 0) *p = *p - 1; while (*s > 0 && r[*s] == 0) *s = *s - 1; }

void poly_copy(long db, long *a, long *b, long *da) /* a = b */ { long i; *da = db; for (i = 0; i <= db; i++) a[i] = b[i]; } void poly_mod(long da, long p, long *a, long *new_da) { int zero; long i; for (i = 0; i <= da; i++) { a[i] %= p; if (a[i] < 0) a[i] += p; } zero = a[da] == 0; for (i = da - 1; zero && i >= 0; i--) { da--; zero = a[i] == 0;

} *new_da = da; } void poly_exp_mod(long degreeA, long degreem, long n, long modulus, long *A, long *m, long *s, long *ds) { long dp, dq, dx = degreeA, i; long p[SIZE], q[SIZE], x[SIZE]; *ds = 0, s[0] = 1; for (i = 0; i <= dx; i++) x[i] = A[i]; while (n > 0) { if ((n & 1) == 1) { /* s = (s * x) % m; */ poly_mul(*ds, dx, s, x, p, &dp); poly_mod(dp, modulus, p, &dp); poly_div(dp, degreem, p, m, q, s, &dq, ds); poly_mod(*ds, modulus, s, ds); } n >>= 1; /* x = (x * x) % m; */ poly_mul(dx, dx, x, x, p, &dp); poly_mod(dp, modulus, p, &dp); poly_div(dp, degreem, p, m, q, x, &dq, &dx); poly_mod(dx, modulus, x, &dx); } } void poly_write(char *label, long da, long *a) { long i; printf("%s", label); for (i = da; i >= 0; i--) printf("%ld ", a[i]); printf("\n"); } int poly_Extended_Euclidean(long db, long dn, long *b, long *n, long *t, long *dt) { int count = 0, nonzero; long db0, dn0, dq, dr, dt0 = 0, dtemp, du, i; long b0[SIZE], n0[SIZE], q[SIZE]; long r[SIZE], t0[SIZE], temp[SIZE], u[SIZE]; *dt = 0; poly_copy(dn, n0, n, &dn0); poly_copy(db, b0, b, &db0); t0[0] = 0; t[0] = 1; poly_div(dn0, db0, n0, b0, q, r, &dq, &dr); nonzero = r[0] != 0; for (i = 1; !nonzero && i <= dr; i++)

nonzero = r[i] != 0; while (nonzero && count++ < 8) { poly_mul(dq, *dt, q, t, u, &du); if (dt0 < du) for (i = dt0 + 1; i <= du; i++) t0[i] = 0; for (i = 0; i <= du; i++) temp[i] = t0[i] - u[i]; dtemp = du; poly_copy(*dt, t0, t, &dt0); poly_copy(dtemp, t, temp, dt); poly_copy(db0, n0, b0, &dn0); poly_copy(dr, b0, r, &db0); if (dn0 - db0 > 0) poly_div(dn0, db0, n0, b0, q, r, &dq, &dr); else return db0 == 0 && b[0] == 1; nonzero = r[0] != 0; for (i = 1; !nonzero && i <= dr; i++) nonzero = r[i] != 0; if (db0 == 0) return 1; } return 0; } int main(void) { char message[4] = "A "; int found, plaintext; long a = 11, dm = 3, dy1, dy2, i, j, k; long dq, dr, dx, dy, dz; long p = 3, m[SIZE] = {1, 0, 2, 1}; long q[SIZE], r[SIZE], y1[SIZE], y2[SIZE]; long x[SIZE], y[SIZE], z[SIZE]; long field[26][4] = {{1, 0, 0, 0}, {2, 0, 0, {0, 1, 0, 0}, {1, 1, 0, {2, 1, 0, 0}, {0, 2, 0, {1, 2, 0, 0}, {2, 2, 0, {0, 0, 1, 0}, {1, 0, 1, {2, 0, 1, 0}, {0, 1, 1, {1, 1, 1, 0}, {2, 1, 1, {0, 2, 1, 0}, {1, 2, 1, {2, 2, 1, 0}, {0, 0, 2, {1, 0, 2, 0}, {2, 0, 2, {0, 1, 2, 0}, {1, 1, 2, {2, 1, 2, 0}, {0, 2, 2, {1, 2, 2, 0}, {2, 2, 2, struct ciphertext c[11] = {{'K', 'H'}, {'P', {'N', 'K'}, {'H', {'T', 'F'}, {'V', {'E', 'H'}, {'F', {'T', 'W'}, {'J', {'U', 'J'}}; for (i = 0; i < 26; i++) { message[0] = (char) (i + 'A'); poly_write(message, 2, field[i]); }

0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}, 0}}; 'X'}, 'R'}, 'Y'}, 'A'}, 'D'},

for (i = 0; i < 11; i++) { printf("(%c, %c) ", c[i].y1, c[i].y2); if ((i + 1) % 5 == 0) printf("\n"); } printf("\n"); for (i = 0; i < 11; i++) { poly_copy(2, y1, field[c[i].y1 - 'A'], &dy1); poly_copy(2, y2, field[c[i].y2 - 'A'], &dy2); poly_mod(dy1, p, y1, &dy1); poly_mod(dy2, p, y2, &dy2); poly_exp_mod(dy1, dm, a, p, y1, m, x, &dx); poly_Extended_Euclidean(dx, dm, x, m, y, &dy); poly_mod(dy, p, y, &dy); poly_mul(dy2, dy, y2, y, z, &dz); poly_mod(dz, p, z, &dz); poly_div(dz, dm, z, m, q, r, &dq, &dr); poly_mod(dr, p, r, &dr); found = 0; for (j = 0; !found && j < 26; j++) { found = r[0] == field[j][0]; for (k = 1; found && k <= dr; k++) found = r[k] == field[j][k]; if (found) plaintext = 'A' + j; } printf("%c", plaintext); } printf("\n"); return 0; }

5.8
/* Author:

ex0508.c

(4,813)

Pate Williams (c) 1997

Exercise "5.8 Let E be the elliptic curve y ^ 2 = x ^ 3 + x + 28 defined over Z_71. (a) Determine the number of points on E. (b) Show that E is not a cyclic group. (c) What is the maximum order of an element in E? Find an element having this order." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 201. */ #include <math.h> #include <stdio.h> #include <stdlib.h> #define MAX_POINTS 142l struct point {long x, y;}; int JACOBI(long a, long n)

int s; long a1, b = a, e = 0, m, n1; if (a == 0) return 0; if (a == 1) return 1; while ((b & 1) == 0) b >>= 1, e++; a1 = b; m = n % 8; if (!(e & 1)) s = 1; else if (m == 1 || m == 7) s = else if (m == 3 || m == 5) s = if (n % 4 == 3 && a1 % 4 == 3) if (a1 != 1) n1 = n % a1; else return s * JACOBI(n1, a1);

+ 1; - 1; s = - s; n1 = 1;

} long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; if (b == 0) return 1; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } if (a < 0) a += n; return a;

long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n; } long E_points(long a, long b, long p, struct point *e) /* returns the number of points on the elliptic

curve y ^ 2 = x ^ 3 + ax + b mod p */ long count = 0, m = (p + 1) / 4, x, y; if (p % 4 == 3) { for (x = 0; x < p; x++) { y = x * x * x + a * x + b; if (JACOBI(y, p) != - 1) { y = exp_mod(y, m, p); printf("(%2ld, %2ld) ", x, y); e[count].x = x; e[count].y = y; y = - y % p; if (y < 0) y += p; printf("(%2ld, %2ld) ", x, y); e[count + 1].x = x; e[count + 1].y = y; count += 2; if (count % 4 == 0) printf("\n"); } } if (count % 4 != 0) printf("\n"); } return count;

} void add(long a, long p, struct point P, struct point Q, struct point *R) /* elliptic curve point partial addition */ { long i, lambda; if (P.x == Q.x && P.y == 0 && Q.y == 0) { R->x = 0; R->y = 1; return; } if (P.x == Q.x && P.y == p - Q.y) { R->x = 0; R->y = 1; return; } if (P.x == 0 && P.y == 1) { *R = Q; return; } if (Q.x == 0 && Q.y == 1) { *R = P; return; } if (P.x != Q.x) { i = Q.x - P.x; if (i < 0) i += p; i = Extended_Euclidean(i, p); lambda = ((Q.y - P.y) * i) % p; } else {

i = Extended_Euclidean((2 * P.y) % p, p); lambda = ((3 * P.x * P.x + a) * i) % p; } if (lambda < 0) lambda += p; R->x = (lambda * lambda - P.x - Q.x) % p; R->y = (lambda * (P.x - R->x) - P.y) % p; if (R->x < 0) R->x += p; if (R->y < 0) R->y += p; } void multiply(long a, long k, long p, struct point P, struct point *R) { struct point S; R->x = 0; R->y = 1; S = P; while (k != 0) { if (k & 1) add(a, p, *R, S, R); k >>= 1; if (k != 0) add(a, p, S, S, &S); } } long order(long a, long p, struct point P) { long ord = 1; struct point Q = P, R; do { ord++; add(a, p, P, Q, &R); Q = R; } while (R.x != 0 && R.y != 1); return ord;

int main(void) { long a = 1, b = 28, h, i, p = 71; long max_order, min_order, noncyclic, ord; struct point e[MAX_POINTS], P, Q; printf("E: y ^ 2 = x ^ 3 + %ld * x + %ld mod %ld\n", a, b, p); printf("the points (x, y) on E are:\n"); h = E_points(a, b, p, e) + 1; printf("the number of points on E is: %ld\n", h); max_order = 0; min_order = h; printf("the points (x, y) on E and their orders are:\n"); for (i = 0; i < h - 1; i++) { ord = order(a, p, e[i]); printf("(%2ld, %2ld) %2ld ", e[i].x, e[i].y, ord); if ((i + 1) % 3 == 0) printf("\n");

} printf("\n"); if (noncyclic) printf("E is not a cyclic group\n"); printf("minimum order: %ld\n", min_order); printf("maximum order: %ld\n", max_order); printf("minimum (%2ld, %2ld)\n", P.x, P.y); printf("maximum (%2ld, %2ld)\n", Q.x, Q.y); return 0;

if (ord < h) noncyclic = 1; if (ord < min_order) { P = e[i]; min_order = ord; } if (ord > max_order) { Q = e[i]; max_order = ord; }

5.9
/* Author:

ex0509.c

(6,316)

Pate Williams (c) 1997

*/

Exercise "5.9 Let E be the elliptic curve y ^ 2 = x ^ 3 + x + 13 defined over Z_31. It can be shown that #E = 34 and (9, 10) is an element of order 34 in E. The Menezes-Vanstone Cryptosystem defined on E will have as its plaintext space Z_34* * Z_34*. Suppose Bob's secret exponent is a = 25. (a) Compute beta = a alpha. (b) Decrypt the following string of ciphertext: ((4, 9), 28, 7), ((19, 28), 9, 13), ((5, 22), 20, 17), ((25, 16), 12, 27). (c) Assuming that each plaintext represents two alphabetic characters, convert the plaintext into an English word. (Here we will use the correspondence A = 1,..., Z = 26, since 0 is not allowed in a (plaintext) ordered pair." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 201.

#include <math.h> #include <stdio.h> #include <stdlib.h> #define MAX_POINTS 64l struct point {long x, y;}; struct ciphertext {struct point y0; long y1, y2;}; int JACOBI(long a, long n)

int s; long a1, b = a, e = 0, m, n1; if (a == 0) return 0; if (a == 1) return 1; while ((b & 1) == 0) b >>= 1, e++; a1 = b; m = n % 8; if (!(e & 1)) s = 1; else if (m == 1 || m == 7) s = else if (m == 3 || m == 5) s = if (n % 4 == 3 && a1 % 4 == 3) if (a1 != 1) n1 = n % a1; else return s * JACOBI(n1, a1);

+ 1; - 1; s = - s; n1 = 1;

} long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; if (b == 0) return 1; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } if (a < 0) a += n; return a;

long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n; } long E_points(long a, long b, long p, struct point *e) /* returns the number of points on the elliptic

curve y ^ 2 = x ^ 3 + ax + b mod p */ long count = 0, m = (p + 1) / 4, x, y; if (p % 4 == 3) { for (x = 0; x < p; x++) { y = x * x * x + a * x + b; if (JACOBI(y, p) != - 1) { y = exp_mod(y, m, p); printf("(%2ld, %2ld) ", x, y); e[count].x = x; e[count].y = y; y = - y % p; if (y < 0) y += p; printf("(%2ld, %2ld) ", x, y); e[count + 1].x = x; e[count + 1].y = y; count += 2; if (count % 4 == 0) printf("\n"); } } if (count % 4 != 0) printf("\n"); } return count;

} void add(long a, long p, struct point P, struct point Q, struct point *R) /* elliptic curve point partial addition */ { long i, lambda; if (P.x == Q.x && P.y == 0 && Q.y == 0) { R->x = 0; R->y = 1; return; } if (P.x == Q.x && P.y == p - Q.y) { R->x = 0; R->y = 1; return; } if (P.x == 0 && P.y == 1) { *R = Q; return; } if (Q.x == 0 && Q.y == 1) { *R = P; return; } if (P.x != Q.x) { i = Q.x - P.x; if (i < 0) i += p; i = Extended_Euclidean(i, p); lambda = ((Q.y - P.y) * i) % p; } else {

i = Extended_Euclidean((2 * P.y) % p, p); lambda = ((3 * P.x * P.x + a) * i) % p; } if (lambda < 0) lambda += p; R->x = (lambda * lambda - P.x - Q.x) % p; R->y = (lambda * (P.x - R->x) - P.y) % p; if (R->x < 0) R->x += p; if (R->y < 0) R->y += p; } void multiply(long a, long k, long p, struct point P, struct point *R) { struct point S; R->x = 0; R->y = 1; S = P; while (k != 0) { if (k & 1) add(a, p, *R, S, R); k >>= 1; if (k != 0) add(a, p, S, S, &S); } } long order(long a, long p, struct point P) { long ord = 1; struct point Q = P, R; do { ord++; add(a, p, P, Q, &R); Q = R; } while (R.x != 0 && R.y != 1); return ord;

int main(void) { long A = 25, a = 1, b = 13, h, i, p = 31; long max_order, min_order, noncyclic, ord; long c1, c2, m1, m2; struct ciphertext c[4] ={{{4, 9}, 28, 7}, {{19, 28}, 9, 13}, {{5, 22}, 20, 17}, {{25, 16}, 12, 27}}; struct point alpha = {9, 10}, beta; struct point e[MAX_POINTS], P, Q; printf("E: y ^ 2 = x ^ 3 + %ld * x + %ld mod %ld\n", a, b, p); printf("the the non-origin points (x, y) on E are:\n"); h = E_points(a, b, p, e) + 1; printf("the number of non-origin points on E is: %ld\n", h - 1); max_order = 0;

min_order = h + 1; printf("the non-origin points (x, y) on E "); printf("and their orders are:\n"); for (i = 0; i < h - 1; i++) { ord = order(a, p, e[i]); if (ord < h) noncyclic = 1; if (ord < min_order) { P = e[i]; min_order = ord; } if (ord > max_order) { Q = e[i]; max_order = ord; } printf("(%2ld, %2ld) %2ld ", e[i].x, e[i].y, ord); if ((i + 1) % 3 == 0) printf("\n"); } printf("\n"); if (noncyclic) printf("E is not a cyclic group\n"); printf("minimum order: %ld\n", min_order); printf("maximum order: %ld\n", max_order); printf("minimum (%2ld, %2ld)\n", P.x, P.y); printf("maximum (%2ld, %2ld)\n", Q.x, Q.y); printf("alpha = (%2ld, %2ld)\n", alpha.x, alpha.y); multiply(a, A, p, alpha, &beta); printf("beta = %ld alpha = (%2ld, %2ld)\n", A, beta.x, beta.y); printf("the ciphertext is:\n"); for (i = 0; i < 4; i++) printf("((%2ld, %2ld), %2ld, %2ld)\n", c[i].y0.x, c[i].y0.y, c[i].y1, c[i].y2); printf("the corresponding plaintext is:\n"); for (i = 0; i < 4; i++) { multiply(a, A, p, c[i].y0, &beta); c1 = beta.x; c2 = beta.y; m1 = (c[i].y1 * Extended_Euclidean(c1, p)) % p; m2 = (c[i].y2 * Extended_Euclidean(c2, p)) % p; printf("%c%c", m1 + 'A' - 1, m2 + 'A' - 1); } printf("\n"); return 0; }

5.10
/* Author:

ex0510.c (2,658)
Pate Williams (c) 1997

Exercise "5.10 Suppose the Merkle-Hellman Cryptosystem has as its public list of sizes the vector t = (1394, 1256, 1508, 1987, 439, 650, 339, 2303, 810). Suppose Oscar discovers that p = 2503. (a) By trial and error, determine the value a

such that a ^ -1 t mod p is a permutation of a superincreasing list. (b) Show how the ciphertext 5746 would be decrypted." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson. */ #include <stdio.h> #include <stdlib.h> long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n; } int superincreasing(long n, long *s) { long i, j, sum; for (j = 0; j < n; j++) { sum = 0; for (i = 0; i < j; i++) sum += s[i]; if (s[j] <= sum) return 0; } return 1; } int subset_sum(long T, long n, long *s, long *x) { long S = T, i, sum = 0; for (i = n - 1; i >= 0; i--) { if (S >= s[i]) { S -= s[i]; x[i] = 1; } else x[i] = 0; } for (i = 0; i < n; i++) sum += x[i] * s[i];

return sum == T;

int main(void) { int found = 0; long a, af, ai, i, j, n = 10, p = 2503, u, s[10]; long x[10], y = 5746, z; long t[10] = {1394, 1256, 1508, 1987, 439, 650, 724, 339, 2303, 810}; for (a = 1; !found && a < p; a++) { ai = Extended_Euclidean(a, p); for (i = 0; i < n; i++) s[i] = (ai * t[i]) % p; /* sort the sizes into ascending order */ for (i = 0; i < n - 1; i++) for (j = i + 1; j < n; j++) if (s[i] > s[j]) u = s[i], s[i] = s[j], s[j] = u; found = superincreasing(n, s); af = a; } printf("the public list of sizes is:\n"); for (i = 0; i < n; i++) printf("%4ld ", t[i]); printf("\n"); printf("a = %ld a ^ - 1 = %ld\n", af, ai); printf("the superincreasing size vector is:\n"); for (i = 0; i < n; i++) printf("%4ld ", s[i]); printf("\n"); z = (ai * y) % p; printf("z = (%ld * %ld) %% %ld = %ld\n", ai, y, p, z); if (subset_sum(z, n, s, x)) { printf("the solution vector is:\n"); for (i = 0; i < n; i++) printf("%ld ", x[i]); printf("\n"); } else printf("subset sum has no solution\n"); return 0;

5.11
/* Author:

ex0511.c

(4,425)

Pate Williams (c) 1997

Exercise "5.11 It can be shown that the matrix H shown below is a parity-check matrix for a [15, 7, 5] code called a BCH code. 1 0 0 0 1 0 0 1 1 0 1 0 1 1 1

0 0 H = 0 1 0 0 0

1 0 0 0 0 0 1

0 1 0 0 0 1 1

0 0 1 0 1 0 1

1 0 0 1 1 1 1

1 1 0 1 0 0 0

0 1 1 0 0 0 1

1 0 1 0 0 1 1

0 1 0 0 1 0 1

1 0 1 1 1 1 1

1 1 0 1 0 0 0

1 1 1 0 0 0 1

1 1 1 0 0 1 1

0 1 1 0 1 0 1

0 0 1 1 1 1 1

*/

Decode, if possible, each of the following received vectors r using the syndrome decoding method. (a) r = (1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0). (b) r = (1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0). (c) r = (1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0)." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 202.

#include <stdio.h> #include <stdlib.h> #define MAX_ATTEMPTS 11025 void generate(long n, long w, long *e) /* generates an error vector of weight w */ { long i; for (i = 0; i < n; i++) e[i] = 0; for (i = 0; i < w; i++) e[rand() % n] = 1; } void mat_vec_mul(long k, long n, long *r, long *s, long **h) { long i, j, sum; for (i = 0; i < k; i++) { sum = 0; for (j = 0; j < n; j++) sum += h[i][j] * r[j]; s[i] = sum % 2; } } int syndrome(long d, long k, long n, long *e, long *r, long *s, long *x, long *y, long **h) { int equal, nonzero = 0; long i, j, l; mat_vec_mul(k, n, r, s, h);

printf("the syndrome vector is:\n"); for (i = 0; i < k; i++) printf("%ld ", s[i]); printf("\n"); for (i = 0; !nonzero && i < k; i++) nonzero = s[i] != 0; if (!nonzero) { for (i = 0; i < n; i++) x[i] = r[i]; return 1; } /* generate and test all error vectors of weight 1 */ for (i = 0; i < n; i++) { for (j = 0; j < i; j++) e[j] = 0; e[i] = 1; for (j = i + 1; j < k; j++) e[j] = 0; mat_vec_mul(k, n, e, y, h); equal = 1; for (j = 0; equal && j < k; j++) equal = s[j] == y[j]; if (equal) { for (j = 0; j < n; j++) { x[j] = r[j] - e[j]; if (x[j] < 0) x[j] += 2; } return 1; } } for (i = 2; i <= d; i++) { for (j = 0; j < MAX_ATTEMPTS; j++) { generate(n, i, e); mat_vec_mul(k, n, e, y, h); equal = 1; for (l = 0; equal && l < k; l++) equal = s[l] == y[l]; if (equal) { for (l = 0; l < n; l++) { x[l] = r[l] - e[l]; if (x[l] < 0) x[l] += 2; } printf("the error vector is:\n"); for (l = 0; l < n; l++) printf("%ld ", e[l]); printf("\n"); return 1; } } } return 0; } int main(void) { long H[8][15] = {{1,0,0,0,1,0,0,1,1,0,1,0,1,1,1}, {0,1,0,0,1,1,0,1,0,1,1,1,1,0,0}, {0,0,1,0,0,1,1,0,1,0,1,1,1,1,0}, {0,0,0,1,0,0,1,1,0,1,0,1,1,1,1}, {1,0,0,0,1,1,0,0,0,1,1,0,0,0,1},

long long long long

{0,0,0,1,1,0,0,0,1,1,0,0,0,1,1}, {0,0,1,0,1,0,0,1,0,1,0,0,1,0,1}, {0,1,1,1,1,0,1,1,1,1,0,1,1,1,1}}; r[3][15] = {{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0}, {1,1,0,1,1,1,1,0,1,0,1,1,0,0,0}, {1,0,1,0,1,0,0,1,0,1,1,0,0,0,0}}; d = 5, i, j, k = 7, n = 15; e[15], s[15], x[15], y[15]; **h = calloc(k + 1, sizeof(long *));

if (!h) { fprintf(stderr, "*error*\ninsufficient memory"); exit(1); } printf("the parity check matrix is:\n"); for (i = 0; i < k + 1; i++) { h[i] = calloc(n, sizeof(long *)); if (!h[i]) { fprintf(stderr, "*error*\ninsufficient memory"); exit(1); } for (j = 0; j < n; j++) { printf("%ld ", H[i][j]); h[i][j] = H[i][j]; } printf("\n"); } for (i = 0; i < 3; i++) { printf("the vector to be decoded is:\n"); for (j = 0; j < n; j++) printf("%ld ", r[i][j]); printf("\n"); if (syndrome(d, k + 1, n, e, r[i], s, x, y, h)) { printf("the decoded vector is:\n"); for (j = 0; j < n; j++) printf("%ld ", x[j]); printf("\n"); } else printf("vector was not decoded\n"); } for (i = 0; i < k + 1; i++) free(h[i]); free(h); return 0; }

Chapter 06
6.6 6.1 te0606.c ex0601.c

Signature Schemes
(2,607) (2,575)

6.2 6.3 6.6 6.8 6.10 6.12 6.13

ex0602.c ex0603.c ex0606.c ex0608.c ex0610.c ex0612.c ex0613.c

freelip (3,317) (2,173) (1,995) (3,176) (2,902) (722) (2,546)

Example

6.6
/* Author:

te0606.c

(2,607)

Pate Williams (c) 1997

Example "6.6 As before, suppose p = 467, alpha = 4, a = 101 and beta = 449. Suppose the message x = 286 is signed with the (bogus) signature y = 83, and Bob wants to convince Alice that the signature is invalid." -Douglas R. StinsonSee "Cryptogrphy: Theory and Practice" by Douglas R. Stinson pages 222-223. */ #include <stdio.h> long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; }

if (a < 0) a += n; return a; } long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

int main(void) { long a = 101, alpha = 4, beta = 449, e1 = 45; long e2 = 237, f1 = 125, f2 = 9, i, j, p = 467; long q, x = 286, y = 83, c, d, C, D, r, s, t; q = (p - 1) >> 1; printf("a = %ld\n", a); printf("alpha = %ld\n", alpha); printf("beta = %ld\n", beta); printf("e1 = %ld\n", e1); printf("e2 = %ld\n", e2); printf("f1 = %ld\n", f1); printf("f2 = %ld\n", f2); printf("p = %ld\n", p); printf("q = %ld\n", q); printf("x = %ld\n", x); printf("y = %ld\n", y); i = Extended_Euclidean(a, q); c = (exp_mod(y, e1, p) * exp_mod(beta, e2, p)) % d = exp_mod(c, i, p); printf("Alice's challenge c = %ld\n", c); printf("Bob's response d = %ld\n", d); if (d != (exp_mod(x, e1, p) * exp_mod(alpha, e2, printf("d != x ^ e1 * alpha ^ e2 mod p\n"); else printf("d == x ^ e1 * alpha ^ e2 mod p\n"); C = (exp_mod(y, f1, p) * exp_mod(beta, f2, p)) % D = exp_mod(C, i, p); printf("Alice's challenge C = %ld\n", C); printf("Bob's response D = %ld\n", D); if (D != (exp_mod(x, f1, p) * exp_mod(alpha, f2,

p;

p)) % p)

p;

p)) % p)

printf("D != x ^ f1 * alpha ^ f2 mod p\n"); else printf("D == x ^ f1 * alpha ^ f2 mod p\n"); i = q - e2; if (i < 0) i += q; j = q - f2; if (j < 0) j += q; r = (d * exp_mod(alpha, i, p)) % p; s = exp_mod(r, f1, p); r = (D * exp_mod(alpha, j, p)) % p; t = exp_mod(r, e1, p); if (s == t) printf("Alice concludes y is a forgery\n"); else printf("Alice does not conclude y is a forgery\n"); return 0; }

Exercises

6.1
/* Author:

ex0601.c

(2,575)

Pate Williams (c) 1997

Exercise 6.1 "Suppose Bob is using the ElGamal Signature Scheme, and he signs two messages x1 and x2 with signatures (gamma_1, delta_1) and (gamma_2, delta_2), respectively. (The same value for gamma occurs in both signatures.) Suppose also that gcd(delta_1 - delta_2, p - 1) = 1. (c) Suppose p = 31847, alpha = 5 and beta = 25703. Perform the computations of k and a, given the signature (23972, 31396) for the message x = 8990 and the signature (23972, 20481) for the message x = 31415." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 231. */ #include <math.h> #include <stdio.h> #include <stdlib.h> struct data {long j, alpha_j;}; struct signature {long gamma, delta;}; long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) {

} if (b0 != 1) return 0; else return t % n; }

temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0;

long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

long gcd(long a, long b) { long r; while (b != 0) r = a % b, a = b, b = r; return a; } int main(void) { long A, B, a0; long a, alpha = 5, beta = 25703, p = 31847; long d, delta_p, epsilon, i, k, xp; long gamma, p1 = p - 1, x1 = 8990, x2 = 31415; struct signature s1 = {23972, 31396}; struct signature s2 = {23972, 20481}; delta_p = s1.delta - s2.delta; if (delta_p < 0) delta_p += p1; xp = x1 - x2; if (xp < 0) xp += p1; epsilon = Extended_Euclidean(delta_p, p1); k = (xp * epsilon) % p1; if (k < 0) k += p1; gamma = exp_mod(alpha, k, p); d = gcd(gamma, p1); A = gamma / d; B = (x1 - (k * s1.delta) % p1) / d; p1 /= d;

if (B < 0) B += p1; a0 = (Extended_Euclidean(A, p1) * B) % p1; for (i = 0; i < d; i++) { a = a0 + i * p1; if (beta == exp_mod(alpha, a, p)) break; } printf("gcd(delta_1 - delta_2, p - 1) = %ld\n", d); printf("(x1 - x2) mod (p - 1) = %ld\n", xp); printf("epsilon = %ld\n", epsilon); printf("gamma = %ld\n", gamma); printf("d = %ld\n", d); printf("k = %ld\n", k); printf("a = %ld\n", a); return 0; }

6.2
/* Author:

ex0602.c

freelip (3,317)

Pate Williams (c) 1997

Exercise "6.2 Suppose I implement the ElGamal Signature Scheme with p = 31847, a = 5, and beta = 26379. Write a computer program does the following. (a) Verify the signature (20679, 11082) on the message x = 20543. (b) Determine my secret exponent, a, using Shanks time-memory tradeoff. Then determine the random value k used in signing the message x." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 231. */ #include <math.h> #include <stdio.h> #include <stdlib.h> #define MAX_COUNT 256l struct data {long j, alpha_j;}; struct factor {long expon, prime;}; long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } if (a < 0) a += n; return a;

} long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

int fcmp(const void *d1, const void *d2) { struct data *data1 = (struct data *) d1; struct data *data2 = (struct data *) d2; if (data1->alpha_j < data2->alpha_j) return - 1; if (data1->alpha_j > data2->alpha_j) return + 1; return 0;

long Shanks(long alpha, long beta, long p) /* returns log(alpha, beta) in Z_p where alpha is a generator and beta is in Z_p */ { long a, alpha_i, i, log, m = ceil(sqrt(p - 1)); struct data *table1 = calloc(m, sizeof(struct data)); struct data *table2 = calloc(m, sizeof(struct data)); struct data *d, key; if (!table1 || !table2) { fprintf(stderr, "*error*\nin sufficient memory\n"); fprintf(stderr, "from Shanks\n"); exit(1); } /* create a logarithm table */ alpha_i = exp_mod(alpha, m, p); for (i = 0; i < m; i++) { table1[i].j = i; table1[i].alpha_j = exp_mod(alpha_i, i, p); } alpha_i = Extended_Euclidean(alpha, p); if ((alpha_i * alpha) % p != 1) { fprintf(stderr, "*error*\nin Extended_Euclidean\n"); exit(1);

} for (i = 0; i < m; i++) { table2[i].j = i; a = exp_mod(alpha_i, i, p); table2[i].alpha_j = (beta * a) % p; } /* sort the tables by second data item */ qsort(table1, m, sizeof(struct data), fcmp); qsort(table2, m, sizeof(struct data), fcmp); for (i = 0; i < m; i++) { key.j = table1[i].j; key.alpha_j = table1[i].alpha_j; d = bsearch(&key, table2, m, sizeof(struct data), fcmp); if (d) { log = (key.j * m + d->j) % (p - 1); if (exp_mod(alpha, log, p) == beta) return log; } } return 0;

int main(void) { long alpha = 5, beta = 26379, gamma = 20679; long a, delta = 11082, k, p = 31847; long p1 = p - 1, x = 20543, y, z; y = exp_mod(beta, gamma, p); z = exp_mod(gamma, delta, p); y = (y * z) % p; if (y == exp_mod(alpha, x, p)) printf("signature accepted\n"); else printf("signature rejected\n"); a = Shanks(alpha, beta, p); k = Shanks(alpha, gamma, p); printf("a = %ld\n", a); printf("k = %ld\n", k); y = Extended_Euclidean(k, p1); z = ((((x - a * gamma) % p1) * y) % p1) % p1; if (z < 0) z += p1; if (z != delta) printf("error in a, k calculations\n"); return 0; }

6.3
/* Author:

ex0603.c

(2,173)

Pate Williams (c) 1997

Exercise "6.3 Suppose Bob is using the ElGamal Signature Scheme as implemented in Example 6.1; p = 467, alpha = 2, and beta = 132. Suppose Bob has signed the message x = 100 with the signature (29, 51).

Compute the forged signature that Oscar can then form using h = 102, i = 45, and j = 293. Check that the resulting signature satisfies the verification condition." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 23l. */ #include <stdio.h> long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a; } int main(void) { long alpha = 2, beta = 132, h = 102, i = 45; long j = 293, p = 467, p1 = p - 1, x = 100; long gamma = 29, delta = 51; long lambda, mu, xp, r, s, t; r = exp_mod(gamma, h, p); s = exp_mod(alpha, i, p); t = exp_mod(beta, j, p); lambda = (r * s * t) % p; r = (h * gamma - j * delta) % p1; if (r < 0) r += p1;

s = Extended_Euclidean(r, p1); mu = (delta * lambda * s) % p1; xp = (lambda * ((h * x + i * delta) % p1) * s) % p1; s = exp_mod(beta, lambda, p); t = exp_mod(lambda, mu, p); printf("alpha = %ld\n", alpha); printf("beta = %ld\n", beta); printf("gamma = %ld\n", gamma); printf("delta = %ld\n", delta); printf("p = %ld\n", p); printf("x = %ld\n", x); printf("lambda = %ld\n", lambda); printf("mu = %ld\n", mu); printf("x' = %ld\n", xp); if ((s * t) % p == exp_mod(alpha, xp, p)) printf("forgery verified\n"); else printf("forgery rejected\n"); return 0;

6.6
/* Author:

ex0606.c

(1,995)

Pate Williams (c) 1997

Exercise "6.6 Suppose Bob uses the DSS with q = 101, p = 7879, alpha = 170, a = 75, and beta = 4567, as in Example 6.3. Determine Bob's signature on the message x = 5001 using the random value k = 49, and show how the resulting signature is verified." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 231. */ #include <stdio.h> long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; }

if (b0 != 1) return 0; else return t % n; } long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

int main(void) { long a = 75, alpha = 170, beta = 4567, k = 49; long p = 7879, q = 101, x = 5001; long delta, e1, e2, gamma, r, s, t; gamma = exp_mod(alpha, k, p) % q; r = Extended_Euclidean(k, q); delta = ((x + (a * gamma) % q) * r) % q; r = Extended_Euclidean(delta, q); e1 = (x * r) % q; e2 = (gamma * r) % q; r = exp_mod(alpha, e1, p); s = exp_mod(beta, e2, p); t = ((r * s) % p) % q; printf("a = %ld\n", a); printf("k = %ld\n", k); printf("p = %ld\n", p); printf("q = %ld\n", q); printf("x = %ld\n", x); printf("alpha = %ld\n", alpha); printf("beta = %ld\n", beta); printf("delta = %ld\n", delta); printf("gamma = %ld\n", gamma); printf("e1 = %ld\n", e1); printf("e2 = %ld\n", e2); printf("ver = %ld\n", t); if (t == gamma) printf("signature accepted\n"); else printf("signature rejected\n"); return 0;

6.8
/* Author:

ex0608.c

(3,176)

Pate Williams (c) 1997

*/

Exercise "6.8 In the Bos-Chaum Scheme with k = 6 and n = 4, suppose that the messages x = (0, 1, 0, 0, 1, 1) and x' = (1, 1, 0, 1, 1, 1) are signed. Determine the new messages that can be signed by Oscar knowing the signatures of x and x'." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 231.

#include <math.h> #include <stdio.h> #define SIZE 10 long factorial(long n) /* computes n factorial = n! */ { long f = 1, i; for (i = 2; i <= n; i++) f *= i; return f;

long binomial(long n, long m) /* computes the binomial coefficient B(n, m) */ { return factorial(n) / (factorial(n - m) * factorial(m)); } void Bos_Chaum(long n, long x, long *phi, long *count) { long b, e = n, t = n << 1; *count = 0; while (t > 0) { t = t - 1; b = binomial(t, e); if (x > b) { x -= b; e--; phi[*count] = t + 1; *count = *count + 1; } }

long Horner(long n, long *x) { long i, s = x[n - 1]; for (i = n - 2; i >= 0; i--) s = 2 * s + x[i]; return s; }

void construct(long count, long k, long n, long *phi, long *x, long *X) { int equal; long i, j, max = pow(2, k), y; long p_count, phip[SIZE]; for (i = 0; i < max; i++) { Bos_Chaum(n, i, phip, &p_count); if (count == p_count) { equal = phi[0] == phip[0]; for (j = 1; equal && j < count; j++) equal = phi[j] == phip[j]; if (equal) { y = *X = i; for (j = 0; j < k; j++) { x[j] = y & 1; y >>= 1; } return; } } } *X = 0;

int main(void) { long i, k = 6, n = 4, r, s; long u_count, v_count; long x_count = 3, y_count = 4; long u[SIZE] = {0, 1, 0, 0, 1, 1}; long v[SIZE] = {1, 1, 0, 1, 1, 1}; long x[SIZE], y[SIZE]; long u_phi[SIZE], v_phi[SIZE]; long x_phi[SIZE] = {8, 6, 4}; long y_phi[SIZE] = {8, 7, 4, 2}; r = Horner(k, u); s = Horner(k, v); Bos_Chaum(n, r, u_phi, &u_count); Bos_Chaum(n, s, v_phi, &v_count); printf("the original messages are:\n"); printf("x = %ld = ", r); for (i = 0; i < k; i++) printf("%ld ", u[i]); printf("\nx' = %ld = ", s); for (i = 0; i < k; i++) printf("%ld ", v[i]); printf("\np = "); for (i = 0; i < u_count; i++) printf("%ld ", u_phi[i]); printf("\np' = "); for (i = 0; i < v_count; i++) printf("%ld ", v_phi[i]); construct(x_count, k, n, x_phi, x, &r);

construct(y_count, k, n, y_phi, y, &s); r = Horner(k, x); s = Horner(k, y); Bos_Chaum(n, r, x_phi, &x_count); Bos_Chaum(n, s, y_phi, &y_count); printf("\nthe new messages are:\n"); printf("x = %ld = ", r); for (i = 0; i < k; i++) printf("%ld ", x[i]); printf("\nx' = %ld = ", s); for (i = 0; i < k; i++) printf("%ld ", y[i]); printf("\np = "); for (i = 0; i < x_count; i++) printf("%ld ", x_phi[i]); printf("\np' = "); for (i = 0; i < y_count; i++) printf("%ld ", y_phi[i]); return 0;

6.10
/* Author:

ex0610.c

(2,902)

Pate Williams (c) 1997

*/

Exercise "6.10 Suppose Bob is using the Chaum-van Antwerpen Undenible Signature Scheme as in Example 6.5. That is, p = 467, alpha = 4, a = 101 and beta = 449. Suppose Bob is presented with the signature y = 25 on the message x = 157 and he wishes to prove it is a forgery. Suppose Alice's random numbers are e1 = 46, e2 = 123, f1 = 198, and f2 = 11 in the disavowel protocol. Compute Alice's challenges, c and d, and Bob responses, C and D, and show that Alice's consistency check will succeed." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 232.

#include <stdio.h> long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } if (a < 0) a += n; return a;

} long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

int main(void) { long a = 101, alpha = 4, beta = 449, e1 = 46; long e2 = 123, f1 = 198, f2 = 11, i, j, p = 467; long q, x = 157, y = 25, c, d, C, D, r, s, t; q = (p - 1) >> 1; printf("a = %ld\n", a); printf("alpha = %ld\n", alpha); printf("beta = %ld\n", beta); printf("e1 = %ld\n", e1); printf("e2 = %ld\n", e2); printf("f1 = %ld\n", f1); printf("f2 = %ld\n", f2); printf("p = %ld\n", p); printf("q = %ld\n", q); printf("x = %ld\n", x); printf("y = %ld\n", y); i = Extended_Euclidean(a, q); c = (exp_mod(y, e1, p) * exp_mod(beta, e2, p)) % d = exp_mod(c, i, p); printf("Alice's challenge c = %ld\n", c); printf("Bob's response d = %ld\n", d); if (d != (exp_mod(x, e1, p) * exp_mod(alpha, e2, printf("d != x ^ e1 * alpha ^ e2 mod p\n"); else printf("d == x ^ e1 * alpha ^ e2 mod p\n"); C = (exp_mod(y, f1, p) * exp_mod(beta, f2, p)) % D = exp_mod(C, i, p); printf("Alice's challenge C = %ld\n", C); printf("Bob's response D = %ld\n", D); if (D != (exp_mod(x, f1, p) * exp_mod(alpha, f2, printf("D != x ^ f1 * alpha ^ f2 mod p\n"); else

p;

p)) % p)

p;

p)) % p)

printf("D == x ^ f1 * alpha ^ f2 mod p\n"); i = q - e2; if (i < 0) i += q; j = q - f2; if (j < 0) j += q; r = (d * exp_mod(alpha, i, p)) % p; s = exp_mod(r, f1, p); r = (D * exp_mod(alpha, j, p)) % p; t = exp_mod(r, e1, p); if (s == t) printf("Alice concludes y is a forgery\n"); else printf("Alice does not conclude y is a forgery\n"); return 0; }

6.12
/* Author:

ex0612.c

(722)

Pate Williams (c) 1997

Exercise "6.12 Suppose Bob is using the Pedersen-van Heyst Fail-stop Signature scheme, where p = 3467, alpha = 4, a0 = 1567 and beta = 514 (of course, the value of a0 is not known to Bob). (a) Using the fact that a0 = 1567, determine all possible keys K = (gamma1, gamma2, a1, a2, b1, b2) such that sig(42) = (1118, 1449). (b) Suppose that sig(420 = (1118, 1449) and sig(969) = (899, 471). Without using the fact that a0 = 1567, determine the value of K (this shows that the scheme is a one-time scheme)." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 232. */

6.13
/* Author:

ex0613.c

(2,546)

Pate Williams (c) 1997

Exercise "6.13 Suppose Bob is using the Pedersen-van Heyst Fail-stop Signature Scheme with p = 5087, alpha = 25, and beta = 1866. Suppose the key is K = (5065, 5076, 144, 874, 1873, 2345). Now suppose Bob finds the signature (2219, 458) has been forged on the message 4785. (a) Prove that this forgery satisfies the verification condition, so it is a valid

signature. (b) Show how Bob will compute the proof of forgery, a0, given this forged signature." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 232. */ #include <stdio.h> long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a; } int main(void) { long a0, a1 = 144, a2 = long alpha = 25, beta = long gamma2 = 5076, p = long r, s, x = 4785, y1

874, b1 = 1873, b2 = 2345; 1866, gamma1 = 5065; 5087, q = (p - 1) >> 1; = 2219, y2 = 458, z1, z2;

r = (gamma1 * exp_mod(gamma2, x, p)) % p; s = (exp_mod(alpha, y1, p) * exp_mod(beta, y2, p)) % p; if (r == s) printf("signature accepted\n"); else printf("signature rejected\n");

z1 = (a1 + x * b1) % q; z2 = (a2 + x * b2) % q; if (z2 > y2) a0 = ((y1 - z1) * Extended_Euclidean(z2 - y2, q)) % q; else a0 = ((z1 - y1) * Extended_Euclidean(y2 - z2, q)) % q; if (a0 < 0) a0 += q; printf("alpha = %ld\n", alpha); printf("beta = %ld\n", beta); printf("p = %ld\n", p); printf("q = %ld\n", q); printf("gamma1 = %ld\n", gamma1); printf("gamma2 = %ld\n", gamma2); printf("a1 = %ld\n", a1); printf("a2 = %ld\n", a2); printf("b1 = %ld\n", b1); printf("b2 = %ld\n", b2); printf("x = %ld\n", x); printf("y1 = %ld\n", y1); printf("y2 = %ld\n", y2); printf("sig(%ld) = (%ld, %ld)\n", x, z1, z2); printf("a0 = %ld\n", a0); if (beta == exp_mod(alpha, a0, p)) printf("a0 verified\n"); else printf("a0 not verified\n"); return 0;

Chapter 07
7.3 7.4 7.6 ex0703.c ex0704.c ex0706.c

Hash Functions
(2,058) freelip (1,776) (1,760)

Exercises

7.3
/* Author:

ex0703.c

(2,058)

Pate Williams (c) 1997

Exercise "7.3 Suppose p = 15083, alpha = 154, beta = 2307 in the Chaum-van Heijst-Piftmann Hash Function. Given the collision

*/

alpha ^ 7431 * beta ^ 5564 = alpha ^ 1459 * beta ^ 954 (mod p), compute log(alpha, beta)." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 257.

#include <stdio.h> long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n; } long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

long gcd(long a, long b) { long r; while (b != 0) r = a % b, a = b, b = r; return a; } int main(void) { long alpha = 154, beta = 2307, p = 15083; long x1 = 7431, x2 = 5564, x3 = 1459, x4 = 954; long d, log, p1 = p - 1, q = (p - 1) >> 1, x, y;

x = x4 - x2; if (x < 0) x += p1; d = gcd(x, p1); if (d == 1) { y = Extended_Euclidean(x, p1); log = ((x1 - x3) * y) % p1; } else if (d == 2) { x = x4 - x2; if (x < 0) x += q; y = Extended_Euclidean(x, q); x = (x1 - x3) * y; log = x % p1; if (beta != exp_mod(alpha, log, p)) log = (x + q) % p1; } if (beta != exp_mod(alpha, log, p)) printf("error in log calculation\n"); printf("alpha = %ld\n", alpha); printf("beta = %ld\n", beta); printf("p = %ld\n", p); printf("q = %ld\n", q); printf("x1 = %ld\n", x1); printf("x2 = %ld\n", x2); printf("x3 = %ld\n", x3); printf("x4 = %ld\n", x4); printf("d = %ld\n", d); printf("log = %ld\n", log); return 0; }

7.4
/* Author:

ex0704.c

freelip (1,776)

Pate Williams (c) 1997

*/

Exercise "5.4 Suppose n = pq, where p and q are two (secret) distinct large primes such that p = 2p_1 + 1 and q = 2q_1 + 1, where p_1 and q_1 are prime. Suppose that alpha is an element of order 2p_1q_1 in Z_n* (this is the largest order of any element in Z_n*). Define a hash function h : {1,...,n * n} -> Z_n* by the rule h(x) = alpha ^ x mod n. Now suppose that n = 603241 and alpha = 11 are used to define a hash function of this type. Suppose we are given three collisions h(1294755) = h(80115359) = h(52738737). Use this information to factor n." -Douglas R. StinsonSee "Cryptogrpahy: Theory and Practice" by Douglas R. Stinson page 257.

#include <stdio.h>

long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; if (b == 0) return 1; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a; } long gcd(long a, long b) { long r; while (b > 0) r = a % b, a = b, b = r; return a;

long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n; } int main(void)

7.6
/* Author:

ex0706.c

(1,760)

Pate Williams (c) 1997

Exercise "7.6 Using the (original) expansion function for SHS, Equation 7.1, express each of X[16],..., X[79] in terms of X[0],..., X[15]. Now, for each pair

X[i], X[j] where 1 <= i < j <= 15, use a computer program to determine lambda(i, j), which denotes the number of X[k]'s (16 <= k <= 79) such that X[i] and X[j] both occur in the expression for X[k]. What is the range of values for lambda(i, j)?" -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson. */ #include <stdio.h> int main(void) { long i, j, k, max = 0, min = 80; long matrix[80][20] = {{0}}; long lambda[20][20] = {{0}}; printf("the X[0] to X[15] usage matrix is:\n"); for (i = 0; i <= 15; i++) matrix[i][i] = 1; for (i = 16; i <= 79; i++) { for (j = 0; j <= 15; j++) { matrix[i][j] ^= matrix[i - 3][j]; matrix[i][j] ^= matrix[i - 8][j]; matrix[i][j] ^= matrix[i - 14][j]; matrix[i][j] ^= matrix[i - 16][j]; } } for (i = 1; i <= 15; i++) for (j = i + 1; j <= 15; j++) for (k = 16; k <= 79; k++) if (matrix[k][i] == 1 && matrix[k][j] == 1) lambda[i][j]++; for (i = 16; i <= 79; i++) { printf("%2ld ", i); for (j = 0; j <= 15; j++) printf("%ld ", matrix[i][j]); printf("\n"); } printf("the lambda matrix is:\n"); for (i = 1; i <= 15; i++) { for (j = i + 1; j <= 15; j++) { printf("%ld ", lambda[i][j]); if (lambda[i][j] < min) min = lambda[i][j]; if (lambda[i][j] > max) max = lambda[i][j]; } printf("\n"); } printf("maximum lambda = %ld\n", max); printf("minimum lambda = %ld\n", min); return 0;

Chapter 08 Key Distribution and Key Agreement


8.1 8.2 8.3 8.4 8.6 ex0801.c ex0802.c ex0803.c ex0804.c ex0806.c (6,278) (7,137) (1,182) (1,440) (2,929)

Exercises

8.1
/* Author:

ex0801.c

(6,278)

Pate Williams (c) 1997

*/

Exercise 8.1 "Suppose the Blom Scheme with k = 1 is implemented with four users U, V, W, and X. Suppose that p = 7873, r_u = 2365, r_v = 6648, r_w = 1837, and r_x = 2186. The secret g polnomials are as follows: g_u(x) = 6018 + 6351x g_v(x) = 3749 + 7121x g_w(x) = 7061 + 7802x g_x(x) = 635 + 6828x (a) Compute the key for each pair of users, verifying that each pair of users obtains a common key (that is K_u,v = K_v,u, etc.). (b) Show that W and X together can compute K_u,v." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 281.

#include <math.h> #include <stdio.h> #include <stdlib.h> #define SIZE 4 long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r;

q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

void Blom_Scheme(long k, long m, long p, long *r, long **g, long **K) { long i, j, l, s, t; for (i = 0; i < m; i++) { for (j = i + 1; j < m; j++) { t = r[j]; s = g[i][k]; for (l = k - 1; l >= 0; l--) s = t * s + g[i][l]; K[i][j] = s % p; } } for (i = 0; i < m; i++) { for (j = 0; j < i; j++) { t = r[j]; s = g[i][k]; for (l = k - 1; l >= 0; l--) s = t * s + g[i][l]; s %= p; if (s != K[j][i]) { fprintf(stderr, "fatal error\nin key calculation\n"); exit(1); } K[i][j] = s; } } } void gaussian_elimination(long n, long p, long *b, long *x, long **m) { int found; long *d = calloc(n, sizeof(long)), ck, dj; long i, j, k, l, sum, t; if (!d) {

} for (j = 0; j < n; j++) { found = 0, i = j; while (!found && i < n) { found = m[i][j] != 0; if (!found) i++; } if (!found) { fprintf(stderr, "fatal error\nnon-invertible matrix\n"); fprintf(stderr, "from gaussian_elimination\n"); for (k = 0; k < n; k++) { for (l = 0; l < n; l++) printf("%ld ", m[k][l]); printf("\n"); } exit(1); } if (i > j) { /* swap elements */ for (l = j; l < n; l++) t = m[i][l], m[i][l] = m[j][l], m[j][l] = t; t = b[i], b[i] = b[j], b[j] = t; } dj = d[j] = Extended_Euclidean(m[j][j], p); if (dj == 0) { fprintf(stderr, "fatal error\nnon-invertlible element\n"); fprintf(stderr, "from gaussian elimination"); fprintf(stderr, "element %ld mod %ld\n", m[j][j], p); exit(1); } for (k = j + 1; k < n; k++) { ck = (dj * m[k][j]) % p; for (l = j + 1; l < n; l++) { m[k][l] = (m[k][l] - (ck * m[j][l]) % p) % p; if (m[k][l] < 0) m[k][l] += p; } b[k] = (b[k] - (ck * b[j]) % p) % p; if (b[k] < 0) b[k] += p; } } for (i = n - 1; i >= 0; i--) { sum = 0; for (j = i + 1; j < n; j++) sum += (m[i][j] * x[j]) % p; if (sum < 0) sum += p; x[i] = (d[i] * ((b[i] - sum) % p)) % p; if (x[i] < 0) x[i] += p; }

fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from gaussian_elimination\n"); exit(1);

void print_matrix(long m, long n, long **matrix) { long i, j;

for (i = 0; i < m; i++) { for (j = 0; j < n; j++) printf("%4ld ", matrix[i][j]); printf("\n"); }

int main(void) { long i, j, k = 1, m = 4, n = 3, p = 7873, s; long b[SIZE], c[SIZE], r[SIZE], x[SIZE]; long **a, **g, **h, **K; long A = 2537, B = 4128, C = 6701; a = calloc(m, sizeof(long *)); g = calloc(m, sizeof(long *)); h = calloc(m, sizeof(long *)); K = calloc(m, sizeof(long *)); if (!a || !g || !h || !K) { fprintf(stderr, "fatal error\ninsufficient memory\n"); exit(1); } for (i = 0; i < m; i++) { a[i] = calloc(n, sizeof(long)); g[i] = calloc(k, sizeof(long)); h[i] = calloc(n, sizeof(long)); K[i] = calloc(m, sizeof(long)); if (!a[i] || !g[i] || !h[i] || !K[i]) { fprintf(stderr, "fatal error\ninsufficient memory\n"); exit(1); } } /* g[0][0] g[1][0] g[2][0] g[3][0] = 6018, g[0][1] = 6351; = 3749, g[1][1] = 7121; = 7601, g[2][1] = 7802; = 635, g[3][1] = 6828;

*/

r[0] = 2365; r[1] = 6648; r[2] = 1837; r[3] = 2186; for (i = 0; i < m; i++) { g[i][0] = (A + (B * r[i]) % p) % p; g[i][1] = (B + (C * r[i]) % p) % p; } Blom_Scheme(k, m, p, r, g, K); printf("the g matrix is:\n"); print_matrix(m, k + 1, g); printf("the K matrix is:\n"); print_matrix(m, m, K); a[0][0] = 1; a[0][1] = r[2]; a[1][1] = 1; a[1][2] = r[2]; a[2][0] = 1; a[2][1] = r[3];

a[3][1] = 1; a[3][2] = r[3]; b[0] = g[2][0]; b[1] = g[2][1]; b[2] = g[3][0]; b[3] = g[3][1]; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { s = 0; for (k = 0; k < m; k++) s += (a[k][i] * a[k][j]) % p; s %= p; h[i][j] = s; } } printf("the A matrix:\n"); print_matrix(m, n, a); printf("A_t * A:\n"); print_matrix(n, n, h); for (i = 0; i < n; i++) { s = 0; for (j = 0; j < m; j++) s += (a[j][i] * b[j]) % p; s %= p; c[i] = s; } gaussian_elimination(n, p, c, x, h); printf("a, b, c in program:\n"); printf("%ld\n", A); printf("%ld\n", B); printf("%ld\n", C); printf("a, b, c calculated:\n"); for (i = 0; i < n; i++) printf("%ld\n", x[i]); h[0][0] = (x[0] + (x[1] * r[0]) % p) % p; h[0][1] = (x[1] + (x[2] * r[0]) % p) % p; printf("g_u given and calculated:\n"); printf("%4ld %4ld\n", g[0][0], h[0][0]); printf("%4ld %4ld\n", g[0][1], h[0][1]); for (i = 0; i < m; i++) { free(a[i]); free(g[i]); free(h[i]); free(K[i]); } free(a); free(g); free(h); free(K); return 0; }

8.2
/* Author:

ex0802.c

(7,137)

Pate Williams (c) 1997

Exercise 8.2 "Suppose the Blom Scheme with k = 2 is implemented for a set of five users, U, V, W, X and Y. Suppose that p = 97, r_u = 14, r_v = 38, r_w = 93, r_x = 69, and r_y = 70. The secret g polnomials are as follows: g_u(x) = 15 + 15x + 2x^2 g_v(x) = 95 + 77x + 83x^2 g_w(x) = 88 + 32x + 18x^2 g_x(x) = 62 + 91x + 59x^2 g_y(x) = 10 + 82x + 52x^2 (a) Show how U and V will compute the key K_u,v = K_v,u. (b) Show how W, X, and Y together can compute K_u,v." -Douglas R. Stinson(b) Show that W and X together can compute K_u,v." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson pages 281-282. The polynomial is: f(x, y) = a + b(x + y) + c(x^2 + y^2) + dxy + e(xy^2 + x^2y) + fx^2y^2 Any three users can collaborate to determine the coefficients of the polynomial by solving a nine by six system of overdetermined equations. */ #include <math.h> #include <stdio.h> #include <stdlib.h> #define SIZE 10 long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

void Blom_Scheme(long k, long m, long p,

long *r, long **g, long **K) long i, j, l, s, t; for (i = 0; i < m; i++) { for (j = i + 1; j < m; j++) { t = r[j]; s = g[i][k]; for (l = k - 1; l >= 0; l--) s = t * s + g[i][l]; K[i][j] = s % p; } } for (i = 0; i < m; i++) { for (j = 0; j < i; j++) { t = r[j]; s = g[i][k]; for (l = k - 1; l >= 0; l--) s = t * s + g[i][l]; s %= p; if (s != K[j][i]) { fprintf(stderr, "fatal error\nin key calculation\n"); exit(1); } K[i][j] = s; } }

void gaussian_elimination(long n, long p, long *b, long *x, long **m) { int found; long *d = calloc(n, sizeof(long)), ck, dj; long i, j, k, l, sum, t; if (!d) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from gaussian_elimination\n"); exit(1); } for (j = 0; j < n; j++) { found = 0, i = j; while (!found && i < n) { found = m[i][j] != 0; if (!found) i++; } if (!found) { fprintf(stderr, "fatal error\nnon-invertible matrix\n"); fprintf(stderr, "from gaussian_elimination\n"); for (k = 0; k < n; k++) { for (l = 0; l < n; l++) printf("%ld ", m[k][l]); printf("\n"); } exit(1);

} for (i = n - 1; i >= 0; i--) { sum = 0; for (j = i + 1; j < n; j++) sum += (m[i][j] * x[j]) % p; if (sum < 0) sum += p; x[i] = (d[i] * ((b[i] - sum) % p)) % p; if (x[i] < 0) x[i] += p; } }

} if (i > j) { /* swap elements */ for (l = j; l < n; l++) t = m[i][l], m[i][l] = m[j][l], m[j][l] = t; t = b[i], b[i] = b[j], b[j] = t; } dj = d[j] = Extended_Euclidean(m[j][j], p); if (dj == 0) { fprintf(stderr, "fatal error\nnon-invertlible element\n"); fprintf(stderr, "from gaussian elimination"); fprintf(stderr, "element %ld mod %ld\n", m[j][j], p); exit(1); } for (k = j + 1; k < n; k++) { ck = (dj * m[k][j]) % p; for (l = j + 1; l < n; l++) { m[k][l] = (m[k][l] - (ck * m[j][l]) % p) % p; if (m[k][l] < 0) m[k][l] += p; } b[k] = (b[k] - (ck * b[j]) % p) % p; if (b[k] < 0) b[k] += p; }

void print_matrix(long m, long n, long **matrix) { long i, j; for (i = 0; i < m; i++) { for (j = 0; j < n; j++) printf("%2ld ", matrix[i][j]); printf("\n"); }

int main(void) { long i, j, k = 2, m = 9, l, mm = 5, n = 6, p = 97, s; long b[SIZE], c[SIZE], r[SIZE], x[SIZE]; long **a, **g, **h, **K; a = calloc(m, sizeof(long *)); g = calloc(m, sizeof(long *)); h = calloc(m, sizeof(long *)); K = calloc(m, sizeof(long *)); if (!a || !g || !h || !K) { fprintf(stderr, "fatal error\ninsufficient memory\n");

exit(1); } for (i = 0; i < m; i++) { a[i] = calloc(n, sizeof(long)); g[i] = calloc(k + 1, sizeof(long)); h[i] = calloc(n, sizeof(long)); K[i] = calloc(m, sizeof(long)); if (!a[i] || !g[i] || !h[i] || !K[i]) { fprintf(stderr, "fatal error\ninsufficient memory\n"); exit(1); } } g[0][0] = 15, g[0][1] = 15, g[0][2] = 2; g[1][0] = 95, g[1][1] = 77, g[1][2] = 83; g[2][0] = 88, g[2][1] = 32, g[2][2] = 18; g[3][0] = 62, g[3][1] = 91, g[3][2] = 59; g[4][0] = 10, g[4][1] = 82, g[4][2] = 52; r[0] = 14; r[1] = 38; r[2] = 92; r[3] = 69; r[4] = 70; Blom_Scheme(k, mm, p, r, g, K); printf("the g matrix is:\n"); print_matrix(mm, k + 1, g); printf("the r vector is:\n"); for (i = 0; i < mm; i++) printf("%2ld ", r[i]); printf("\n"); printf("the K matrix is:\n"); print_matrix(mm, mm, K); a[0][0] = 1; a[0][1] = r[2]; a[0][2] = r[2] * r[2]; a[1][1] = 1; a[1][3] = r[2]; a[1][4] = r[2] * r[2]; a[2][2] = 1; a[2][4] = r[2]; a[2][5] = r[2] * r[2]; a[3][0] = 1; a[3][1] = r[3]; a[3][2] = r[3] * r[3]; a[4][1] = 1; a[4][3] = r[3]; a[4][4] = r[3] * r[3]; a[5][2] = 1; a[5][4] = r[3]; a[5][5] = r[3] * r[3]; a[6][0] = 1; a[6][1] = r[4]; a[6][2] = r[4] * r[4]; a[7][1] = 1; a[7][3] = r[4]; a[7][4] = r[4] * r[4]; a[8][2] = 1; a[8][4] = r[4];

a[8][5] = r[4] * r[4]; b[0] = g[2][0]; b[1] = g[2][1]; b[2] = g[2][2]; b[3] = g[3][0]; b[4] = g[3][1]; b[5] = g[3][2]; b[6] = g[4][0]; b[7] = g[4][1]; b[8] = g[4][2]; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { s = 0; for (l = 0; l < m; l++) s += (a[l][i] * a[l][j]) % p; s %= p; h[i][j] = s; } } printf("the A matrix:\n"); print_matrix(m, n, a); printf("A_t * A:\n"); print_matrix(n, n, h); for (i = 0; i < n; i++) { s = 0; for (j = 0; j < m; j++) s += (a[j][i] * b[j]) % p; s %= p; c[i] = s; } gaussian_elimination(n, p, c, x, h); printf("a, b, c, d, e, f calculated:\n"); for (i = 0; i < n; i++) printf("%ld ", x[i]); printf("\n"); h[0][0] = x[0] + x[1] * r[0] + x[2] * r[0] h[0][1] = x[1] + x[3] * r[0] + x[4] * r[0] h[0][2] = x[2] + x[4] * r[0] + x[5] * r[0] printf("g_u given and calculated\n"); for (i = 0; i <= k; i++) printf("%2ld %2ld\n", g[0][i], h[0][i] % for (i = 0; i < m; i++) { free(a[i]); free(g[i]); free(h[i]); free(K[i]); } free(a); free(g); free(h); free(K); return 0; }

* r[0]; * r[0]; * r[0]; p);

8.3

ex0803.c

(1,182)

/*

Author:

Pate Williams (c) 1997

Exercise 8.3 "Suppose that U and V carry out the Diffie-Hellman Key Exchange with p = 27001 and alpha = 101. Suppose that U chooses a_u = 21768 and V chooses a_v = 9898. Show the computations performed by both U and V, and determine the key they will compute." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 282. */ #include <stdio.h> long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

int main(void) { long alpha = 101, p = 27001; long a_u = 21768, a_v = 9898, b_u, b_v; long k_vu, k_uv; b_u = exp_mod(alpha, a_u, p); b_v = exp_mod(alpha, a_v, p); k_vu = exp_mod(b_u, a_v, p); k_uv = exp_mod(b_v, a_u, p); printf("alpha = %ld\n", alpha); printf("p = %ld\n", p); printf("a_u = %ld\n", a_u); printf("a_v = %ld\n", a_v); printf("b_u = %ld\n", b_u); printf("b_v = %ld\n", b_v); printf("K_v,u = %ld\n", k_vu); printf("K_u,v = %ld\n", k_uv); return 0; }

8.4
/* Author:

ex0804.c

(1,440)

Pate Williams (c) 1997

Exercise 8.4

*/

"Suppose that U and V carry out the MTI Protocol where p = 30113 and alpha = 52. Suppose that U has a_u = 8642 and chooses r_u = 28654, and V has a_v = 24763 and chooses r_v = 12385. Show the computations performed by both U and V, and determine the key they will compute." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 282.

#include <stdio.h> long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a; } int main(void) { long alpha = 52, p = 30113; long a_u = 8642, a_v = 24763; long r_u = 28654, r_v = 12385; long b_u, b_v, s_u, s_v, K_uv, K_vu; b_u = exp_mod(alpha, a_u, p); b_v = exp_mod(alpha, a_v, p); s_u = exp_mod(alpha, r_u, p); s_v = exp_mod(alpha, r_v, p); K_uv = (exp_mod(s_v, a_u, p) * exp_mod(b_v, r_u, p)) % p; K_vu = (exp_mod(s_u, a_v, p) * exp_mod(b_u, r_v, p)) % p; printf("alpha = %ld\n", alpha); printf("p = %ld\n", p); printf("a_u = %ld\n", a_u); printf("a_v = %ld\n", a_v); printf("r_u = %ld\n", r_u); printf("r_v = %ld\n", r_v); printf("b_u = %ld\n", b_u); printf("b_v = %ld\n", b_v); printf("K_u,v = %ld\n", K_uv); printf("K_v,u = %ld\n", K_vu); return 0; }

8.6
/* Author:

ex0806.c

(2,929)

Pate Williams (c) 1997

*/

Exercise 8.6 "Consider the Girault Scheme where p = 167, q = 179, and hence n = 29893. Suppose alpha = 2 and e = 11101. (a) Compute d. (b) Given ID(U) = 10021 and a_u = 9843, compute b_u and p_u. Given that ID(V) = 10022 and a_v = 7692, compute b_v and p_v. (c) Show how b_u can be computed from p_u and ID(U) using the public exponent e. Similarly, show how b_v can be computed from p_v and ID(V). (d) Suppose that U chooses r_u = 15556 and V chooses r_v = 6420. Compute s_u and s_v, and show how U and V each compute a common key." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 282.

#include <stdio.h> long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n; } long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

int main(void) {

long long long long long long long

alpha = 2, e = 11101, p = 167, q = 179; a_u = 9843, a_v = 7692, c_u, c_v; n = p * q, phi = (p - 1) * (q - 1); d = Extended_Euclidean(e, phi); ID_U = 10021, ID_V = 10022; r_u = 15556, r_v = 6420; b_u, b_v, p_u, p_v, s_u, s_v, K_uv, K_vu;

b_u = exp_mod(alpha, a_u, n); b_v = exp_mod(alpha, a_v, n); c_u = b_u - ID_U; if (c_u < 0) c_u += n; c_v = b_v - ID_V; if (c_v < 0) c_v += n; p_u = exp_mod(c_u, d, n); p_v = exp_mod(c_v, d, n); s_u = exp_mod(alpha, r_u, n); s_v = exp_mod(alpha, r_v, n); c_u = (exp_mod(p_v, e, n) + ID_V) % n; c_v = (exp_mod(p_u, e, n) + ID_U) % n; K_uv = (exp_mod(s_v, a_u, n) * exp_mod(c_u, r_u, n)) % n; K_vu = (exp_mod(s_u, a_v, n) * exp_mod(c_v, r_v, n)) % n; c_u = (exp_mod(p_u, e, n) + ID_U) % n; c_v = (exp_mod(p_v, e, n) + ID_V) % n; printf("alpha = %ld\n", alpha); printf("p = %ld\n", p); printf("q = %ld\n", q); printf("n = %ld\n", n); printf("a_u = %ld\n", a_u); printf("a_v = %ld\n", a_v); printf("ID(U) = %ld\n", ID_U); printf("ID(V) = %ld\n", ID_V); printf("r_u = %ld\n", r_u); printf("r_v = %ld\n", r_v); printf("e = %ld\n", e); printf("d = %ld\n", d); printf("b_u = %ld\n", b_u); printf("b_v = %ld\n", b_v); printf("p_u = %ld\n", p_u); printf("p_v = %ld\n", p_v); printf("b_u another way = %ld\n", c_u); printf("b_v another way = %ld\n", c_v); printf("K_uv = %ld\n", K_uv); printf("K_vu = %ld\n", K_vu); return 0;

Chapter 09 9.2 9.3 ex0902.c ex0903.c

Identifciation Schemes freelip (1,891) freelip (1,724)

9.4 9.5 9.6 9.7

ex0904.c ex0905.c ex0906.c ex0907.c

freelip (2,591) freelip (3,245) freelip (1,716) freelip (1,605)

Exercises

9.2
/* Author:

ex0902.c

freelip (1,891)

Pate Williams (c) 1997

Exercise 9.2 "Suppose Alice is using the Schnorr Scheme where q = 1201, p = 122503, t = 10, and alpha = 11538. (a) Verify that alpha has order q in Z_p*. (b) Suppose that Alice's secret exponent is a = 357. Compute v. (c) Suppose that k = 868. Compute gamma. (d) Suppose that Bob issues the challenge r = 501. Compute Alice's response y. (e) Perform Bob's calculations to verify y." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson pages 303-304. */ #include <stdio.h> #include "lip.h" int main(void) { long alpha = 11538, p = 122503, q = 1201; long a = 357, k = 868, r = 501; verylong za = 0, zb = 0, zc = 0, zk = 0, zp = 0; verylong zq = 0, zr = 0, zv = 0, zy = 0; verylong zalpha = 0, zgamma = 0; zintoz(alpha, &zalpha); zintoz(a, &za); zintoz(k, &zk); zintoz(p, &zp); zintoz(q, &zq); zintoz(r, &zr); zexpmod(zalpha, zq, zp, &zb); printf("alpha = %ld\n", alpha);

printf("p = %ld\n", p); printf("q = %ld\n", q); printf("a = %ld\n", a); printf("k = %ld\n", k); printf("r = %ld\n", r); printf("alpha ^ q mod p = "); zwriteln(zb); zcopy(za, &zb); znegate(&zb); zadd(zb, zq, &zc); zexpmod(zalpha, zc, zp, &zv); printf("v = "); zwriteln(zv); zexpmod(zalpha, zk, zp, &zgamma); printf("gamma = "); zwriteln(zgamma); zmulmod(za, zr, zq, &zb); zaddmod(zk, zb, zq, &zy); printf("y = "); zwriteln(zy); zexpmod(zalpha, zy, zp, &zb); zexpmod(zv, zr, zp, &zc); zmulmod(zb, zc, zp, &za); if (zcompare(za, zgamma) == 0) printf("y verified\n"); else printf("y not verfied\n"); zfree(&za); zfree(&zb); zfree(&zc); zfree(&zk); zfree(&zp); zfree(&zq); zfree(&zr); zfree(&zy); zfree(&zalpha); zfree(&zgamma); return 0; }

9.3
/* Author:

ex0903.c

freelip (1,724)

Pate Williams (c) 1997

*/

Exercise 9.3 "Suppose that Alice uses the Schnorr Scheme with p, q, t, and alpha as in Exercise 9.2. Now suppose that v = 51131, and Olga has learned that alpha ^ 3 v ^ 148 = alpha ^ 151 v ^ 1077 mod p. Show how Olga can compute Alice's secret exponent a." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 304.

#include <stdio.h> #include "lip.h" int main(void) { long alpha = 11538, p = 122503, q = 1201; long r1 = 148, r2 = 1077, y1 = 3, y2 = 151; long v = 51131; verylong za = 0, zb = 0, zc = 0, zd = 0, zp = 0; verylong zq = 0, zv = 0; verylong zalpha = 0, zr1 = 0, zr2 = 0; verylong zy1 = 0, zy2 = 0; zintoz(alpha, &zalpha); zintoz(p, &zp); zintoz(q, &zq); zintoz(v, &zv); zintoz(r1, &zr1); zintoz(r2, &zr2); zintoz(y1, &zy1); zintoz(y2, &zy2); zexpmod(zalpha, zq, zp, &zb); printf("alpha = %ld\n", alpha); printf("p = %ld\n", p); printf("q = %ld\n", q); printf("v = %ld\n", v); printf("r_1 = %ld\n", r1); printf("r_2 = %ld\n", r2); printf("y_1 = %ld\n", y1); printf("y_2 = %ld\n", y2); zsubmod(zy1, zy2, zq, &zb); zsubmod(zr1, zr2, zq, &zc); zinvmod(zc, zq, &zd); zmulmod(zb, zd, zq, &za); printf("a = "); zwriteln(za); zsub(zq, za, &zb); zexpmod(zalpha, zb, zp, &zc); if (zcompare(zc, zv) == 0) printf("private exponent verified\n"); else printf("private exponent not verified\n"); zfree(&za); zfree(&zb); zfree(&zc); zfree(&zd); zfree(&zp); zfree(&zq); zfree(&zv); zfree(&zalpha); zfree(&zr1); zfree(&zr2); zfree(&zy1); zfree(&zy2); return 0;

9.4
/* Author:

ex0904.c

freelip (2,591)

Pate Williams (c) 1997

*/

Exercise 9.4 "Suppose that Alice is using the Okamoto Scheme with q = 1201, p = 122503, t = 10, alpha_1 = 60497 and alpha_2 = 17163. (a) Suppose that Alice's secret exponents are a_1 = 432 and a_2 = 423. Compute v. (b) Suppose that k_1 = 389 and k_2 = 191. Compute gamma. (c) Suppose that Bob issues the challenge r = 21. Compute Alices's response y_1 and y_2. (d) Perform Bob's calculations to verify y_1 and y_2." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 304.

#include <stdio.h> #include "lip.h" int main(void) { long alpha1 = 60497, alpha2 = 17163; long p = 122503, q = 1201, r = 21; long a1 = 432, a2 = 423, k1 = 389, k2 = 191; verylong za = 0, zb = 0, zc = 0, zd = 0; verylong zp = 0, zq = 0, zr = 0, zv = 0; verylong za1 = 0, za2 = 0, zk1 = 0, zk2 = 0; verylong zy1 = 0, zy2 = 0; verylong zalpha1 = 0, zalpha2 = 0, zgamma = 0; zintoz(alpha1, &zalpha1); zintoz(alpha2, &zalpha2); zintoz(p, &zp); zintoz(q, &zq); zintoz(r, &zr); zintoz(a1, &za1); zintoz(a2, &za2); zintoz(k1, &zk1); zintoz(k2, &zk2); zsub(zq, za1, &za); zsub(zq, za2, &zb); zexpmod(zalpha1, za, zp, &zc); zexpmod(zalpha2, zb, zp, &zd); zmulmod(zc, zd, zp, &zv); zexpmod(zalpha1, zk1, zp, &za); zexpmod(zalpha2, zk2, zp, &zb); zmulmod(za, zb, zp, &zgamma); zmulmod(za1, zr, zq, &za); zaddmod(zk1, za, zq, &zy1); zmulmod(za2, zr, zq, &za); zaddmod(zk2, za, zq, &zy2);

zexpmod(zalpha1, zy1, zp, &za); zexpmod(zalpha2, zy2, zp, &zb); zexpmod(zv, zr, zp, &zc); zmulmod(za, zb, zp, &zd); zmulmod(zc, zd, zp, &za); printf("alpha_1 = %ld\n", alpha1); printf("alpha_2 = %ld\n", alpha2); printf("p = %ld\n", p); printf("q = %ld\n", q); printf("a_1 = %ld\n", a1); printf("a_2 = %ld\n", a2); printf("k_1 = %ld\n", k1); printf("k_2 = %ld\n", k2); printf("r = %ld\n", r); printf("v = "); zwriteln(zv); printf("gamma = "); zwriteln(zgamma); printf("y_1 = "); zwriteln(zy1); printf("y_2 = "); zwriteln(zy2); if (zcompare(zgamma, za) == 0) printf("y_1 and y_2 verified\n"); else printf("y_1 and y_2 not verified\n"); zfree(&za); zfree(&zb); zfree(&zc); zfree(&zd); zfree(&zp); zfree(&zq); zfree(&zr); zfree(&zv); zfree(&za1); zfree(&za2); zfree(&zk1); zfree(&zk2); zfree(&zy1); zfree(&zy2); zfree(&zalpha1); zfree(&zalpha2); zfree(&zgamma); return 0; }

9.5
/* Author:

ex0905.c

freelip (3,245)

Pate Williams (c) 1997

Exercise 9.5 "Suppose that Alice uses the Okamoto Scheme with p, q, t, alpha_1 and alpha_2 as in Exercise 9.4. Suppose also that v = 119504. (a) Verify that

*/

alpha_1 ^ 70 alpha_2 ^ 1033 v ^ 877 = alpha_1 ^ 248 alpha_2 ^ 882 v ^ 992 mod p. (b) Use this information to compute b_1 and b_2 such that alpha_1 ^ - b_1 alpha_2 ^ - b_2 = v mod p. (c) Now suppose that Alice reveals that a_1 = 484 and a_2 = 935. Show how Alice and Olga together will compute log(alpha_1, alpha_2)." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 304.

#include <stdio.h> #include "lip.h" int main(void) { long alpha1 = 60497, alpha2 = 17163; long p = 122503, q = 1201, r = 877, s = 992; long v = 119504; long a1 = 484, a2 = 935; long y1 = 70, y2 = 1033, z1 = 248, z2 = 883; verylong za = 0, zb = 0, zc = 0, zd = 0, ze = 0; verylong zf = 0, zp = 0, zq = 0, zr = 0, zs = 0; verylong zv = 0; verylong za1 = 0, za2 = 0, zb1 = 0, zb2 = 0; verylong zy1 = 0, zy2 = 0, zz1 = 0, zz2 = 0; verylong zalpha1 = 0, zalpha2 = 0; zintoz(alpha1, &zalpha1); zintoz(alpha2, &zalpha2); zintoz(p, &zp); zintoz(q, &zq); zintoz(r, &zr); zintoz(s, &zs); zintoz(v, &zv); zintoz(a1, &za1); zintoz(a2, &za2); zintoz(y1, &zy1); zintoz(y2, &zy2); zintoz(z1, &zz1); zintoz(z2, &zz2); zexpmod(zalpha1, zy1, zp, &za); zexpmod(zalpha2, zy2, zp, &zb); zexpmod(zv, zr, zp, &zc); zmulmod(za, zb, zp, &zd); zmulmod(zc, zd, zp, &ze); zexpmod(zalpha1, zz1, zp, &za); zexpmod(zalpha2, zz2, zp, &zb); zexpmod(zv, zs, zp, &zc); zmulmod(za, zb, zp, &zd); zmulmod(zc, zd, zp, &zf); zsubmod(zy1, zz1, zq, &za); zsubmod(zr, zs, zq, &zb); zinvmod(zb, zq, &zc); zmulmod(za, zc, zq, &zb1);

zsubmod(zy2, zz2, zq, &za); zmulmod(za, zc, zq, &zb2); zsubmod(za1, zb1, zq, &za); zsubmod(zb2, za2, zq, &zb); zinvmod(zb, zq, &zd); zmulmod(za, zd, zq, &zc); zexpmod(zalpha1, zc, zp, &za); printf("alpha_1 = %ld\n", alpha1); printf("alpha_2 = %ld\n", alpha2); printf("p = %ld\n", p); printf("q = %ld\n", q); printf("r = %ld\n", r); printf("s = %ld\n", s); printf("v = %ld\n", v); printf("a_1 = %ld\n", a1); printf("a_2 = %ld\n", a2); printf("y_1 = %ld\n", y1); printf("y_2 = %ld\n", y2); printf("z_1 = %ld\n", z1); printf("z_2 = %ld\n", z2); if (zcompare(ze, zf) == 0) printf("equation verified\n"); else printf("equation not verified\n"); printf("b_1 = "); zwriteln(zb1); printf("b_2 = "); zwriteln(zb2); printf("log(alpha_1, alpha_2) = "); zwriteln(zc); if (zcompare(za, zalpha2) == 0) printf("log(alpha_1, alpha_2) verified\n"); else printf("log(alpha_1, alpha_2) not verified\n"); zfree(&za); zfree(&zb); zfree(&zc); zfree(&zd); zfree(&ze); zfree(&zf); zfree(&zp); zfree(&zq); zfree(&zr); zfree(&zs); zfree(&zv); zfree(&za1); zfree(&za2); zfree(&zb1); zfree(&zb2); zfree(&zy1); zfree(&zy2); zfree(&zz1); zfree(&zz2); zfree(&zalpha1); zfree(&zalpha2); return 0; }

9.6
/* Author:

ex0906.c

freelip (1,716)

Pate Williams (c) 1997

*/

Exercise 9.6 "Suppose that Alice is using the Quisquater Scheme with p = 503, q = 379, and b = 509. (a) Suppose that Alice's secret u = 155863. Compute v. (b) Suppose that k = 123845. Compute gamma. (c) Suppose that Bob issues the challenge r = 487. Compute Alice's response y. (d) Perform Bob's calculation to verify y." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 304.

#include <stdio.h> #include "lip.h" int main(void) { long b = 509, k = 123845, p = 503, q = 379; long n = p * q, r = 487, u = 155863; verylong za = 0, zb = 0, zc = 0, zd = 0, zk = 0; verylong zn = 0, zr = 0, zu = 0, zv = 0, zy = 0; verylong zgamma = 0; zintoz(b, &zb); zintoz(k, &zk); zintoz(n, &zn); zintoz(r, &zr); zintoz(u, &zu); zexpmod(zk, zb, zn, &zgamma); zexpmod(zu, zr, zn, &za); zmulmod(za, zk, zn, &zy); zinvmod(zu, zn, &za); zexpmod(za, zb, zn, &zv); zexpmod(zv, zr, zn, &za); zexpmod(zy, zb, zn, &zc); zmulmod(za, zc, zn, &zd); printf("p = %ld\n", p); printf("q = %ld\n", q); printf("b = %ld\n", b); printf("n = %ld\n", n); printf("k = %ld\n", k); printf("r = %ld\n", r); printf("u = %ld\n", u); printf("v = "); zwriteln(zv); printf("gamma = "); zwriteln(zgamma); printf("y = "); zwriteln(zy);

if (zcompare(zd, zgamma) == 0) printf("Bob has verified y\n"); else printf("Bob did no verify y\n"); zfree(&za); zfree(&zb); zfree(&zc); zfree(&zd); zfree(&zk); zfree(&zn); zfree(&zr); zfree(&zu); zfree(&zv); zfree(&zy); zfree(&zgamma); return 0; }

9.7
/* Author:

ex0907.c

freelip (1,605)

Pate Williams (c) 1997

Exercise 9.7 "Suppose that Alice is using the Quisquater Scheme with n = 199543, b = 523, and v = 146152. Suppose that Olga has discovered that v ^ 465 101360 ^ b = v ^ 257 36056 ^ b mod n. Show how Olga can compute u." -Douglas R. StinsonSee "Cryptograhy: Theory and Practice" by Douglas R. Stinson page 304. */ #include <stdio.h> #include "lip.h" int main(void) { long b = 523, n = 199543l, v = 146152l; long r1 = 456, r2 = 257, y1 = 101360l; long y2 = 36056l; verylong za = 0, zb = 0, zc = 0, zd = 0, zl = 0; verylong zn = 0, zt = 0, zu = 0, zv = 0; verylong zr1 = 0, zr2 = 0, zy1 = 0, zy2 = 0; zintoz(b, &zb); zintoz(n, &zn); zintoz(v, &zv); zintoz(r1, &zr1); zintoz(r2, &zr2); zintoz(y1, &zy1); zintoz(y2, &zy2); zsubmod(zr1, zr2, zb, &za); zinvmod(za, zb, &zt); zmul(za, zt, &zc); zsadd(zc, - 1l, &zd);

zdiv(zd, zb, &zl, &zc); zdiv(zy1, zy2, &za, &zc); zexpmod(za, zt, zn, &zc); zexpmod(zv, zl, zn, &za); zmulmod(za, zc, zn, &zu); printf("b = %ld\n", b); printf("n = %ld\n", n); printf("v = %ld\n", v); printf("r_1 = %ld\n", r1); printf("r_2 = %ld\n", r2); printf("y_1 = %ld\n", y1); printf("y_2 = %ld\n", y2); printf("t = "); zwriteln(zt); printf("l = "); zwriteln(zl); printf("u = "); zwriteln(zu); zfree(&za); zfree(&zb); zfree(&zc); zfree(&zd); zfree(&zl); zfree(&zn); zfree(&zt); zfree(&zu); zfree(&zv); zfree(&zr1); zfree(&zr2); zfree(&zy1); zfree(&zy2); return 0; }

Chapter 10 10.1 10.4 ex1001.c ex1004.c

Authentication Codes (3,415) (1,550)

Exercises

10.1
/* Author:

ex1001.c

(3,415)

Pate Williams (c) 1997

Exercise 10.1 "Compute Pd_0 and Pd_1 for the following authenication code, represented in matrix

form: key 1 2 3 4 5 6 1 1 1 2 2 3 3 2 1 2 1 3 2 3 3 2 3 3 1 1 2 4 3 1 1 2 3 1

The probability distributions on S and K are as follows: p_s(1) = p_s(4) = 1 / 6, p_s(2) = p_s(3) = 1 / 3 p_k(1) = p_k(6) = 1 / 4, p_k(2) = p_k(3) = 1 / 8 p_k(4) = p_k(5) = 1 / 8. What are the optimal impersonation and substitution stategies?" -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 325.

*/

#include <stdio.h> int main(void) { double Pd_0 = 0.0, Pd_1 = 0.0, sum; double payoff[5][4], p_k[7], p_s[5]; double p[5][4], po[5][4][5][4]; long A[7][5], a, ap, k, s, sp; A[1][1] = 1, A[1][2] = 1, A[1][3] = 2, A[1][4] A[2][1] = 1, A[2][2] = 2, A[2][3] = 3, A[2][4] A[3][1] = 2, A[3][2] = 1, A[3][3] = 3, A[3][4] A[4][1] = 2, A[4][2] = 3, A[4][3] = 1, A[4][4] A[5][1] = 3, A[5][2] = 2, A[5][3] = 1, A[5][4] A[6][1] = 3, A[6][2] = 3, A[6][3] = 2, A[6][4] p_s[1] = p_s[4] = 1.0 / 6.0; p_s[2] = p_s[3] = 1.0 / 3.0; p_k[1] = p_k[6] = 1.0 / 4.0; p_k[2] = p_k[3] = p_k[4] = p_k[5] = 1.0 / 8.0; printf("the authenication matrix is:\n"); for (k = 1; k <= 6; k++) { printf("%ld ", k); for (s = 1; s <= 4; s++) printf("%ld ", A[k][s]); printf("\n"); } printf("the source probabilities are:\n"); for (s = 1; s <= 4; s++) printf("%7.5lf ", p_s[s]); printf("\nthe key probabilities are:\n"); for (k = 1; k <= 6; k++) printf("%7.5lf ", p_k[k]); printf("\nthe payoffs are:\n"); for (s = 1; s <= 4; s++) { = = = = = = 3; 1; 1; 2; 3; 1;

} printf("Pd_0 = %7.5lf\n", Pd_0); printf("the optimal impersonation strategy is:\n"); for (s = 1; s <= 4; s++) { for (a = 1; a <= 3; a++) if (payoff[s][a] == Pd_0) printf("(%ld, %ld) ", s, a); } printf("\n"); for (s = 1; s <= 4; s++) { for (a = 1; a <= 3; a++) { for (sp = 1; sp <= 4; sp++) { for (ap = 1; ap <= 3; ap++) { sum = 0.0; for (k = 1; k <= 6; k++) if (A[k][s] == a && A[k][sp] == ap) sum += p_k[k]; po[s][a][sp][ap] = sum; } } sum = 0.0; for (sp = 1; sp <= 4; sp++) { for (ap = 1; ap <= 3; ap++) if (po[s][a][sp][a] > sum) sum = po[s][a][sp][ap]; } p[s][a] = sum; } } printf("the p_s,a matrix is:\n"); for (s = 1; s <= 4; s++) { for (a = 1; a <= 3; a++) { Pd_1 += p_s[s] * payoff[s][a] * p[s][a]; printf("%7.5lf ", p[s][a]); } printf("\n"); } printf("Pd_1 = %7.5lf\n", Pd_1); printf("the optimal substitution strategy is:\n"); for (s = 1; s <= 4; s++) { for (a = 1; a <= 3; a++) { printf("(%ld, %ld) -> ", s, a); sum = p[s][a]; for (sp = 1; sp <= 4; sp++) for (ap = 1; ap <= 3; ap++) if (sum == po[s][a][sp][ap]) printf("(%ld, %ld) ", sp, ap); printf("\n"); }

for (a = 1; a <= 3; a++) { sum = 0.0; for (k = 1; k <= 6; k++) if (a == A[k][s]) sum += p_k[k]; payoff[s][a] = sum; if (sum > Pd_0) Pd_0 = sum; printf("%7.5lf ", sum); } printf("\n");

} return 0; }

10.4
/* Author:

ex1004.c

(1,550)

Pate Williams (c) 1997

*/

Exercise 10.4 "Construct an orthogonal array OA(3, 13, 3)." -Douglas R. StinsonSee "Cryptography: Theory and Practice by Douglas R. Stinson page 325.

#include <stdio.h> long radix_representation(long b, long A, long *a) { long i = 0, q, x = A; q = x / b, while (q > i++, x = return i + a[i] = x - q * b; 0) q, q = x / b, a[i] = x - q * b; 1;

int main(void) { int found; long count = 0, i, j, k, length, p = 3, s; long R[27][3], C[27][3]; printf("R\n"); for (i = 0; i < 27; i++) { length = radix_representation(3, i, R[i]); for (j = length; j < 3; j++) R[i][j] = 0; for (j = 2; j >= 0; j--) printf("%d", R[i][j]); printf(" "); if ((i + 1) % 8 == 0) printf("\n"); } for (i = 0; i < 27; i++) { found = 0; for (j = 2; !found && j >= 0; j--) { found = R[i][j] != 0; if (found) k = j; } if (found) { if (R[i][k] == 1) { for (j = 0; j < 3; j++) C[count][j] = R[i][j]; count++; } }

} printf("\nC\n"); for (i = 0; i < count; i++) { for (j = 2; j >= 0; j--) printf("%d", C[i][j]); printf(" "); if ((i + 1) % 8 == 0) printf("\n"); } printf("\nOA(3, 13, 3)\n"); for (i = 0; i < 27; i++) { for (j = 0; j < count; j++) { s = 0; for (k = 2; k >= 0; k--) s += R[i][k] * C[j][k]; s %= p; printf("%d ", s); } printf("\n"); } return 0; }

Chapter 11
11.1 11.1 te1101.c ex1101.c

Secret Sharing Schemes


(5,577) (6,774)

Example

11.1
/* Author:

te1101.c

(5,577)

Pate Williams (c) 1997

*/

Example 11.1 "Suppose that p = 17, t = 3, and w = 5; and the public x-co-ordinates are x[i] = i, 1 <= i <= 5. Suppose that B = {P[1], P[3], P[5]}, pool their shares, which are respectively 8, 10, and 11. Determine the key." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 329.

#include <stdio.h> #include <stdlib.h>

#define SIZE 256 long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

long Lagrange(long p, long t, long *x, long *y) /* computes the key using the Lagrange interpolation formula, returns the key */ { long bj, i, j, k, s = 0; for (j = 0; j < t; j++) { bj = 1; for (k = 0; k < t; k++) { if (j != k) { i = (x[k] - x[j]) % p; if (i < 0) i += p; bj = (((bj * x[k]) % p) * Extended_Euclidean(i, p)) % p; } } s = (s + (bj * y[j]) % p) % p; } if (s < 0) s += p; return s; } long share(long p, long t, long x, long *a) { long j, s; s = a[t - 1]; for (j = t - 2; j >= 0; j--) s = ((s * x) % p + a[j]) % p; if (s < 0) s += p; return s;

long **create_square_matrix(long n) {

long i, **matrix = calloc(n, sizeof(long *)); if (!matrix) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from create_matrix\n"); exit(1); } for (i = 0; i < n; i++) { matrix[i] = calloc(n, sizeof(long)); if (!matrix[i]) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from create_matrix\n"); exit(1); } } return matrix; } void delete_square_matrix(long n, long **matrix) { long i; for (i = 0; i < n; i++) free(matrix[i]); free(matrix); } void Euclid_extended(long a, long b, long *u, long *v, long *d) { long q, t1, t3, v1, v3; *u = 1, *d = a; if (b == 0) { *v = 0; return; } v1 = 0, v3 = b; while (v3 != 0) { q = *d / v3; t3 = *d - q * v3; t1 = *u - q * v1; *u = v1, *d = v3; v1 = t1, v3 = t3; } *v = (*d - a * *u) / b;

long inv(long number, long modulus) { long d, u, v; Euclid_extended(number, modulus, &u, &v, &d); if (d == 1) return u; return 0; } void gaussian_elimination(long n, long p,

long *b, long *x, long **m) { int found; long *d = calloc(n, sizeof(long)), ck, dj; long i, j, k, l, sum, t; if (!d) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from gaussian_elimination\n"); exit(1); } for (j = 0; j < n; j++) { found = 0, i = j; while (!found && i < n) { found = m[i][j] != 0; if (!found) i++; } if (!found) { fprintf(stderr, "fatal error\nnon-invertible matrix\n"); fprintf(stderr, "from gaussian_elimination\n"); for (k = 0; k < n; k++) { for (l = 0; l < n; l++) printf("%ld ", m[k][l]); printf("\n"); } exit(1); } if (i > j) { /* swap elements */ for (l = j; l < n; l++) t = m[i][l], m[i][l] = m[j][l], m[j][l] = t; t = b[i], b[i] = b[j], b[j] = t; } dj = d[j] = inv(m[j][j], p); if (dj == 0) { fprintf(stderr, "fatal error\nnon-invertlible element\n"); fprintf(stderr, "from gaussian elimination"); fprintf(stderr, "element %ld mod %ld\n", m[j][j], p); exit(1); } for (k = j + 1; k < n; k++) { ck = (dj * m[k][j]) % p; for (l = j + 1; l < n; l++) { m[k][l] = (m[k][l] - ck * m[j][l]) % p; if (m[k][l] < 0) m[k][l] += p; } b[k] = (b[k] - ck * b[j]) % p; if (b[k] < 0) b[k] += p; } } for (i = n - 1; i >= 0; i--) { sum = 0; for (j = i + 1; j < n; j++) sum += (m[i][j] * x[j]) % p; if (sum < 0) sum += p; x[i] = (d[i] * (b[i] - sum)) % p;

} }

if (x[i] < 0) x[i] += p;

void solve(long p, long t, long *a, long *x, long *y) { long i, j, k, q, z; long **A = create_square_matrix(t); for (i = 0; i < t; i++) { z = x[i]; for (j = 0; j < t; j++) { q = 1; for (k = 1; k <= j; k++) q = (q * z) % p; A[i][j] = q; } } printf("the matrix is:\n"); for (i = 0; i < t; i++) { for (j = 0; j < t; j++) printf("%5ld ", A[i][j]); printf("\n"); } gaussian_elimination(t, p, y, a, A); delete_square_matrix(t, A);

int main(void) { long i, p = 17, long a[3]; long x[3] = {1, long y[3] = {8, long z[3] = {8,

s, t = 3; 3, 5}; 10, 11}; 10, 11};

printf("====\n"); printf("x y\n"); printf("====\n"); for (i = 0; i < 3; i++) printf("%ld %2ld\n", x[i], y[i]); printf("====\n"); printf("key = %ld\n", Lagrange(p, t, x, y)); solve(p, t, a, x, y); for (i = 0; i < t; i++) printf("a[%ld] = %ld\n", i, a[i]); for (i = 0; i < t; i++) { s = share(p, t, x[i], a); if (s != z[i]) { printf("*error*\nin calculation of share %ld\n", i + 1); printf("%ld %ld\n", s, y[i]); } } return 0;

Exercise

11.1
/* Author:

ex1101.c

(6,774)

Pate Williams (c) 1997

Exercise 11.1 "Write a computer program to compute the key for a Shamir (t, w)-threshold scheme implemented in Z_p. That is, given t public x-coordinates, x_1, x_2,..., x_t, and t y-coordinates y_1, y_2,..., y_t, compute the resulting key. Use the Lagrange interpolation method, as it is easier to program. (a) Test your program if p = 31847, t = 5, and w = 10, with the following shares: x_1 = 413 y_1 = 25439 x_2 = 432 y_2 = 14847 x_3 = 451 y_3 = 24780 x_4 = 470 y_4 = 5910 x_5 = 489 y_5 = 12734 x_6 = 508 y_1 = 12492 x_7 = 527 y_2 = 12555 x_8 = 546 y_3 = 28578 x_9 = 565 y_4 = 20806 x_10 = 584 y_5 = 21462 Verify that the same key is computed using several different subsets of the five shares. (b) Having determined the key, compute the share that would be given to a participant with x-coordinate 10000." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson pages 359-360. */ #include <stdio.h> #include <stdlib.h> #define SIZE 256 long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n); t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; }

if (b0 != 1) return 0; else return t % n; } long Lagrange(long p, long t, long *x, long *y) /* computes the key using the Lagrange interpolation formula, returns the key */ { long bj, i, j, k, s = 0; for (j = 0; j < t; j++) { bj = 1; for (k = 0; k < t; k++) { if (j != k) { i = (x[k] - x[j]) % p; if (i < 0) i += p; bj = (((bj * x[k]) % p) * Extended_Euclidean(i, p)) % p; } } s = (s + (bj * y[j]) % p) % p; } if (s < 0) s += p; return s;

long share(long p, long t, long x, long *a) { long j, s; s = a[t - 1]; for (j = t - 2; j >= 0; j--) s = ((s * x) % p + a[j]) % p; if (s < 0) s += p; return s; } long **create_square_matrix(long n) { long i, **matrix = calloc(n, sizeof(long *)); if (!matrix) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from create_matrix\n"); exit(1); } for (i = 0; i < n; i++) { matrix[i] = calloc(n, sizeof(long)); if (!matrix[i]) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from create_matrix\n"); exit(1); } } return matrix; } void delete_square_matrix(long n, long **matrix)

long i; for (i = 0; i < n; i++) free(matrix[i]); free(matrix);

void Euclid_extended(long a, long b, long *u, long *v, long *d) { long q, t1, t3, v1, v3; *u = 1, *d = a; if (b == 0) { *v = 0; return; } v1 = 0, v3 = b; while (v3 != 0) { q = *d / v3; t3 = *d - q * v3; t1 = *u - q * v1; *u = v1, *d = v3; v1 = t1, v3 = t3; } *v = (*d - a * *u) / b; } long inv(long number, long modulus) { long d, u, v; Euclid_extended(number, modulus, &u, &v, &d); if (d == 1) return u; return 0;

void gaussian_elimination(long n, long p, long *b, long *x, long **m) { int found; long *d = calloc(n, sizeof(long)), ck, dj; long i, j, k, l, sum, t; if (!d) { fprintf(stderr, "fatal error\ninsufficient memory\n"); fprintf(stderr, "from gaussian_elimination\n"); exit(1); } for (j = 0; j < n; j++) { found = 0, i = j; while (!found && i < n) { found = m[i][j] != 0; if (!found) i++; } if (!found) {

fprintf(stderr, fprintf(stderr, for (k = 0; k < for (l = 0; l printf("%ld printf("\n"); } exit(1);

"fatal error\nnon-invertible matrix\n"); "from gaussian_elimination\n"); n; k++) { < n; l++) ", m[k][l]);

} for (i = n - 1; i >= 0; i--) { sum = 0; for (j = i + 1; j < n; j++) sum += (m[i][j] * x[j]) % p; if (sum < 0) sum += p; x[i] = (d[i] * (b[i] - sum)) % p; if (x[i] < 0) x[i] += p; } }

} if (i > j) { /* swap elements */ for (l = j; l < n; l++) t = m[i][l], m[i][l] = m[j][l], m[j][l] = t; t = b[i], b[i] = b[j], b[j] = t; } dj = d[j] = inv(m[j][j], p); if (dj == 0) { fprintf(stderr, "fatal error\nnon-invertlible element\n"); fprintf(stderr, "from gaussian elimination"); fprintf(stderr, "element %ld mod %ld\n", m[j][j], p); exit(1); } for (k = j + 1; k < n; k++) { ck = (dj * m[k][j]) % p; for (l = j + 1; l < n; l++) { m[k][l] = (m[k][l] - ck * m[j][l]) % p; if (m[k][l] < 0) m[k][l] += p; } b[k] = (b[k] - ck * b[j]) % p; if (b[k] < 0) b[k] += p; }

void solve(long p, long t, long *a, long *x, long *y) { long i, j, k, q, z; long **A = create_square_matrix(t); for (i = 0; i < t; i++) { z = x[i]; for (j = 0; j < t; j++) { q = 1; for (k = 1; k <= j; k++) q = (q * z) % p; A[i][j] = q; } } printf("the matrix is:\n"); for (i = 0; i < t; i++) {

} gaussian_elimination(t, p, y, a, A); delete_square_matrix(t, A); } int main(void) { long i, p = 31847, r = 10000, t = long a[5], u[5], v[5]; long x[10] = {413, 432, 451, 470, 508, 527, 546, 565, long y[5] = {25439, 14847, 24780, long z[5] = {12492, 12555, 28578,

for (j = 0; j < t; j++) printf("%5ld ", A[i][j]); printf("\n");

5; 489, 584}; 5910, 12734}; 20806, 21462};

for (i = 0; i < 10; i++) { printf("%ld ", x[i]); if (i < t) printf("%5ld\n", y[i]); else printf("%5ld\n", z[i - t]); } u[0] = x[0], v[0] = y[0]; u[1] = x[1], v[1] = y[1]; u[2] = x[4], v[2] = y[4]; u[3] = x[5], v[3] = z[0]; u[4] = x[6], v[4] = z[1]; printf("key = %ld\n", Lagrange(p, t, x, y)); printf("key = %ld\n", Lagrange(p, t, x + 5, z)); printf("key = %ld\n", Lagrange(p, t, u, v)); for (i = 0; i < t; i++) v[i] = y[i]; solve(p, t, a, x, v); printf("the polynomial coefficients are:\n"); for (i = 0; i < t; i++) printf("a[%ld] = %ld\n", i, a[i]); for (i = 0; i < t; i++) printf("%ld %5ld %5ld\n", x[i], y[i], share(p, t, x[i], a)); x[4] = r, y[4] = share(p, t, r, a); printf("x = %ld y = %ld\n", x[4], y[4]); printf("key = %ld\n", Lagrange(p, t, x, y)); return 0;

Chapter 12 12.2 12.3 12.6

Pseudo-random Number Generation (1,056) (1,772) (3,383)

ex1202.c ex1203.c ex1206.c

Exercises

12.2
/* Author:

ex1202.c

(1,056)

Pate Williams (c) 1997

Exercise 12.2 "Suppose we have an RSA Generator with n = 36863, b = 229 and seed s_0 = 25. Compute the first 100 bits produced by this generator." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 385. */ #include <stdio.h> long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

int main(void) { long b = 229, n = 36863l, s_0 = 25; long c_0 = 0, c_1 = 0, i, s, t; printf("b = %ld\n", b); printf("n = %ld\n", n); printf("s_0 = %ld\n", s_0); printf("the first 100 bits are:\n"); for (i = 0; i < 100; i++) { s = exp_mod(s_0, b, n); s_0 = s; t = s & 1; if (!t) c_0++; else c_1++; printf("%ld", t); if ((i + 1) % 25 == 0) printf("\n"); } printf("count of 0's = %ld\n", c_0); printf("count of 1's = %ld\n", c_1); return 0;

12.3

ex1203.c

(1,772)

/*

Author:

Pate Williams (c) 1997

*/

Exercise 12.3 "A PRBG based on the Discrete Logarithm problem is given in Figure 12.11. Suppose p = 21383, the primitive element alpha = 5 and the seed s_0 = 15886. Compute the first 100 bits produced by this generator." -Douglas R. StinsonSee "Cryptography: Theory and Practice" by Douglas R. Stinson page 385.

#include <stdio.h> long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a; } int main(void) { long alpha = 5, p = 21383, x_0 = 15886; long c_0 = 0, c_1 = 0, i, p2 = p / 2, x, z; printf("alpha = %ld\n", alpha); printf("p = %ld\n", p); printf("x_0 = %ld\n", x_0); printf("the first 100 bits are:\n"); for (i = 0; i < 100; i++) { x = exp_mod(alpha, x_0, p); x_0 = x; z = (x > p2) ? 1 : 0; if (!z) c_0++; else c_1++; printf("%ld", z); if ((i + 1) % 25 == 0) printf("\n"); } printf("count of 0's = %ld\n", c_0); printf("count of 1's = %ld\n", c_1); return 0; }

12.6
/* Author:

ex1206.c

(3,383)

Pate Williams (c) 1997

Exercise 12.6

"Suppose Bob receives some ciphertext which was encrypted with the Blum-Goldwasser Probabilistic Public-key Cryptosystem. The original plaintext consisted of English text. Each alphabetic character was converted to a bitstring of length five in an obvious way: A -> 00000, B -> 00001, ..., Z -> 11001. The plaintext consisted of 236 alphabetic characters, so a bitstring of length 1180 resulted. This bitstring was then encrypted. The resulting ciphertext was then converted to a hexadecimal representation, to save space. The final string of 295 hexadecimal characters is presented in Table 12.4. Also, s_1181 = 20291 is part of the ciphertext, and n = 29893 is Bob's public key. Bob's secret factorization of n is n = pq, where p = 167 and q = 179. Your task is to decrypt the given ciphertext and restore the original plaintext." -Douglas R. Stinson*/ #include <stdio.h> #define BITS_PER_LONG 32 #define BITS_PER_LONG_1 31 #define SIZE 256 long exp_mod(long x, long b, long n) /* returns x ^ b mod n */ { long a = 1l, s = x; while (b != 0) { if (b & 1l) a = (a * s) % n; b >>= 1; if (b != 0) s = (s * s) % n; } return a;

long get_bit(long i, long *sieve) { long b = i % BITS_PER_LONG; long c = i / BITS_PER_LONG; } return (sieve[c] >> (BITS_PER_LONG_1 - b)) & 1;

long Extended_Euclidean(long b, long n) { long b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r; q = n0 / b0; r = n0 - q * b0; while (r > 0) { temp = t0 - q * t; if (temp >= 0) temp = temp % n; else temp = n - (- temp % n);

t0 = t; t = temp; n0 = b0; b0 = r; q = n0 / b0; r = n0 - q * b0; } if (b0 != 1) return 0; else return t % n;

long Chinese_Remainder(long r, long *a, long *m) { long i, N = 1, M[SIZE], y[SIZE], x = 0; for (i for (i M[i] y[i] x += } return } int main(void) { long ciphertext[18] = {0xe1866663, 0xf17fdbd1, 0xdc8c8fd2, 0xeebc36ad, 0x7f53795d, 0xba3c9ce2, 0x2dc9a9c7, 0xe2a56455, 0x501399ca, 0x6b98aed2, 0x2c346a52, 0x9a09c193, 0x6c61ecde, 0x10b43d22, 0x6ec683a6, 0x69929f2f, 0xfb912bfa, 0x96a83021}; long p = 167, q = 179, l1 = 1181, s1 = 20291; long a1, a2, b1, b2, s0; long n = p * q, p1 = p - 1, q1 = q - 1; long a[2], m[2], r = 2, bits = 18 * BITS_PER_LONG; long bit[5], i = 0, j, k, z; a1 = exp_mod((p + 1) / 4, l1, p1); a2 = exp_mod((q + 1) / 4, l1, q1); b1 = exp_mod(s1, a1, p); b2 = exp_mod(s1, a2, q); a[0] = b1; a[1] = b2; m[0] = p; m[1] = q; s0 = Chinese_Remainder(r, a, m); for (j = 0; j < bits / 5; j++) { for (k = 0; k < 5; k++) { s1 = (s0 * s0) % n; s0 = s1; z = s1 & 1; bit[k] = z ^ get_bit(i++, ciphertext); } = 0; i < r; i++) N *= m[i]; = 0; i < r; i++) { = N / m[i]; = Extended_Euclidean(M[i], m[i]); (a[i] * M[i] * y[i]) % N; x;

} return 0;

z = bit[0]; for (k = 1; k < 5; k++) z = z * 2 + bit[k]; printf("%c", z + 'A'); if ((j + 1) % 20 == 0) printf("\n");

Вам также может понравиться