简体   繁体   中英

Different results when return multiple values in python (Cryptopal challenges)


I'm working on problem 3(set 1) of the cryptopals challenges ( https://cryptopals.com/sets/1/challenges/3 )
I've already found the key ('x') and decrypted the message ('Cooking mcs like a pound of bacon') Here is my code:

from hexToBase64 import hexToBinary
from fixedXOR import xorBuffers

def binaryToChar(binaryString):
    asciiValue = 0
    for i in range(int(len(binaryString))-1,-1,-1):
        if(binaryString[i] == '1'):
          asciiValue = asciiValue + 2**(7-i)
    return chr(asciiValue)

def decimalToBinary(number):
    binaryString = ""
    while (number != 0):
        bit = number % 2 
        binaryString = str(bit) + binaryString
        number = int(number/2)
    while(len(binaryString) < 8): 
        binaryString = "0" + binaryString
    return binaryString

def breakSingleByteXOR(cipherString):
    decryptedMess = ""
    lowestError = 10000
    realKey = ""
    for i in range(0,128):
        errorChar = 0 
        tempKey = decimalToBinary(i)
        tempMess = ""
        for j in range(0,len(cipherString),2):
            #Take each byte of the cipherString 
            cipherChar = hexToBinary(cipherString[j:j+2])
            decryptedChar = binaryToChar(xorBuffers(cipherChar,tempKey))
            asciiValue = ord(decryptedChar)
            if (not ((asciiValue >= 65) and (asciiValue <= 90)) \
               or ((asciiValue >= 90) and (asciiValue <= 122)) \
               or ( asciiValue == 32 )):
               # if the character is not one of the characters ("A-Z" or "a-z"
               # or " ") consider it as an "error" 
               errorChar += 1 
            tempMess = tempMess + decryptedChar
        if(errorChar < lowestError):
            lowestError = errorChar
            decryptedMess = tempMess
            realKey = chr(i)
    return (realKey,decryptedMess)



if __name__ == "__main__":
    print(breakSingleByteXOR("1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736")) 

The problem is when I use the function breakSingleByteXOR to return one value (decryptedMess), it came out okay "cOOKING mcS LIKE A POUND OF BACON"
But when I return 2 values with the function (as the code above - (key,decryptedMess)), I received a weird result ('x', 'cOOKING\\x00mc\\x07S\\x00LIKE\\x00A\\x00POUND\\x00OF\\x00BACON'), can anyboby explain to me why this is the case?

Tbh, I'm learning python as I'm doing the challenges so hopefully I dont trigger anyone with these code.... I'd also really appreciate it if anyone could give me some advices on writing good python code
Thanks guys :D

It's true that the reason for the difference in the printed string is a quirk of the print function.

The deeper problem with that program is that it's not producing the correct answer. That's because the big ugly if that tries to decide whether a decrypted character is in the acceptable range is incorrect.

It's incorrect in two ways. The first is that (asciiValue >= 90) should be (asciiValue >= 97) . A better way to write all of those expressions, which would have avoided this error, is to express them as (asciiValue >= ord('a')) and (asciiValue == ord(' ')) and so on, avoiding the inscrutable numbers.

The second way is that the expressions are not properly grouped. As they stand they do this:

character is not in the range 'A' to 'Z',
    or character is in the range 'a' to 'z',
    or character is 'space',
        then count this as an error

so some of the characters that should be good (specifically 'a' through 'z' and space) are counted as bad. To fix, you need to rework the parentheses so that the condition is:

character is not in the range 'A' to 'Z',
    and character is not in the range 'a' to 'z',
    and character is not space,
        then count this as an error

or (this is style you were trying for)

character is not (in the range 'A' to 'Z'
    or in the range 'a' to 'z'
    or a space)

I'm not going to give you the exact drop-in expression to fix the program, it'll be better for you to work it out for yourself. (A good way to deal with this kind of complexity is to move it into a separate function that returns True or False . That makes it easy to test that your implementation is correct, just by calling the function with different characters and seeing that the result is what you wanted.)

When you get the correct expression, you'll find that the program discovers a different "best key" and the decrypted string for that key contains no goofy out-of-range characters that behave strangely with print .

The print function is the culprit - it is translating the characters \\x00 and \\x07 to ASCII values when executed. Specifically, this only occurs when passing a string to the print function, not an iterable or other object (like your tuple ).

This is an example:

>>> s = 'This\x00string\x00is\x00an\x00\x07Example.'

>>> s
'This\x00string\x00is\x00an\x00\x07Example.'

>>> print(s)
This string is an Example.

If you were to add the string s to an iterable ( tuple , set , or list ), s will not be formatted by the print function:

>>> s_list = [s]
>>> print(s_list)  # List
['This\x00string\x00is\x00an\x00\x07Example.']

>>> print(set(s_list))  # Set
{'This\x00string\x00is\x00an\x00\x07Example.'}

>>> print(tuple(s_list))  # Tuple
('This\x00string\x00is\x00an\x00\x07Example.')

Edit

Because the \\x00 and \\x07 bytes are ASCII control characters, ( \\x00 being NUL and \\x07 being BEL), you can't represent them in any other way. So one of the only ways you could strip these characters from the string without printing would be to use the .replace() method; but given \\x00 bytes are being treated as spaces by the terminal, you would have to use s.replace('\\x00', ' ') to get the same output, which has now changed the true content of the string.

Otherwise when building the string; you could try and implement some logic to check for ASCII control characters and either not add them to tempMess or add a different character like a space or similar.

References

ASCII Wiki: https://en.wikipedia.org/wiki/ASCII

Curses Module: https://docs.python.org/3.7/library/curses.ascii.html?highlight=ascii#module-curses.ascii (Might be useful if you wish to implement any logic).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM