mirror of
https://github.com/TheAlgorithms/C
synced 2024-12-24 20:16:57 +03:00
feat: added Affine Cipher (#1245)
* feat: added affine cipher * chore: applied clang-format * docs: fixed typo Co-authored-by: Sharon "Cass" Cassidy <monadicdiffusive@proton.me> * docs: added brief qualifier Co-authored-by: Sharon "Cass" Cassidy <monadicdiffusive@proton.me> * 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 <halfpacho@gmail.com> * chore: made test function static Co-authored-by: David Leal <halfpacho@gmail.com> * docs: added back quotes to variable `a` Co-authored-by: David Leal <halfpacho@gmail.com> * docs: added back quotes to more variables Co-authored-by: David Leal <halfpacho@gmail.com> * chore: Added capitalization to string indicating passing tests Co-authored-by: David Leal <halfpacho@gmail.com> * chore: apply suggestions from code review --------- Co-authored-by: Sharon "Cass" Cassidy <monadicdiffusive@proton.me> Co-authored-by: David Leal <halfpacho@gmail.com>
This commit is contained in:
parent
0a5f5c61f7
commit
b5b2218e60
207
cipher/affine.c
Normal file
207
cipher/affine.c
Normal file
@ -0,0 +1,207 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief An [affine cipher](https://en.wikipedia.org/wiki/Affine_cipher) is a
|
||||
* letter substitution cipher that uses a linear transformation to substitute
|
||||
* letters in a message.
|
||||
* @details Given an alphabet of length M with characters with numeric values
|
||||
* 0-(M-1), an arbitrary character x can be transformed with the expression (ax
|
||||
* + b) % M into our ciphertext character. The only caveat is that a must be
|
||||
* relatively prime with M in order for this transformation to be invertible,
|
||||
* i.e., gcd(a, M) = 1.
|
||||
* @author [Daniel Murrow](https://github.com/dsmurrow)
|
||||
*/
|
||||
|
||||
#include <assert.h> /// for assertions
|
||||
#include <stdio.h> /// for IO
|
||||
#include <stdlib.h> /// for div function and div_t struct as well as malloc and free
|
||||
#include <string.h> /// for strlen, strcpy, and strcmp
|
||||
|
||||
/**
|
||||
* @brief number of characters in our alphabet (printable ASCII characters)
|
||||
*/
|
||||
#define ALPHABET_SIZE 95
|
||||
|
||||
/**
|
||||
* @brief used to convert a printable byte (32 to 126) to an element of the
|
||||
* group Z_95 (0 to 94)
|
||||
*/
|
||||
#define Z95_CONVERSION_CONSTANT 32
|
||||
|
||||
/**
|
||||
* @brief a structure representing an affine cipher key
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int a; ///< what the character is being multiplied by
|
||||
int b; ///< what is being added after the multiplication with `a`
|
||||
} affine_key_t;
|
||||
|
||||
/**
|
||||
* @brief finds the value x such that (a * x) % m = 1
|
||||
*
|
||||
* @param a number we are finding the inverse for
|
||||
* @param m the modulus the inversion is based on
|
||||
*
|
||||
* @returns the modular multiplicative inverse of `a` mod `m`
|
||||
*/
|
||||
int modular_multiplicative_inverse(unsigned int a, unsigned int m)
|
||||
{
|
||||
int x[2] = {1, 0};
|
||||
div_t div_result;
|
||||
|
||||
if (m == 0) {
|
||||
return 0;
|
||||
}
|
||||
a %= m;
|
||||
if (a == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
div_result.rem = a;
|
||||
|
||||
while (div_result.rem > 0)
|
||||
{
|
||||
div_result = div(m, a);
|
||||
|
||||
m = a;
|
||||
a = div_result.rem;
|
||||
|
||||
// Calculate value of x for this iteration
|
||||
int next = x[1] - (x[0] * div_result.quot);
|
||||
|
||||
x[1] = x[0];
|
||||
x[0] = next;
|
||||
}
|
||||
|
||||
return x[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Given a valid affine cipher key, this function will produce the
|
||||
* inverse key.
|
||||
*
|
||||
* @param key They key to be inverted
|
||||
*
|
||||
* @returns inverse of key
|
||||
*/
|
||||
affine_key_t inverse_key(affine_key_t key)
|
||||
{
|
||||
affine_key_t inverse;
|
||||
|
||||
inverse.a = modular_multiplicative_inverse(key.a, ALPHABET_SIZE);
|
||||
|
||||
// Turn negative results positive
|
||||
inverse.a += ALPHABET_SIZE;
|
||||
inverse.a %= ALPHABET_SIZE;
|
||||
|
||||
inverse.b = -(key.b % ALPHABET_SIZE) + ALPHABET_SIZE;
|
||||
|
||||
return inverse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encrypts character string `s` with key
|
||||
*
|
||||
* @param s string to be encrypted
|
||||
* @param key affine key used for encryption
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
void affine_encrypt(char *s, affine_key_t key)
|
||||
{
|
||||
for (int i = 0; s[i] != '\0'; i++)
|
||||
{
|
||||
int c = (int)s[i] - Z95_CONVERSION_CONSTANT;
|
||||
|
||||
c *= key.a;
|
||||
c += key.b;
|
||||
c %= ALPHABET_SIZE;
|
||||
|
||||
s[i] = (char)(c + Z95_CONVERSION_CONSTANT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decrypts an affine ciphertext
|
||||
*
|
||||
* @param s string to be decrypted
|
||||
* @param key Key used when s was encrypted
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
void affine_decrypt(char *s, affine_key_t key)
|
||||
{
|
||||
affine_key_t inverse = inverse_key(key);
|
||||
|
||||
for (int i = 0; s[i] != '\0'; i++)
|
||||
{
|
||||
int c = (int)s[i] - Z95_CONVERSION_CONSTANT;
|
||||
|
||||
c += inverse.b;
|
||||
c *= inverse.a;
|
||||
c %= ALPHABET_SIZE;
|
||||
|
||||
s[i] = (char)(c + Z95_CONVERSION_CONSTANT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tests a given string
|
||||
*
|
||||
* @param s string to be tested
|
||||
* @param a value of key.a
|
||||
* @param b value of key.b
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
void test_string(const char *s, const char *ciphertext, int a, int b)
|
||||
{
|
||||
char *copy = malloc((strlen(s) + 1) * sizeof(char));
|
||||
strcpy(copy, s);
|
||||
|
||||
affine_key_t key = {a, b};
|
||||
|
||||
affine_encrypt(copy, key);
|
||||
assert(strcmp(copy, ciphertext) == 0); // assert that the encryption worked
|
||||
|
||||
affine_decrypt(copy, key);
|
||||
assert(strcmp(copy, s) ==
|
||||
0); // assert that we got the same string we started with
|
||||
|
||||
free(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test multiple strings
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
static void tests()
|
||||
{
|
||||
test_string("Hello!", "&3ddy2", 7, 11);
|
||||
test_string("TheAlgorithms/C", "DNC}=jHS2zN!7;E", 67, 67);
|
||||
test_string("0123456789", "840,($ {ws", 91, 88);
|
||||
test_string("7W@;cdeRT9uL", "JDfa*we?z&bL", 77, 76);
|
||||
test_string("~Qr%^-+++$leM", "r'qC0$sss;Ahf", 8, 90);
|
||||
test_string("The quick brown fox jumps over the lazy dog",
|
||||
"K7: .*6<4 =-0(1 90' 5*2/, 0):- +7: 3>%& ;08", 94, 0);
|
||||
test_string(
|
||||
"One-1, Two-2, Three-3, Four-4, Five-5, Six-6, Seven-7, Eight-8, "
|
||||
"Nine-9, Ten-10",
|
||||
"H&60>\\2*uY0q\\2*p4660E\\2XYn40x\\2XDB60L\\2VDI0 "
|
||||
"\\2V6B6&0S\\2%D=p;0'\\2tD&60Z\\2*6&0>j",
|
||||
51, 18);
|
||||
|
||||
printf("All tests have successfully passed!\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief main function
|
||||
*
|
||||
* @returns 0 upon successful program exit
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
tests();
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user