Ziad Fakhoury
Ziad Fakhoury

Reputation: 197

Error I can't fix while converting text to number in RSA algorithm using Python

I was tasked an assignment by my teacher to make a python program that can encrypt messages using the RSA algorithm. So to do that I had to create functions which convert text to numbers and then be able to manipulate those numbers and encrypt it. However to test if i have encrypted it correctly i also designed a decryption function which I also made at the bottom. All the functions between the encryption functions and Ascii functions are there to be used in other functions(At this point you would realize i like dividing my code into functions). The problem with my code is that i keep getting this error when i run it

    Traceback (most recent call last):
  File "D:/Applications/Python/Ziad/RSA algotrithim V2.py", line 127, in <module>
    print(decrypt(Phi_N, e, encrypted))
  File "D:/Applications/Python/Ziad/RSA algotrithim V2.py", line 115, in decrypt
    return Num2Text(message)
  File "D:/Applications/Python/Ziad/RSA algotrithim V2.py", line 58, in Num2Text
    return Ascii2Text(Num2Ascii(message))
  File "D:/Applications/Python/Ziad/RSA algotrithim V2.py", line 17, in Ascii2Text
    text = text + chr(int(char))
ValueError: invalid literal for int() with base 10: '1.0'
>>> ================================ RESTART =================

I tested all the functions individually to see if they work and they do. I rechecked my encryption and decryption functions and they should work. For some reason i think my problem is with my decryption function as i think it is giving me the decimal number however i searched the internet on the algorithm and all the sites i looked uses the same algorithm. Please help me with finding the problem, I usually dont ask other people for help (This is my first ever question on StackOverFlow) and I've been hurting my head on this for 3 weeks.

import math

def Text2Ascii(message):
    Ascii = ""
    for count in range(len(message)):
        if len(str(ord(message[count]))) <= 2:
            Ascii = Ascii + "0" +str(ord(message[count])) + " "
        else:
            Ascii = Ascii + str(ord(message[count])) + " "
    return Ascii

def Ascii2Text(message):
    text = ""
    char = ""
    for count in range(len(message)):
        if message[count] == " ":
            text = text + chr(int(char))
            char = ""
        else:
            char = char + message[count]
    text = text + chr(int(char))
    return text

def Ascii2Num(message):
    Num = ""
    for count in range(len(message)):
        if message[count] != " ":
            Num = Num + message[count]
    return int(Num)

def Num2Ascii(message):
    Ascii = ""
    char = ""
    message = str(message)
    if len(message)%3 == 0:
        for count in range(len(message)):
            if (count+1)%3 == 0:
                char = char + message[count]
                Ascii = Ascii + char + " "
                char = ""
            else:
                char = char + message[count]
    else:
        Ascii = "0"
        for count in range(len(message)):
            if (count+2)%3 == 0:
                char = char + message[count]
                Ascii = Ascii + char + " "
                char = ""
            else:
                char = char + message[count]
    return Ascii

def Text2Num(message):
    return Ascii2Num(Text2Ascii(message))

def Num2Text(message):
    return Ascii2Text(Num2Ascii(message))

def exponent(number, power):
    answer = 1
    for count in range(power):
        answer = number*answer
    return answer

def primeCheck(number):
    if number == 2:
        prime = True
    elif number <= 1:
        prime = False
    else:
        n = math.sqrt(number)
        n = math.ceil(n)
        for count in range(2,n+1):
            if number%count == 0:
                prime = False
                break
            else:
                prime = True
    return prime


def factors(number):
    answer = []
    if primeCheck(number)== True:
        answer = [number,1]
    else:
        for count in range(1,number+1):
            if number%count == 0:
                answer.append(count)
    return answer

def phi(number):
    answer = number
    if primeCheck(number) == True:
        answer = number - 1
    else:
        factor = factors(number)
        for count in factor:
            for count2 in factors(count):
                if count2 == count:
                    answer = answer -1
                    break   
    return answer
def encrypt(N,e):
    message = input("What is the message you need encrypted?\n")
    message = Text2Num(message)
    encrypted = (message ** e)%N
    return encrypted

def decrypt(Phi_N, e,encrypted):
    k = 3
    d = (Phi_N*k + 1)/e
    message = encrypted ** d
    return Num2Text(message)

p = int(input("Give me a prime number: "))
while primeCheck(p) == False:
    p = int(input("Give me a prime number: "))
q = int(input("Give me a prime number: "))
while primeCheck(q) == False:
    q = int(input("Give me a prime number: "))
N = q*p
Phi_N = (q-1)*(p-1)
e = int(input("Enter your encryption key: "))
encrypted = encrypt(N,e)
print(decrypt(Phi_N, e, encrypted))

Upvotes: 1

Views: 904

Answers (1)

mwobee
mwobee

Reputation: 524

That specific error that you're getting is due to combination of two factors:

The first, and probably easiest to address, is the way you are breaking up the string in Ascii2Text:

def Ascii2Text(message):
text = ""
char = ""
for count in range(len(message)):
    **if message[count] == " ":**      #<-- This line is not robust
        text = text + chr(int(char))
        char = ""
    else:
        char = char + message[count]
text = text + chr(int(char))
return text

The problem is that, with the way Num2Ascii is implemented, you are ending up with both a trailing space that causes you to try to cast empty string to an int at the end of the loop, and non-space characters earlier in your ascii-fied message. I would recommend either loudly choking on invalid characters, or at the very least not trying to append them to the "char" string that you try to cast to an int on the next line.

This brings us to the next problem: that you even have non-numeral characters in the first place. The decimal point causing the failure you see is coming from an incorrect calculation for d. d is supposed to be the modular multiplicative inverse of e, and it will always be an integer when calculated correctly. Yours, however, was coming up as a decimal more often than not when I ran this code. This is one of the bits of RSA that people choke on most often, since it involves manipulating the modulo operator in ways that people without a math background aren't typically taught. One of the more common, efficient ways to calculate this value is to use the Extended Euclidean algorithm.

A few more notes on things you will probably run into after addressing the two issues above:

  • e should be co-prime with and less than Phi_N, which you are not currently validating. Failure to do this can make it impossible to correctly calculate d (only few pairs of numbers have a modular multiplicative inverse). The easiest (though not strictly correct) way to do this would be to use a while loop beneath e like you did for the prime numbers, which checks that False == primeCheck(e) or e >= Phi_N. To be more correct, you'd have to write a coprime check that takes in both Phi_N and e, and asserts that they do not share a common factor besides 1.

  • The padded version of your message, m, must be less than N. Failure to do this may result in some messages being unrecoverable when you attempt to reverse the exponentiation during decryption. To more easily verify this, you may want to capture the message from the user before they select P and Q (they're more likely to change their numbers than the message they want to send), then add an additional gate after the two primes are provided which checks that N > m.

Upvotes: 1

Related Questions