Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Enigma Decoding Problem
#1
I have been working on a Python application. It was initially coded in C++. I have the encoding working however the decoding is not producing the expected results.

Encode Functions:
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
Decode Functions:
# 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
Rotors:
"""
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]
Encode Debug Output:
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! :-)

DECODE Debug Output.

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.

The decode should be able to get back to the original input. This was originally a C++ application where both the encode and decode worked. Now, just the encoding is working. I have to think I am missing something simple, but I do no know where to go.
Reply
#2
You should post the C++ code so we know what it is supposed to do.

What type was the checksum variable in the C++ code? A lot of C checksum algorithms rely on overflow to limit the checksum to 8 bits or 16 bits or whatever. Python does not have fixed size integers, so you need to use modulo or arithmetic and operations to limit the size of the checksum.
Reply
#3
(Dec-12-2023, 07:38 PM)deanhystad Wrote: You should post the C++ code so we know what it is supposed to do.

What type was the checksum variable in the C++ code? A lot of C checksum algorithms rely on overflow to limit the checksum to 8 bits or 16 bits or whatever. Python does not have fixed size integers, so you need to use modulo or arithmetic and operations to limit the size of the checksum.

Thank you, and here is the C++ code. I'm not nearly as versed in C+ as I am in Python, and even in Python, I'm a beginner. I know the basics.

C++ Code

// Filename: enigma.cpp
// Original: 6/11/2007
// Revision: 8/15/2007
//
// DESCRIPTION: C/C++ program to calculate/check Fluke option key codes.
// STOLEN FROM: Peter Oesper (LANMeter), Bruce Kosbab, Jamie Howze (NetTool)
//
// To Build: Use the free Windows 32-bit Dev-C++ compiler (www.bloodshed.net)
// To Run:   Enter "enigma" from a DOS command prompt window

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

//#include "enigma.h"

#ifndef __ENIGMA_H__
#define __ENIGMA_H__

#include <stdio.h>

typedef unsigned short uint16;
typedef signed short int16;
typedef signed int int32;


typedef struct
{
    char szCode[5];
    char szAbbr[20];
    char szName[30];
} T_ProductInfo;

#define SW_VERSION  "2.00"

#define CODE_NTS2   "3001"
#define CODE_ESCOPE "6963"
#define CODE_LRPRO  "7001"
#define CODE_1TAT "7920"
     
T_ProductInfo productTable[] = {
    CODE_NTS2,		"NTs2",        "NetTool Series II",
    CODE_LRPRO,		"LRPro",       "LinkRunner Pro",
	CODE_ESCOPE,	"Escope/MSv2", "EtherScope/MetroScope"
	// CODE_1TAT,		"OneTouch AT", "OneTouch AT"
};

int g_tableSize = sizeof( productTable ) / sizeof( productTable[0] );


class EnigmaC
{
  public:
    // Encrypt the string pointed to by inString and places the
    // resulting string at the location pointed to by outString.  The
    // encrpyted string will be the same length as the input string.
    // RETURNS: 1 == Success
    //          0 == Failure
    static int16 encrypt(char *inString, char *outString);
  
    // Decrypt the string pointed to by inString and place the
    // resulting string at the location pointed to by outString.
    // RETURNS: 1 == Success
    //          0 == Failure
    static int16 decrypt(char *inString, char *outString);
	static bool checkOptionKey(uint16 option, char *key, char *serialStr);

};

// added for enigma 2 routines

const int16 PRODUCT_CODE_SIZE = 4;
const int16 OPTION_CODE_SIZE  = 3;
const int16 SERIAL_NUMBER_SIZE = 7;
const int16 CHECK_SUM_SIZE = 2;
const int16 KEY_LENGTH = PRODUCT_CODE_SIZE+OPTION_CODE_SIZE+SERIAL_NUMBER_SIZE+CHECK_SUM_SIZE;
const int16 SERIAL_LOCATION = CHECK_SUM_SIZE + PRODUCT_CODE_SIZE;
const int16 PRODUCT_LOCATION = CHECK_SUM_SIZE;
const int16 OPTION_LOCATION = CHECK_SUM_SIZE + PRODUCT_CODE_SIZE + SERIAL_NUMBER_SIZE;

const int MAX_CHECK_SUM = 26000;

class Enigma2C{

public:
	static bool checkOptionKey(uint16 option, char *key);
    static bool encrypt (char *inString, char *outString);
    static bool decrypt (char *inString, char *outString);
};

#endif // __ENIGMA_H__


/*
 * NOTE: The only valid values input to these routines are ascii hex values
 */

              /* Index  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 */
static int16 rotor[] =  { 5, 4,14,11, 1, 8,10,13, 7, 3,15, 0, 2,12, 9, 6 };

#define ROTOR_SIZE (sizeof(rotor)/sizeof(int16))

// enigma 2 stuff

static int16 dRotor10[] =
{
	6,2,7,5,1,0,9,4,3,8
};

static int16 dRotor26[] = 
{
	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
};

static int16 eRotor10[] =
{
	5,4,1,8,7,3,0,2,9,6
};

static int16 eRotor26[] =
{
	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,
};

static char m_keyCode[KEY_LENGTH];



// Encrypt the string pointed to by inString and places the
// resulting string at the location pointed to by outString.  The
// encrpyted string will be the same length as the input string.
// RETURNS: 1 == Success
//          0 == Failure
int16 EnigmaC::encrypt(char *inString, char *outString)
{
  int16 inValue;
  int16 outValue = 0;
  int16 index = 0;

  while (*inString)
  {
    int32 TMP_inValue;
    if (!isxdigit(*inString) || !sscanf (inString++, "%1x", &TMP_inValue))
      return 0; // Illegal input value
    inValue = TMP_inValue;

    // Do the encryption - table lookup XORed with last output
    outValue = (rotor[(inValue + index) % ROTOR_SIZE]) ^ outValue;

    // Output the result as a string
    sprintf(outString++, "%1x", outValue);

    index++;
  }

  return 1;
}


// Decrypt the string pointed to by inString and place the
// resulting string at the location pointed to by outString.
// RETURNS: 1 == Success
//          0 == Failure
int16 EnigmaC::decrypt(char *inString, char *outString)
{
  int16 inValue;
  int16 outValue;
  int16 oldOut;
  int16 xorValue = 0;
  int16 index = 0;
  int16 temp;

  while (*inString)
  {
    int32 TMP_oldOut;
    if (!isxdigit(*inString) || !sscanf(inString++, "%1x", &TMP_oldOut))
      return 0;   // Illegal input value
    oldOut = TMP_oldOut;
    //printf( "oldOut= %x\n", oldOut ); // DEBUG only

    // Reverse the encryption
    inValue = oldOut ^ xorValue;
    for (outValue = 0; outValue < ROTOR_SIZE; outValue++)
      if (rotor[outValue] == inValue)
        break;

    // Output the result as a string
    temp = (outValue - index) % ROTOR_SIZE;
    sprintf(outString++, "%1x", temp);

    xorValue = oldOut;
    index++;
  }

  return 1;
}


// Validate the key for the option
bool EnigmaC::checkOptionKey(uint16 option, char *key, char *serialStr)
{
  // NULL keys are invalid
  if (!key || strlen(key) == 0)
    return false;
    
  // Check for the secret master override
  if (strcmp(key, "bladerules") == 0)
    return true;
    
  // See if the key matches the desired option and MAC.
  char dStr[20];
  int16 sucess = EnigmaC::decrypt(key, dStr);
  
  // serial   = bytes 0..9
  // Option # = bytes 10..11
  if (sucess)
  {
    // Compare the Serial Number
    char s[20];
	int i, j;
	for (i = 11, j = 0; i > 1; j++, i--)
	  s[j] = dStr[i];
    if (strncmp(serialStr, s, 10))
      return false;
    
    // Compare the option
	dStr[2] = 0;
    int o = strtol(dStr, NULL, 10);
    if (o != option)
      return false;
      
    return true;
  }
  return false;
}

///////////////////////////////////////////////////////
///	ENIGMA 2 routines
///////////////////////////////////////////////////////

// Validate the key for the option
bool Enigma2C::checkOptionKey(uint16 option, char *key)
{
  // NULL keys are invalid
  if (!key || strlen(key) == 0)
    return false;
    
  // Decode the key
  char dStr[20];
  int success = Enigma2C::decrypt(key, dStr);
  
  // return value
  // checksum     = bytes 0-1
  // product code = bytes 2-5
  // serial #     = bytes 6-12
  // option #     = bytes 13-15

  if (success)
  {
	// Compare the serial number (ignore the leading 3 zeroes)
#if 0
    if (strncmp(ComPrcsC::sysInfo.serial + 3, dStr + SERIAL_LOCATION, 7))
#endif
      return false;
    
    // Compare the option
    int16 o = strtol(dStr + OPTION_LOCATION, NULL, 10);
    if (o != option)
      return false;
 
    return true;
  }
  return false;
}

bool Enigma2C::decrypt(char *inString, char *outString)
{
    int16 checksum = 0;
    int16 tmpSum = 0;

	strcpy(m_keyCode, inString);

    // Decipher key.
    for (int16 i = 0; i < KEY_LENGTH; i++)
    {
		if (m_keyCode[i] >= 'A')
		{
			tmpSum = (dRotor26[m_keyCode[i] - 'A'] + checksum) % 26;
			m_keyCode[i] = tmpSum + 'A';
		}
		else
		{
			tmpSum = (dRotor10[m_keyCode[i] - '0'] + checksum) % 10;
			m_keyCode[i] = tmpSum + '0';
		}
//		printf( "DEBUG> m_keyCode[%02x]= %02x\n", i, m_keyCode[i] );
		checksum += i + tmpSum + (i * tmpSum);
    }
 
	strcpy(outString, m_keyCode);

    // Return true if checksum okay.
    checksum += (8 * (m_keyCode[1] - '0'));
    return ((checksum % 100) == 0);
}


bool Enigma2C::encrypt(char *inString, char *outString)
{
    int16 csum = 1, checksum = 0;
    int16 tmpSum = 0;
    int16 i =0;

	strcpy(m_keyCode, inString);

    // Calculate and store 2-digit checksum.
    for (i = 2; i < KEY_LENGTH; i++){
	if (isdigit(m_keyCode[i])){
		tmpSum = m_keyCode[i] - '0';
	} else {
		tmpSum = m_keyCode[i] - 'A';
	}
	
	csum += i + tmpSum+ (i * tmpSum);
    }
    
    csum = 100 - (csum % 100);
    m_keyCode[0] = '0' + (csum % 10);
    m_keyCode[1] = '0' + ((csum / 10) % 10);

    // Encipher key
    checksum = 0;
    for (i = 0; i < KEY_LENGTH; i++){
    
	if (isdigit(m_keyCode[i])){
	    tmpSum = m_keyCode[i] - '0';
	    m_keyCode[i] = eRotor10[(tmpSum + MAX_CHECK_SUM - checksum) % 10] + '0';
	} else {
	    tmpSum = m_keyCode[i] - 'A';
	    m_keyCode[i] = eRotor26[(tmpSum + MAX_CHECK_SUM - checksum) % 26] + 'A';
	}
 	checksum += i + tmpSum + (i * tmpSum);
    }

	strcpy(outString, m_keyCode);

    return true;
}


void printOptKey( char *optString )
{
     printf( "Option Key:" );
     for ( int k = 0; k < strlen(optString); k++ )
     {
         if ( k % 4 == 0 )
         {
              printf( " " );
         }
         printf( "%c", optString[k] );
     }
     printf( "\n" );
}


void calcNetToolClassicOptionKey(char *serialStr, int optionNum)
{
    // Original NetTool -- Calculate Option Key
    char szLine[100] = "";

    // default for Enigma 1
    char optionStr[1+1] = "4"; // 1 digit option number

    if ( strlen(serialStr) == 0 )
    {
        int numChars = 0;
        do {
            printf( "Enter Serial Number (10 digits): " );
            strcpy( szLine, "" );
            fgets( szLine, sizeof(szLine), stdin );
            numChars = strlen(szLine);
            szLine[10] = 0;    
            strcpy( serialStr, szLine );
        } while ( numChars != (10+1) ); // allow for newline
    }
    else
    {
        // truncate serial number string to 10 digits
        serialStr[10] = 0;
    }
    
    if ( optionNum < 0 )
    {
        printf( "\n" );
        printf( "NetTool Options: 0=Inline 1=Reports/Ping 3=Personal 4=VoIP 5=SwitchWizard\n" );
        printf( "Enter Option Number (1 digit): " );
        strcpy( szLine, "" );
        fgets( szLine, sizeof(szLine), stdin );
        szLine[1] = 0;    
        optionNum = atoi(szLine);
    }
    if ( optionNum < 0 || optionNum > 9 ) // option must be a single digit
    {
        optionNum = 0;
    }
    sprintf( optionStr, "%d", optionNum );
    
    char inString[12+1] = "";
    sprintf( inString, "%s%s%s", serialStr, optionStr, "0" );
    
    // reverse the string
    char revString[20] = "";
	int i, j;
	for (i = 11, j = 0; i >= 0; j++, i--)
	{
        revString[j] = inString[i];
    }

//    printf( "inString: %s\n", inString ); // DEBUG only
//    printf( "revString: %s\n", revString ); // DEBUG only

    char outString[100] = "";

    printf( "\n" );   
    printf( "Encrypting with Enigma 1...\n" );
    EnigmaC::encrypt(revString, outString);

    //printf( "DEBUG> serialStr: %s (must be 10 digits)\n", serialStr );
    //printf( "DEBUG> optionNum: %s\n", optionStr );
    //printf( "DEBUG> optionKey: %s\n", outString );
    printOptKey( outString );
}


void checkNetToolClassicOptionKey(char *optionKeyStr)
{
    // Original NetTool -- Check Option Key
    char szLine[100] = "";

    // default for Enigma 1
    char serialStr[10+1] = "0003333016"; // 10 digit serial number

    // valid key test
    char inString[12+1] = "5dabade112dd"; // option #04 for s/n 0003333016
    uint16 optionNum = 4; // VoIP option (NetTool Series II)

    do {    
        printf( "Enter Serial Number (10 digits): " );
        strcpy( szLine, "" );
        fgets( szLine, sizeof(szLine), stdin );
        szLine[10] = 0;    
        strcpy( serialStr, szLine );
    } while ( strlen(serialStr) != 10 );

    if ( strlen(optionKeyStr) == 12 )
    {
        strcpy( inString, optionKeyStr );
    }
    else
    {
        do {    
           printf( "Enter Option Key (12 digits): " );
           strcpy( szLine, "" );
           fgets( szLine, sizeof(szLine), stdin );
           szLine[12] = 0;    
           strcpy( inString, szLine );
        } while ( strlen(inString) != 12 );
    }

    printf( "Enter Option Number (1 digit): " );
    strcpy( szLine, "" );
    fgets( szLine, sizeof(szLine), stdin );
    szLine[1] = 0;    
    int nOption = atoi(szLine);
    if ( nOption < 0 || nOption > 9 )
    {
        nOption = 0;
    }
    optionNum = (uint16) nOption;

    printf( "\n" );
    printf( "EnigmaC::checkOptionKey()...\n" );
    printf( "serialNum: %s\n", serialStr );
    printf( "optionKey: %s\n", inString );
    printf( "optionNum: %x\n", optionNum );
    
    if ( EnigmaC::checkOptionKey(optionNum, inString, serialStr) )
    {
         printf( "Option valid\n" );
    }
    else
    {
         printf( "Option invalid\n" );
    }
}


void calcEnigma2OptionKey(char *serialStr, int optionNum, int productCode, bool bAssumeEscope)
{
    // NetTool Series II -- Calculate Option Key
    char szLine[100] = "";
    char outString[100] = "";

    // 2 char checksum + 4 char productID + 7 char S/N + 3 char option
    char inString[] = "ccppppsssssssooo"; // checksum, product code, s/n, option
    char szProductCode[4+1] = CODE_NTS2;
    char optionStr[3+1] = "003"; // 1 digit option number
    int k = 0;
    
    if ( strlen(serialStr) == 0 )
    {
        int numChars = 0;
        do 
        {    
            printf( "Enter Serial Number (7 digits): " );
            strcpy( szLine, "" );
            fgets( szLine, sizeof(szLine), stdin );
            numChars = strlen(szLine);
            szLine[7] = 0;    
            strcpy( serialStr, szLine );
        } while ( numChars != (7+1) ); // allow for newline
    }
    else
    {
        // command line parameter
        serialStr[7] = 0; // truncate serial number string to 7 digits
        printf( "SerialNum= %s\n", serialStr );
    }

    if ( optionNum < 0 )
    {
        printf( "Enter Option # (3 digits max. 4=NTs2_VoIP 7=EScope_All, 2=LR_Reflect, 31=MSv2_All): " );
        strcpy( szLine, "" );
        fgets( szLine, sizeof(szLine), stdin );
        szLine[3] = 0;
        optionNum = atoi(szLine);
        if ( szLine[0] == '\n' )
        {
            if ( bAssumeEscope )
            {
                printf( "Assuming EtherScope LAN/WLAN option 7 \n\n" );
                optionNum = 7;
            }
            else
            {
                printf( "Assuming LinkRunner Pro Reflector option 2 \n\n" );
                optionNum = 2;
            }
        }
    }
    else
    {
        // command line parameter
        printf( "OptionNum= %d\n", optionNum );
    }
    sprintf( optionStr, "%03d", optionNum );
    
    if ( productCode < 0 )
    {
        //printf( "Enter Product Code (4 digits. 3001=NTs2 6963=EScope, 7001=LRPro): " );
        printf( "Enter Product Code (4 digits. " );
        for ( k = 0; k < g_tableSize; k++ )
        {
            printf( "%s=%s", productTable[k].szCode, productTable[k].szAbbr);
            if ( k < g_tableSize - 1 )
            {
                 printf( " " );
            }
        }
        printf( "): " );
        
        strcpy( szLine, "" );
        fgets( szLine, sizeof(szLine), stdin );
        szLine[4] = 0;
        productCode = atoi(szLine);
        if ( productCode == 0 )
        {
            if ( bAssumeEscope )
            {
                printf( "Assuming EtherScope product code %s\n", CODE_ESCOPE );
                productCode = atoi(CODE_ESCOPE);
            }
            else
            {
                printf( "Assuming NetTool Series II product code %s\n", CODE_NTS2 );
                productCode = atoi(CODE_NTS2);
            }
        }
    }
    else
    {
        // command line parameter
        printf( "Product= " );
        for ( k = 0; k < g_tableSize; k++ )
        {
            if ( productCode == atoi(productTable[k].szCode) )
            {
                 printf( "%s", productTable[k].szName );
                 break;
            }
        }
        if ( k == g_tableSize )
        {
             printf( "%d", productCode );
        }
        printf( "\n" );
    }
    sprintf( szProductCode, "%04d", productCode );

    strcpy( inString, "0000000000000000" );
    memcpy( inString+2, szProductCode, 4 );
    memcpy( inString+2+4, serialStr, 7 );
    memcpy( inString+2+4+7, optionStr, 3 );
    
    //printf( "DEBUG> inString: %s\n", inString );

    printf( "\n" );
    printf( "Encrypting with Enigma 2...\n" );
    Enigma2C::encrypt(inString, outString);
    
    //printf( "Option Key: %s\n", outString );
    printOptKey( outString );
}

   
void checkEnigma2OptionKey(char *optionKeyStr)
{
    // NetTool Series II -- Check Option Key

    // test case 
    // OptionKey=   "6406257948597747"; // 16 digit key code from EtherScope
    // ProductCode=  6963 -> EtherScope
    // SerialNumber= 0000607
    // OptionNumber= 007

    char szLine[100] = "";
    char inString[16+1] = "aaaabbbbccccdddd"; // 16 digit option key
    char outString[100] = "";
    int k = 0;

    if ( strlen(optionKeyStr) == 16 )
    {
        strcpy( inString, optionKeyStr );
    }
    else
    {
        int numChars = 0;
        do {
           printf( "Enter Option Key (16 digits): " );
           strcpy( szLine, "" );
           fgets( szLine, sizeof(szLine), stdin );
           numChars = strlen(szLine);
           szLine[16] = 0;    
           strcpy( inString, szLine );
        } while ( numChars != (16+1) ); // allow for newline
    }

    printf( "Decrypting with Enigma 2...\n" );
    Enigma2C::decrypt(inString, outString);

    char szProductCode[4+1] = "";
    memcpy(szProductCode, outString+2, 4);
    printf( "Product Code: %s -> ", szProductCode );
    
    // perform lookup in product table
    for ( k = 0; k < g_tableSize; k++ )
    {
        if ( strcmp(szProductCode, productTable[k].szCode) == 0 )
        {
            printf( "%s", productTable[k].szName );
            break;
        }
    }
    if ( k == g_tableSize )
    {
        printf( "Unknown" );
    }
    printf( "\n" );

    char szSerialNum[7+1] = "";
    memcpy(szSerialNum, outString+2+4, 7);
    printf( "SerialNumber: %s\n", szSerialNum );

    char szOptionNum[3+1] = "";
    memcpy(szOptionNum, outString+2+4+7, 3);
    printf( "OptionNumber: %s\n", szOptionNum );
}


int main(int argc, char *argv[])
{
    char szLine[100] = "";
    int selection = 0;
    
    // 10 digit serial number for Original NetTool
    //  7 digit serial number for NetTool Series II
    char serialStr[10+1] = ""; 

    char optionKeyStr[16+1] = ""; // for EtherScope/NetTool Series II

    int optionNum = -1; // 3 digits max
    int productCode = -1; // 4 digits max
    bool bAssumeEscope = false;
    bool bPromptToExit = true;

    // handle command line arguments
    if ( argc > 1 )
    {
         if ( strcmp("?", argv[1]) == 0 ||
             strcmp("-?", argv[1]) == 0 )
         {
              // show usage
              printf( "usage: %s [-d|-e|-n|-x] [optionKey|serialNo] [optionNo] [productCode]\n", argv[0] );
              printf( "where, \n" );
              printf( "-d is decrypt EtherScope option key \n" );
              printf( "-e is encrypt EtherScope option key \n" );
              printf( "-l is encrypt LinkRunner Pro option key \n" );
              printf( "-n is calculate NetTool option key \n" );
              printf( "-x is check NetTool option key \n" );
              return 1;
         }
         else if ( strcmp("-n", argv[1]) == 0 )
         {
              // encrypt for Original NetTool
              selection = 1;
         }
         else if ( strcmp("-x", argv[1]) == 0 )
         {
              // check Original NetTool option key
              selection = 2;
         }
         else if ( strcmp("-e", argv[1]) == 0 )
         {
              // encrypt for EtherScope
              selection = 3;
              productCode = atoi(CODE_ESCOPE);
              bAssumeEscope = true;
         }
         else if ( strcmp("-l", argv[1]) == 0 )
         {
              // encrypt for LinkRunner Pro
              selection = 3; // encrypt
              productCode = atoi(CODE_LRPRO); // LinkRunner Pro
         }
         else if ( strcmp("-d", argv[1]) == 0 )
         {
              // decrypt for other Fluke products (see table)
              selection = 4;
         }
         else
         {
              selection = atoi(argv[1]);
         }

         if ( argc > 2 )
         {
              if ( selection == 4 )
              {
                   strncpy(optionKeyStr, argv[2], 16);
              }
              else if ( selection == 2 )
              {
                   strncpy(optionKeyStr, argv[2], 12);
              }
              else
              {
                  // selection is 1 or 3
                  strncpy(serialStr, argv[2], 10);
                  if ( argc > 3 )
                  {
                      optionNum = atoi(argv[3]);
                      if ( argc > 4 )
                      {
                          productCode = atoi(argv[4]);
                          bPromptToExit = false;
                      }
                  }
              }
         }
    }

    while ( selection < 1 || selection > 4 )
    {
        printf( "\n" );
        printf( "Enter selection: \n" );
        printf( "1. Calculate option key for NetTool 10/100\n" );
        printf( "2. Check option key for NetTool 10/100\n" );
        printf( "3. Calculate option key for other Fluke products\n" );
        printf( "4. Check option key for other Fluke products\n" );

        strcpy( szLine, "" );
        fgets( szLine, sizeof(szLine), stdin );
        selection = atoi(szLine);
    }
    
    printf( "\n" );
    if ( !bPromptToExit )
    {
        //printf( "Selection= %d\n", selection );
    }

    switch ( selection )
    {
        case 1:
            // calculate option key for original NetTool
             calcNetToolClassicOptionKey(serialStr, optionNum);
        break;

        case 2:
            // check option key for Original NetTool
            checkNetToolClassicOptionKey(optionKeyStr);
        break;

        case 3:
            // encrypt option key for other Fluke products (see table)
            calcEnigma2OptionKey(serialStr, optionNum, productCode, bAssumeEscope);
        break;

        case 4:
            // decrypt option key for other Fluke products (see table)
            checkEnigma2OptionKey(optionKeyStr);
        break;

        default:
            printf( "invalid selection\n" );
        break;
    }

    printf( "\n" ); 
    
    if ( bPromptToExit )
    {   
        fprintf( stderr, "Press Enter key to exit\n" );
        getchar();
    }

    return 0;
}

// end-of-file
Reply
#4
Prune that down to the relevant C++ code.

Your decode checsum calc looks nothing like this:
bool Enigma2C::decrypt(char *inString, char *outString)
{
    int16 checksum = 0;
    int16 tmpSum = 0;

    strcpy(m_keyCode, inString);

    // Decipher key.
    for (int16 i = 0; i < KEY_LENGTH; i++)
    {
        if (m_keyCode[i] >= 'A')
        {
            tmpSum = (dRotor26[m_keyCode[i] - 'A'] + checksum) % 26;
            m_keyCode[i] = tmpSum + 'A';
        }
        else
        {
            tmpSum = (dRotor10[m_keyCode[i] - '0'] + checksum) % 10;
            m_keyCode[i] = tmpSum + '0';
        }
//      printf( "DEBUG> m_keyCode[%02x]= %02x\n", i, m_keyCode[i] );
        checksum += i + tmpSum + (i * tmpSum);
    }

    strcpy(outString, m_keyCode);

    // Return true if checksum okay.
    checksum += (8 * (m_keyCode[1] - '0'));
    return ((checksum % 100) == 0);
}
I modified your decode function to look like this:
def decode(encoded_option_code):
    # Convert the encoded option code to bytes
    encoded_bytes = bytes(encoded_option_code, "utf-8")

    # Continue with the decoding process...
    return decode_with_rotor_10(encoded_bytes)
This decodes the message correctly.

I don't think you can compute the checksum without also decoding the message. It is a single step process, not a two-step process. You can either modify the decode function so it returns a success/fail status, or you can raise a checksum exception
Reply
#5
You might be interested in this old enigma thread.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Numeric Enigma Machine idev 9 545 Mar-29-2024, 06:15 PM
Last Post: idev
  Decoding lat/long in file name johnmcd 4 394 Mar-22-2024, 11:51 AM
Last Post: johnmcd
  json decoding error deneme2 10 3,680 Mar-22-2023, 10:44 PM
Last Post: deanhystad
  flask app decoding problem mesbah 0 2,365 Aug-01-2021, 08:32 PM
Last Post: mesbah
  Decoding a serial stream AKGentile1963 7 8,611 Mar-20-2021, 08:07 PM
Last Post: deanhystad
  xml decoding failure(bs4) roughstroke 1 2,273 May-09-2020, 04:37 PM
Last Post: snippsat
  python3 decoding problem but python2 OK mesbah 0 1,808 Nov-30-2019, 04:42 PM
Last Post: mesbah
  utf-8 decoding failed every time i try adnanahsan 21 10,908 Aug-27-2019, 04:25 PM
Last Post: adnanahsan
  hex decoding in Python 3 rdirksen 2 4,625 May-12-2019, 11:49 AM
Last Post: rdirksen
  Decoding log files in binary using an XML file. captainfantastic 1 2,437 Apr-04-2019, 02:24 AM
Last Post: captainfantastic

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020