191 lines
5.9 KiB
Fortran
191 lines
5.9 KiB
Fortran
!
|
|
!This program implements the **Chaocipher encryption algorithm** in FORTRAN, extended to handle all printable ASCII characters (32-127).
|
|
! The Chaocipher is a polyalphabetic cipher invented by John F. Byrne in 1918, which dynamically permutes two alphabets after
|
|
! processing each character, making it highly secure and resistant to cryptanalysis.
|
|
!
|
|
!#### Key Features:
|
|
!1. **Initialization**:
|
|
! - Two alphabets (`left` and `right`) are initialized with shuffled ASCII characters (32-127).
|
|
! - The `plaintext` is set to "WELLDONEISBETTERTHANWELLSAID".
|
|
!
|
|
!2. **Enciphering**:
|
|
! - For each character in the plaintext:
|
|
! - Locate the character in the `right` alphabet.
|
|
! - Find the corresponding ciphertext character in the `left` alphabet.
|
|
! - Permute both alphabets based on the Chaocipher rules:
|
|
! - The `left` alphabet is rotated to bring the ciphertext character to the top, and a specific character is moved to a new position.
|
|
! - The `right` alphabet is similarly rotated, with an additional rotation step.
|
|
!
|
|
!3. **Deciphering**:
|
|
! - The ciphertext is decrypted back into plaintext using the reverse process:
|
|
! - Locate the ciphertext character in the `left` alphabet.
|
|
! - Find the corresponding plaintext character in the `right` alphabet.
|
|
! - Permute both alphabets identically as in enciphering.
|
|
!
|
|
!4. **Verification**:
|
|
! - After enciphering and deciphering, the program compares the original plaintext with the decrypted text to ensure correctness.
|
|
!
|
|
!#### Output:
|
|
!The program prints:
|
|
!- Initial left and right alphabets.
|
|
!- Enciphered ciphertext.
|
|
!- Deciphered plaintext.
|
|
!- A success message if decryption matches the original plaintext.
|
|
!
|
|
!#### Example Execution:
|
|
!For input plaintext "WELLDONEISBETTERTHANWELLSAID", the program outputs:
|
|
!- Ciphertext: `OAHQHCNYNXTSZJRRHJBYHQKSOUJY`
|
|
!- Decrypted Text: `WELLDONEISBETTERTHANWELLSAID`
|
|
!- Verification: "Decryption successful: plaintext matches decrypted text."
|
|
!
|
|
!This implementation demonstrates both encryption and decryption processes while adhering to Chaocipher's dynamic permutation rules,
|
|
!extended for modern ASCII characters.
|
|
!
|
|
!
|
|
PROGRAM Chaocipher
|
|
IMPLICIT NONE
|
|
CHARACTER(LEN=96) :: left, right, left_orig, right_orig
|
|
CHARACTER(LEN=1000) :: plaintext, ciphertext, decrypted
|
|
INTEGER :: i, len, ascii_start
|
|
LOGICAL :: trace
|
|
|
|
! Initialize alphabets and input
|
|
CALL initialize_alphabets(left, right)
|
|
left_orig = left
|
|
right_orig = right
|
|
plaintext = 'Well, when I was in Egypt, I had a conversation with the Sphinx! She taught me how to sew.'
|
|
ciphertext = ''
|
|
decrypted = ''
|
|
trace = .FALSE.
|
|
ascii_start = 32 ! ASCII start at space character
|
|
|
|
len = LEN_TRIM(plaintext)
|
|
|
|
PRINT *, 'Initial Left: ', left
|
|
PRINT *, 'Initial Right:', right
|
|
PRINT *, 'Plaintext: ', TRIM(plaintext)
|
|
|
|
! Encipher
|
|
DO i = 1, len
|
|
CALL encipher(plaintext(i:i), ciphertext(i:i), left, right, ascii_start)
|
|
IF (trace) THEN
|
|
PRINT *, 'Step ', i
|
|
PRINT *, 'Left: ', left
|
|
PRINT *, 'Right: ', right
|
|
END IF
|
|
END DO
|
|
|
|
PRINT *, 'Ciphertext: ', TRIM(ciphertext)
|
|
|
|
! Reset alphabets for deciphering
|
|
left = left_orig
|
|
right = right_orig
|
|
|
|
! Decipher
|
|
DO i = 1, len
|
|
CALL decipher(ciphertext(i:i), decrypted(i:i), left, right, ascii_start)
|
|
END DO
|
|
|
|
PRINT *, 'Decrypted: ', TRIM(decrypted)
|
|
|
|
! Check for correctness
|
|
IF (plaintext == decrypted) THEN
|
|
PRINT *, 'Decryption successful: plaintext matches decrypted text'
|
|
ELSE
|
|
PRINT *, 'Decryption failed: plaintext does not match decrypted text'
|
|
END IF
|
|
|
|
CONTAINS
|
|
|
|
SUBROUTINE initialize_alphabets(left, right)
|
|
CHARACTER(LEN=96), INTENT(OUT) :: left, right
|
|
INTEGER :: i, j
|
|
CHARACTER :: temp
|
|
REAL :: r
|
|
|
|
! Initialize alphabets with ASCII characters 32-127
|
|
DO i = 1, 96
|
|
left(i:i) = CHAR(i + 31)
|
|
right(i:i) = CHAR(i + 31)
|
|
END DO
|
|
|
|
! Fisher-Yates shuffle for left alphabet
|
|
DO i = 96, 2, -1
|
|
CALL RANDOM_NUMBER(r)
|
|
j = 1 + FLOOR(i * r)
|
|
temp = left(i:i)
|
|
left(i:i) = left(j:j)
|
|
left(j:j) = temp
|
|
END DO
|
|
|
|
! Fisher-Yates shuffle for right alphabet
|
|
DO i = 96, 2, -1
|
|
CALL RANDOM_NUMBER(r)
|
|
j = 1 + FLOOR(i * r)
|
|
temp = right(i:i)
|
|
right(i:i) = right(j:j)
|
|
right(j:j) = temp
|
|
END DO
|
|
END SUBROUTINE initialize_alphabets
|
|
|
|
SUBROUTINE encipher(p, c, left, right, ascii_start)
|
|
CHARACTER(LEN=1), INTENT(IN) :: p
|
|
CHARACTER(LEN=1), INTENT(OUT) :: c
|
|
CHARACTER(LEN=96), INTENT(INOUT) :: left, right
|
|
INTEGER, INTENT(IN) :: ascii_start
|
|
INTEGER :: pos
|
|
|
|
pos = INDEX(right, p)
|
|
c = left(pos:pos)
|
|
|
|
CALL permute_left(left, c, ascii_start)
|
|
CALL permute_right(right, p, ascii_start)
|
|
END SUBROUTINE encipher
|
|
|
|
SUBROUTINE decipher(c, p, left, right, ascii_start)
|
|
CHARACTER(LEN=1), INTENT(IN) :: c
|
|
CHARACTER(LEN=1), INTENT(OUT) :: p
|
|
CHARACTER(LEN=96), INTENT(INOUT) :: left, right
|
|
INTEGER, INTENT(IN) :: ascii_start
|
|
INTEGER :: pos
|
|
|
|
pos = INDEX(left, c)
|
|
p = right(pos:pos)
|
|
|
|
CALL permute_left(left, c, ascii_start)
|
|
CALL permute_right(right, p, ascii_start)
|
|
END SUBROUTINE decipher
|
|
|
|
SUBROUTINE permute_left(alphabet, pivot, ascii_start)
|
|
CHARACTER(LEN=96), INTENT(INOUT) :: alphabet
|
|
CHARACTER(LEN=1), INTENT(IN) :: pivot
|
|
INTEGER, INTENT(IN) :: ascii_start
|
|
INTEGER :: pos
|
|
CHARACTER(LEN=1) :: temp
|
|
|
|
pos = INDEX(alphabet, pivot)
|
|
alphabet = alphabet(pos:) // alphabet(:pos-1)
|
|
|
|
temp = alphabet(2:2)
|
|
alphabet(2:49) = alphabet(3:50)
|
|
alphabet(50:50) = temp
|
|
END SUBROUTINE permute_left
|
|
|
|
SUBROUTINE permute_right(alphabet, pivot, ascii_start)
|
|
CHARACTER(LEN=96), INTENT(INOUT) :: alphabet
|
|
CHARACTER(LEN=1), INTENT(IN) :: pivot
|
|
INTEGER, INTENT(IN) :: ascii_start
|
|
INTEGER :: pos
|
|
CHARACTER(LEN=1) :: temp
|
|
|
|
pos = INDEX(alphabet, pivot)
|
|
alphabet = alphabet(pos:) // alphabet(:pos-1)
|
|
alphabet = alphabet(2:) // alphabet(1:1)
|
|
|
|
temp = alphabet(3:3)
|
|
alphabet(3:49) = alphabet(4:50)
|
|
alphabet(50:50) = temp
|
|
END SUBROUTINE permute_right
|
|
|
|
END PROGRAM Chaocipher
|