Skip to content

Commit b5b2218

Browse files
dsmurrowCascadingCascadePanquesito7
authored
feat: added Affine Cipher (#1245)
* feat: added affine cipher * chore: applied clang-format * docs: fixed typo Co-authored-by: Sharon "Cass" Cassidy <[email protected]> * docs: added brief qualifier Co-authored-by: Sharon "Cass" Cassidy <[email protected]> * chore: added const qualifier to test_string input * test: added checks for correct ciphertext * chore: removed asserts in modular_multiplicative_inverse and defined the ASCII conversion character * removed previous_remainder variable in modular_multiplicative_inverse() * chore: added brackets Co-authored-by: David Leal <[email protected]> * chore: made test function static Co-authored-by: David Leal <[email protected]> * docs: added back quotes to variable `a` Co-authored-by: David Leal <[email protected]> * docs: added back quotes to more variables Co-authored-by: David Leal <[email protected]> * chore: Added capitalization to string indicating passing tests Co-authored-by: David Leal <[email protected]> * chore: apply suggestions from code review --------- Co-authored-by: Sharon "Cass" Cassidy <[email protected]> Co-authored-by: David Leal <[email protected]>
1 parent 0a5f5c6 commit b5b2218

File tree

1 file changed

+207
-0
lines changed

1 file changed

+207
-0
lines changed

cipher/affine.c

+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
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

Comments
 (0)