|
| 1 | +/** |
| 2 | + * @file |
| 3 | + * @brief An [affine cipher](https://en.wikipedia.org/wiki/Affine_cipher) is a |
| 4 | + * letter substitution cipher that uses a linear transformation to substitute |
| 5 | + * letters in a message. |
| 6 | + * @details Given an alphabet of length M with characters with numeric values |
| 7 | + * 0-(M-1), an arbitrary character x can be transformed with the expression (ax |
| 8 | + * + b) % M into our ciphertext character. The only caveat is that a must be |
| 9 | + * relatively prime with M in order for this transformation to be invertible, |
| 10 | + * i.e., gcd(a, M) = 1. |
| 11 | + * @author [Daniel Murrow](https://github.com/dsmurrow) |
| 12 | + */ |
| 13 | + |
| 14 | +#include <assert.h> /// for assertions |
| 15 | +#include <stdio.h> /// for IO |
| 16 | +#include <stdlib.h> /// for div function and div_t struct as well as malloc and free |
| 17 | +#include <string.h> /// for strlen, strcpy, and strcmp |
| 18 | + |
| 19 | +/** |
| 20 | + * @brief number of characters in our alphabet (printable ASCII characters) |
| 21 | + */ |
| 22 | +#define ALPHABET_SIZE 95 |
| 23 | + |
| 24 | +/** |
| 25 | + * @brief used to convert a printable byte (32 to 126) to an element of the |
| 26 | + * group Z_95 (0 to 94) |
| 27 | + */ |
| 28 | +#define Z95_CONVERSION_CONSTANT 32 |
| 29 | + |
| 30 | +/** |
| 31 | + * @brief a structure representing an affine cipher key |
| 32 | + */ |
| 33 | +typedef struct |
| 34 | +{ |
| 35 | + int a; ///< what the character is being multiplied by |
| 36 | + int b; ///< what is being added after the multiplication with `a` |
| 37 | +} affine_key_t; |
| 38 | + |
| 39 | +/** |
| 40 | + * @brief finds the value x such that (a * x) % m = 1 |
| 41 | + * |
| 42 | + * @param a number we are finding the inverse for |
| 43 | + * @param m the modulus the inversion is based on |
| 44 | + * |
| 45 | + * @returns the modular multiplicative inverse of `a` mod `m` |
| 46 | + */ |
| 47 | +int modular_multiplicative_inverse(unsigned int a, unsigned int m) |
| 48 | +{ |
| 49 | + int x[2] = {1, 0}; |
| 50 | + div_t div_result; |
| 51 | + |
| 52 | + if (m == 0) { |
| 53 | + return 0; |
| 54 | + } |
| 55 | + a %= m; |
| 56 | + if (a == 0) { |
| 57 | + return 0; |
| 58 | + } |
| 59 | + |
| 60 | + div_result.rem = a; |
| 61 | + |
| 62 | + while (div_result.rem > 0) |
| 63 | + { |
| 64 | + div_result = div(m, a); |
| 65 | + |
| 66 | + m = a; |
| 67 | + a = div_result.rem; |
| 68 | + |
| 69 | + // Calculate value of x for this iteration |
| 70 | + int next = x[1] - (x[0] * div_result.quot); |
| 71 | + |
| 72 | + x[1] = x[0]; |
| 73 | + x[0] = next; |
| 74 | + } |
| 75 | + |
| 76 | + return x[1]; |
| 77 | +} |
| 78 | + |
| 79 | +/** |
| 80 | + * @brief Given a valid affine cipher key, this function will produce the |
| 81 | + * inverse key. |
| 82 | + * |
| 83 | + * @param key They key to be inverted |
| 84 | + * |
| 85 | + * @returns inverse of key |
| 86 | + */ |
| 87 | +affine_key_t inverse_key(affine_key_t key) |
| 88 | +{ |
| 89 | + affine_key_t inverse; |
| 90 | + |
| 91 | + inverse.a = modular_multiplicative_inverse(key.a, ALPHABET_SIZE); |
| 92 | + |
| 93 | + // Turn negative results positive |
| 94 | + inverse.a += ALPHABET_SIZE; |
| 95 | + inverse.a %= ALPHABET_SIZE; |
| 96 | + |
| 97 | + inverse.b = -(key.b % ALPHABET_SIZE) + ALPHABET_SIZE; |
| 98 | + |
| 99 | + return inverse; |
| 100 | +} |
| 101 | + |
| 102 | +/** |
| 103 | + * @brief Encrypts character string `s` with key |
| 104 | + * |
| 105 | + * @param s string to be encrypted |
| 106 | + * @param key affine key used for encryption |
| 107 | + * |
| 108 | + * @returns void |
| 109 | + */ |
| 110 | +void affine_encrypt(char *s, affine_key_t key) |
| 111 | +{ |
| 112 | + for (int i = 0; s[i] != '\0'; i++) |
| 113 | + { |
| 114 | + int c = (int)s[i] - Z95_CONVERSION_CONSTANT; |
| 115 | + |
| 116 | + c *= key.a; |
| 117 | + c += key.b; |
| 118 | + c %= ALPHABET_SIZE; |
| 119 | + |
| 120 | + s[i] = (char)(c + Z95_CONVERSION_CONSTANT); |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +/** |
| 125 | + * @brief Decrypts an affine ciphertext |
| 126 | + * |
| 127 | + * @param s string to be decrypted |
| 128 | + * @param key Key used when s was encrypted |
| 129 | + * |
| 130 | + * @returns void |
| 131 | + */ |
| 132 | +void affine_decrypt(char *s, affine_key_t key) |
| 133 | +{ |
| 134 | + affine_key_t inverse = inverse_key(key); |
| 135 | + |
| 136 | + for (int i = 0; s[i] != '\0'; i++) |
| 137 | + { |
| 138 | + int c = (int)s[i] - Z95_CONVERSION_CONSTANT; |
| 139 | + |
| 140 | + c += inverse.b; |
| 141 | + c *= inverse.a; |
| 142 | + c %= ALPHABET_SIZE; |
| 143 | + |
| 144 | + s[i] = (char)(c + Z95_CONVERSION_CONSTANT); |
| 145 | + } |
| 146 | +} |
| 147 | + |
| 148 | +/** |
| 149 | + * @brief Tests a given string |
| 150 | + * |
| 151 | + * @param s string to be tested |
| 152 | + * @param a value of key.a |
| 153 | + * @param b value of key.b |
| 154 | + * |
| 155 | + * @returns void |
| 156 | + */ |
| 157 | +void test_string(const char *s, const char *ciphertext, int a, int b) |
| 158 | +{ |
| 159 | + char *copy = malloc((strlen(s) + 1) * sizeof(char)); |
| 160 | + strcpy(copy, s); |
| 161 | + |
| 162 | + affine_key_t key = {a, b}; |
| 163 | + |
| 164 | + affine_encrypt(copy, key); |
| 165 | + assert(strcmp(copy, ciphertext) == 0); // assert that the encryption worked |
| 166 | + |
| 167 | + affine_decrypt(copy, key); |
| 168 | + assert(strcmp(copy, s) == |
| 169 | + 0); // assert that we got the same string we started with |
| 170 | + |
| 171 | + free(copy); |
| 172 | +} |
| 173 | + |
| 174 | +/** |
| 175 | + * @brief Test multiple strings |
| 176 | + * |
| 177 | + * @returns void |
| 178 | + */ |
| 179 | +static void tests() |
| 180 | +{ |
| 181 | + test_string("Hello!", "&3ddy2", 7, 11); |
| 182 | + test_string("TheAlgorithms/C", "DNC}=jHS2zN!7;E", 67, 67); |
| 183 | + test_string("0123456789", "840,($ {ws", 91, 88); |
| 184 | + test_string("7W@;cdeRT9uL", "JDfa*we?z&bL", 77, 76); |
| 185 | + test_string("~Qr%^-+++$leM", "r'qC0$sss;Ahf", 8, 90); |
| 186 | + test_string("The quick brown fox jumps over the lazy dog", |
| 187 | + "K7: .*6<4 =-0(1 90' 5*2/, 0):- +7: 3>%& ;08", 94, 0); |
| 188 | + test_string( |
| 189 | + "One-1, Two-2, Three-3, Four-4, Five-5, Six-6, Seven-7, Eight-8, " |
| 190 | + "Nine-9, Ten-10", |
| 191 | + "H&60>\\2*uY0q\\2*p4660E\\2XYn40x\\2XDB60L\\2VDI0 " |
| 192 | + "\\2V6B6&0S\\2%D=p;0'\\2tD&60Z\\2*6&0>j", |
| 193 | + 51, 18); |
| 194 | + |
| 195 | + printf("All tests have successfully passed!\n"); |
| 196 | +} |
| 197 | + |
| 198 | +/** |
| 199 | + * @brief main function |
| 200 | + * |
| 201 | + * @returns 0 upon successful program exit |
| 202 | + */ |
| 203 | +int main() |
| 204 | +{ |
| 205 | + tests(); |
| 206 | + return 0; |
| 207 | +} |
0 commit comments