/* 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. Stinson- See "Cryptography: Theory and Practice" by Douglas R. Stinson page 42. */ #include #include #include 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; }