Skip to content

Conversation

@AliAlimohammadi
Copy link
Contributor

Description

This PR adds a comprehensive implementation of the Affine Cipher to the ciphers module. The affine cipher is a type of mono-alphabetic substitution cipher that uses modular arithmetic to encrypt and decrypt messages.

Implementation Details

The implementation provides three main functions:

  1. affine_encrypt(key: usize, message: &str) -> Result<String, String>

    • Encrypts a message using the affine cipher
    • Encryption formula: E(x) = (ax + b) mod m
    • Returns encrypted ciphertext or error for invalid keys
    • Validates key strength (key_a ≠ 1, key_b ≠ 0)
  2. affine_decrypt(key: usize, message: &str) -> Result<String, String>

    • Decrypts a message using the affine cipher
    • Decryption formula: D(x) = a^(-1)(x - b) mod m
    • Uses modular multiplicative inverse
    • Returns decrypted plaintext or error for invalid keys
  3. affine_generate_key() -> usize

    • Generates a cryptographically valid random key
    • Ensures key_a is coprime with symbol set size
    • Ensures key_b is not zero
    • Key encoded as: key_a * SYMBOLS.len() + key_b

Helper Functions

  • gcd(a: usize, b: usize) -> usize

    • Calculates greatest common divisor using Euclidean algorithm
    • Used to verify key_a is coprime with symbol set size
  • find_mod_inverse(a: i64, m: i64) -> Option<i64>

    • Finds modular multiplicative inverse using Extended Euclidean Algorithm
    • Required for decryption process
    • Returns None if inverse doesn't exist
  • check_keys(key_a: usize, key_b: usize, is_encrypt: bool) -> Result<(), String>

    • Validates encryption/decryption keys
    • Ensures mathematical requirements are met
    • Prevents weak keys

Algorithm Details

Encryption:

For each character:
1. Find position in symbol set: x
2. Apply formula: (x * key_a + key_b) % m
3. Convert result back to character

Decryption:

For each character:
1. Find position in symbol set: y
2. Find modular inverse of key_a
3. Apply formula: (y - key_b) * inverse(key_a) % m
4. Convert result back to character

Key Requirements:

  • gcd(key_a, m) = 1 (key_a must be coprime with symbol set size)
  • key_a ≠ 1 (prevents weak encryption)
  • key_b ≠ 0 (prevents weak encryption)
  • 0 ≤ key_b < m (must be within valid range)

Time Complexity:

  • Encryption: $O(n)$ where $n$ is message length
  • Decryption: $O(n)$ where $n$ is message length
  • Key validation: $O(log m)$ for GCD calculation
  • Modular inverse: $O(log m)$ using Extended Euclidean Algorithm

Space Complexity: $O(n)$ for output string

Symbol Set

Uses all printable ASCII characters (95 total):

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

Key Features

  1. Type Safety: Uses Result for comprehensive error handling
  2. Key Validation: Prevents weak keys that compromise security
  3. Mathematical Correctness: Implements proper modular arithmetic
  4. Extended Character Set: Supports 95 printable ASCII characters
  5. Random Key Generation: Creates cryptographically valid keys
  6. Comprehensive Tests: 20+ test cases covering all scenarios
  7. Error Messages: Clear, descriptive error messages for debugging

Changes Made

  • ✅ Added src/ciphers/affine_cipher.rs with complete implementation
  • ✅ Will update src/ciphers/mod.rs to include and export the module
  • ✅ Implemented comprehensive unit tests (20+ tests) covering:
    • Basic encryption and decryption
    • Roundtrip encryption/decryption
    • Various message types (letters, numbers, special characters)
    • Empty strings
    • Invalid key detection (key_a = 1, key_b = 0, non-coprime)
    • Characters outside symbol set
    • Random key generation and validation
    • All symbols in character set
    • Different keys producing different outputs
    • Long messages
    • GCD calculation
    • Modular inverse calculation
  • ✅ Added detailed documentation with examples for all public functions
  • ✅ Included doctests demonstrating usage
  • ✅ Module-level documentation explaining the algorithm

Type of Change

  • New cipher implementation
  • Documentation (docstrings and examples)
  • Tests (20+ comprehensive tests)

Testing

All tests pass successfully:

cargo test affine_cipher
cargo test --test ciphers
cargo test --doc
cargo fmt --check
cargo clippy -- -D warnings

Test Coverage

20+ Unit Tests Including:

  • Roundtrip testing with various messages
  • Empty string handling
  • Invalid key detection:
    • key_a = 1 (weak)
    • key_b = 0 (weak)
    • key_a not coprime with symbol set size
  • Characters not in symbol set (preserved unchanged)
  • Random key generation
  • Random key validity verification
  • All symbols can be encrypted/decrypted
  • Different keys produce different ciphertexts
  • Long message handling
  • GCD algorithm correctness
  • Modular inverse calculation
  • Doctests for public API

Examples

use the_algorithms_rust::ciphers::{affine_encrypt, affine_decrypt, affine_generate_key};

// Basic encryption and decryption
let key = 4545;
let message = "The affine cipher is a type of monoalphabetic substitution cipher.";

let encrypted = affine_encrypt(key, message).unwrap();
assert_eq!(encrypted, "VL}p MM{I}p~{HL}Gp{vp pFsH}pxMpyxIx JHL O}F{~pvuOvF{FuF{xIp~{HL}Gi");

let decrypted = affine_decrypt(key, &encrypted).unwrap();
assert_eq!(decrypted, message);

// Simple example
let encrypted = affine_encrypt(4545, "Hello World!").unwrap();
let decrypted = affine_decrypt(4545, &encrypted).unwrap();
assert_eq!(decrypted, "Hello World!");

// Generate random key
let random_key = affine_generate_key();
let encrypted = affine_encrypt(random_key, "Secret message").unwrap();
let decrypted = affine_decrypt(random_key, &encrypted).unwrap();
assert_eq!(decrypted, "Secret message");

// Error handling for weak keys
let symbols_len = 95; // SYMBOLS.len()
let weak_key = 1 * symbols_len + 5; // key_a = 1 (weak)
assert!(affine_encrypt(weak_key, "test").is_err());

let weak_key2 = 5 * symbols_len + 0; // key_b = 0 (weak)
assert!(affine_encrypt(weak_key2, "test").is_err());

// Non-coprime key
let bad_key = 5 * symbols_len + 10; // gcd(5, 95) = 5, not coprime
assert!(affine_encrypt(bad_key, "test").is_err());

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my implementation is correct
  • New and existing unit tests pass locally with my changes
  • I have checked my code with cargo fmt
  • I have checked my code with cargo clippy
  • Module is properly exported in mod.rs

Additional Context

  • Type Safety: Uses Result type instead of sys.exit() for errors
  • No Runtime Panics: Graceful error handling throughout
  • Const Symbol Set: Compile-time constant for better performance
  • Integrated Helpers: GCD and modular inverse built-in (no external imports)
  • Better Error Messages: More descriptive error messages
  • More Tests: 20+ comprehensive tests vs. just doctests
  • Documentation: Full rustdoc with mathematical explanations
  • Memory Efficient: Uses iterators and string builders
  • Thread-Safe Random: Uses rand crate's thread-safe RNG

Security Considerations:

  • Key validation prevents mathematically weak keys
  • Coprimality check ensures reversibility
  • Extended character set provides larger key space
  • Random key generator creates strong keys

Mathematical Correctness:

  • Implements proper modular arithmetic
  • Uses Extended Euclidean Algorithm for modular inverse
  • Handles both positive and negative numbers correctly
  • Validates all mathematical requirements

Use Cases:

  • Educational purposes (learning cryptography)
  • Historical cipher implementation
  • Demonstrating modular arithmetic
  • Introduction to substitution ciphers

Note on Security:
The affine cipher is not secure for modern cryptographic use. It's included for:

  • Educational purposes
  • Understanding classical cryptography
  • Demonstrating mathematical concepts
  • Historical interest

For actual security needs, use modern cryptographic algorithms.

References

@codecov-commenter
Copy link

codecov-commenter commented Jan 7, 2026

Codecov Report

❌ Patch coverage is 99.13793% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.87%. Comparing base (6937f91) to head (d22add2).

Files with missing lines Patch % Lines
src/ciphers/affine_cipher.rs 99.13% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #989      +/-   ##
==========================================
+ Coverage   95.80%   95.87%   +0.07%     
==========================================
  Files         357      358       +1     
  Lines       23965    24197     +232     
==========================================
+ Hits        22960    23200     +240     
+ Misses       1005      997       -8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@AliAlimohammadi
Copy link
Contributor Author

@siriak, this is ready to be merged.

@siriak siriak merged commit c3e6909 into TheAlgorithms:master Jan 7, 2026
7 checks passed
@AliAlimohammadi AliAlimohammadi deleted the add-affine-cipher branch January 7, 2026 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants