Welcome to Code Forum!

Join a community that supports you and your coding journey from day one. We strive to be a friendly, supportive community that empowers everyone to be better developers. By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!
  • Guest, before posting your code please take these rules into consideration:
    • It is required to use our BBCode feature to display your code. While within the editor click < / > or >_ and place your code within the BB Code prompt. This helps others with finding a solution by making it easier to read and easier to copy.
    • You can also use markdown to share your code. When using markdown your code will be automatically converted to BBCode. For help with markdown check out the markdown guide.
    • Don't share a wall of code. All we want is the problem area, the code related to your issue.

    GIF shows where to locate </> in the thread and or post editor toolbar.
    To learn more about how to use our BBCode feature, review our "How to post your code into threads" here.

    Thank you, Code Forum.

Python Enigma - Decode

I have an enigma machine that works for encoding; however, I'm lost on the decoding part. This was originally a C++ app that I have been converting to Python.

Code:
from enigma_common import encode_rotor_10, encode_rotor_26
from enigma_common import n0, n9, nA, nZ


def encode_checksum_calc(in_string):
    """
    Calculate the checksum for a given input string.

    Args:
    in_string (str): The input string to calculate the checksum for.

    Returns:
    None: The function directly calls another function to continue the process.

    Raises:
    ValueError: If the input string is not of the expected length.
    """

    key_length = len(in_string)

    # in_string is a str converting in_string to bytes in m_key_code
    m_key_code = str.encode(in_string)

    check_sum = 1
    for idx, n in enumerate(m_key_code[2:], 2):
        if n0 <= n <= n9:
            temp_sum = n - n0
        else:
            temp_sum = n - nA
        check_sum += idx + temp_sum + (idx * temp_sum)
    check_sum = ((100 - check_sum) % 100)
    dummy = [n0 + (check_sum % 10), n0 + ((check_sum // 10) % 10)]
    m_key_code = bytes(dummy) + m_key_code[2:]
    print("DEBUG: Calculated Checksum:", check_sum)
    print("DEBUG: Modified Key Code with Checksum:", m_key_code)
    return m_key_code


def encode_option_code_calc(m_key_code):
    """
    Calculate the option code based on the modified key code.

    Args:
    m_key_code (bytes): The modified key code used for option code calculation.

    Returns:
    None: The function calls another function to print the option code.

    Raises:
    TypeError: If the input is not in bytes format.
    """

    # Setting max check sum size to 26000
    max_check_sum = 26000

    # Encipher Key
    option_key = []
    checksum = 0
    for idx, n in enumerate(m_key_code[:]):
        print(f"DEBUG: Original Char: {n}")
        if n0 <= n <= n9:
            temp_sum = (n - n0)
            m_key_code = (encode_rotor_10[(temp_sum + max_check_sum - checksum) % 10] + 0)
        else:
            temp_sum = n - nA
            m_key_code = (encode_rotor_10[(temp_sum + max_check_sum - checksum) % 26] + nA)

        checksum += idx + temp_sum + (idx * temp_sum)

        option_key.append(int(m_key_code))
        print(f"DEBUG: Transformed Char: {m_key_code}, Checksum: {checksum}")
        print(f"DEBUG: Original Option Key: {option_key}")

    return option_key

And here is the decode, which does not currently work.
Code:
# enigma_decode.py

from enigma_common import decode_rotor_10, decode_rotor_26
from enigma_common import n0, n9, nA, nZ


def decode(encoded_option_code):
    # Convert the encoded option code to bytes
    encoded_bytes = bytes(encoded_option_code, 'utf-8')

    # Validate the checksum
    if not decode_checksum_calc(encoded_bytes):
        print("Invalid checksum.")
        return None, None  # Return a tuple of None values

    # Continue with the decoding process...
    decoded_data = decode_with_rotor_10(encoded_bytes)
    original_key = decode_with_rotor_26(decoded_data)

    # Return the decoded data and the original key
    return decoded_data, original_key


def decode_with_rotor_10(encoded_data):
    decoded_data = ''
    checksum = 0
    for idx, char in enumerate(encoded_data):
        str_char = chr(char)  # Convert byte to its character representation
        if str_char.isdigit():
            byte_val = ord(str_char)  # Convert character to its ASCII value
            adjusted_val = (byte_val - n0) % 10  # Reverse the transformation
            decoded_val = decode_rotor_10[adjusted_val]

            # Adjust for the checksum effect (if applicable)
            temp_sum = decoded_val
            checksum += idx + temp_sum + (idx * temp_sum)

            print(f"DEBUG: idx={idx}, char={char}, byte_va{byte_val}, adjusted_val={adjusted_val}, decoded_val={decoded_val}, temp_sum={temp_sum}, checksum={checksum}")

            # Add the decoded value to the decoded data
            decoded_data += str(decoded_val)
        else:
            decoded_data += str_char  # Non-digit characters are left as is
    return decoded_data


def decode_with_rotor_26(encoded_data):
    """
    Calculate the original key code based on the option key for rotor 26.

    Args:
    encoded_data (list): The encoded data used for original key code calculation.

    Returns:
    list: The original key code.

    Raises:
    TypeError: If the input is not in list format.
    """

    # Setting max check sum size to 26000
    max_check_sum = 26000

    # Decipher Key
    original_key = []
    checksum = 0
    for idx, n in enumerate(encoded_data[:]):
        if nA <= n <= nZ:
            temp_sum = n - nA
            original_key_code = (decode_rotor_26[(temp_sum + max_check_sum - checksum) % 26] + nA)
            checksum += idx + temp_sum + (idx * temp_sum)
            original_key.append(int(original_key_code))

    return original_key


def decode_checksum_calc(encoded_string):
    """
    Validate the checksum for a given encoded string.

    Args:
    encoded_string (bytes): The encoded string to validate the checksum for.

    Returns:
    bool: True if the checksum is valid, False otherwise.
    """

    # Extract the checksum from the encoded string
    extracted_checksum = (encoded_string[1] - n0) * 10 + (encoded_string[0] - n0)

    # Calculate the checksum for the remaining string
    check_sum = 1
    for idx, n in enumerate(encoded_string[2:], 2):
        if n0 <= n <= n9:
            temp_sum = n - n0  # Reverse the transformation
        else:
            temp_sum = n - nA
        check_sum += idx + temp_sum + (idx * temp_sum)
        print(f"DEBUG: idx={idx}, n={n}, temp_sum={temp_sum}, check_sum={check_sum}")  # Debug statement

    check_sum = ((100 - check_sum) % 100)

    print("DEBUG: Extracted Checksum:", extracted_checksum)
    print("DEBUG: Calculated Checksum:", check_sum)

    # Return True if the calculated checksum matches the extracted checksum
    return check_sum == extracted_checksum

The Rotors are here.

Code:
"""
This module contains global variables and lists used for encoding and decoding in the Enigma machine.
"""

n0, n9, nA, nZ = b'09A'

# Encode Enigma Rotors 10 & 26
encode_rotor_10 = [5, 4, 1, 8, 7, 3, 0, 2, 9, 6]
encode_rotor_26 = [16, 8, 25, 5, 23, 21, 18, 17, 2, 1, 7, 24, 15, 11, 9, 6, 3, 0, 19, 12, 22, 14, 10, 4, 20, 13]

# Decode Enigma Rotors 10 & 26 - Inverse of encode
decode_rotor_10 = [6, 2, 7, 5, 1, 0, 9, 4, 3, 8]
decode_rotor_26 = [17, 9, 8, 16, 23, 3, 15, 10, 1, 14, 22, 13, 19, 25, 21, 12, 0, 7, 6, 18, 24, 5, 20, 4, 11, 2]

Below is the encoding and debugging output.
Code:
DEBUG: Calculated Checksum: 48
DEBUG: Modified Key Code with Checksum: b'8469641234567001'
DEBUG: Original Char: 56
DEBUG: Transformed Char: 9, Checksum: 8
DEBUG: Original Option Key: [9]
DEBUG: Original Char: 52
DEBUG: Transformed Char: 0, Checksum: 17
DEBUG: Original Option Key: [9, 0]
DEBUG: Original Char: 54
DEBUG: Transformed Char: 6, Checksum: 37
DEBUG: Original Option Key: [9, 0, 6]
DEBUG: Original Char: 57
DEBUG: Transformed Char: 1, Checksum: 76
DEBUG: Original Option Key: [9, 0, 6, 1]
DEBUG: Original Char: 54
DEBUG: Transformed Char: 5, Checksum: 110
DEBUG: Original Option Key: [9, 0, 6, 1, 5]
DEBUG: Original Char: 52
DEBUG: Transformed Char: 7, Checksum: 139
DEBUG: Original Option Key: [9, 0, 6, 1, 5, 7]
DEBUG: Original Char: 49
DEBUG: Transformed Char: 1, Checksum: 152
DEBUG: Original Option Key: [9, 0, 6, 1, 5, 7, 1]
DEBUG: Original Char: 50
DEBUG: Transformed Char: 5, Checksum: 175
DEBUG: Original Option Key: [9, 0, 6, 1, 5, 7, 1, 5]
DEBUG: Original Char: 51
DEBUG: Transformed Char: 9, Checksum: 210
DEBUG: Original Option Key: [9, 0, 6, 1, 5, 7, 1, 5, 9]
DEBUG: Original Char: 52
DEBUG: Transformed Char: 7, Checksum: 259
DEBUG: Original Option Key: [9, 0, 6, 1, 5, 7, 1, 5, 9, 7]
DEBUG: Original Char: 53
DEBUG: Transformed Char: 0, Checksum: 324
DEBUG: Original Option Key: [9, 0, 6, 1, 5, 7, 1, 5, 9, 7, 0]
DEBUG: Original Char: 54
DEBUG: Transformed Char: 1, Checksum: 407
DEBUG: Original Option Key: [9, 0, 6, 1, 5, 7, 1, 5, 9, 7, 0, 1]
DEBUG: Original Char: 55
DEBUG: Transformed Char: 5, Checksum: 510
DEBUG: Original Option Key: [9, 0, 6, 1, 5, 7, 1, 5, 9, 7, 0, 1, 5]
DEBUG: Original Char: 48
DEBUG: Transformed Char: 5, Checksum: 523
DEBUG: Original Option Key: [9, 0, 6, 1, 5, 7, 1, 5, 9, 7, 0, 1, 5, 5]
DEBUG: Original Char: 48
DEBUG: Transformed Char: 2, Checksum: 537
DEBUG: Original Option Key: [9, 0, 6, 1, 5, 7, 1, 5, 9, 7, 0, 1, 5, 5, 2]
DEBUG: Original Char: 49
DEBUG: Transformed Char: 7, Checksum: 568
DEBUG: Original Option Key: [9, 0, 6, 1, 5, 7, 1, 5, 9, 7, 0, 1, 5, 5, 2, 7]
Option Code: 9061 5715 9701 5527
Would you like to generate another Option Code (Y/N): n
OK, goodbye! :-)

    ~/Dev/sr/P/Enigma_v3.5  on   main !3  /usr/local/bin/python3 /Users/krisarmstrong/Developer/src/Python3/Enigma_v3.5/enigma_main.py
Enigma V3.5
Do you want to (E)ncode or (D)ecode? Enter E or D: d
Enter the option code to decode: 9061571597015527
DEBUG: idx=2, n=54, temp_sum=6, check_sum=21
DEBUG: idx=3, n=49, temp_sum=1, check_sum=28
DEBUG: idx=4, n=53, temp_sum=5, check_sum=57
DEBUG: idx=5, n=55, temp_sum=7, check_sum=104
DEBUG: idx=6, n=49, temp_sum=1, check_sum=117
DEBUG: idx=7, n=53, temp_sum=5, check_sum=164
DEBUG: idx=8, n=57, temp_sum=9, check_sum=253
DEBUG: idx=9, n=55, temp_sum=7, check_sum=332
DEBUG: idx=10, n=48, temp_sum=0, check_sum=342
DEBUG: idx=11, n=49, temp_sum=1, check_sum=365
DEBUG: idx=12, n=53, temp_sum=5, check_sum=442
DEBUG: idx=13, n=53, temp_sum=5, check_sum=525
DEBUG: idx=14, n=50, temp_sum=2, check_sum=569
DEBUG: idx=15, n=55, temp_sum=7, check_sum=696
DEBUG: Extracted Checksum: 9
DEBUG: Calculated Checksum: 4
Invalid checksum.
Decoding failed.

69641234567001 was the number encoded to 9061571597015527 however it does not decode 9061571597015527 back to 69641234567001.
 
Solution
I'm not a Python programmer, so I can't see any syntax errors, but I think I see a couple of logic errors.

Problem 1: decode_checksum_calc()

check_sum is the checksum of the ciphertext, and extracted_checksum is the checksum of the plaintext, so they're not going to match.

Suggestion: Test after decoding, not before.

Problem 2: decode_with_rotor_10()

Notice that checksum is being calculated but not used?

decoded_val is not the plaintext, yet.

It is (plaintext + max_check_sum - checksum)%10

You haven't fully reversed the encoding.

Just my opinion, I could be wrong.
The operation of an Enigma machine is not complicated, a simple program can mimic 3 rotors and a reflector.

Suggestion, get rid of the checksum.

A decode routine should not be needed, because encoding and decoding are the same operation.

Just my opinion.
 
I'm not a Python programmer, so I can't see any syntax errors, but I think I see a couple of logic errors.

Problem 1: decode_checksum_calc()

check_sum is the checksum of the ciphertext, and extracted_checksum is the checksum of the plaintext, so they're not going to match.

Suggestion: Test after decoding, not before.

Problem 2: decode_with_rotor_10()

Notice that checksum is being calculated but not used?

decoded_val is not the plaintext, yet.

It is (plaintext + max_check_sum - checksum)%10

You haven't fully reversed the encoding.

Just my opinion, I could be wrong.
 
Solution
Back
Top Bottom