Is there any easy way to reverse this hash function? - python

I've got the following python method, it gets a string and returns an integer. I'm looking for the correct input that will print "Great Success!"
input = "XXX"
def enc(pwd):
inc = 0
for i in range(1, len(pwd) + 1):
_1337 = pwd[i - 1]
_move = ord(_1337) - 47
if i == 1:
inc += _move
else:
inc += _move * (42 ** (i - 1))
return inc
if hex(enc(input)) == 0xEA9D1ED352B8:
print "Great Success!"

It's encoding the input in base 42 (starting from chr(47) which is '/'), and easy to decode:
def dec(x):
while x:
yield chr(47 + x % 42)
x //= 42
print ''.join(dec(0xEA9D1ED352B8))
The output is: ?O95PIVII

Yes it is easy to crack. The answer is "?O95PIVII".
>>> enc("?O95PIVII")==0xEA9D1ED352B8
True
The check in your example is invalid by the way, you need to drop the hex from it because the literal 0xEA9D1ED352B8 is parsed as an int.
edit See Rawing's comment for a hint of how I got the answer. A bit more detail: because its summing powers of 42 based on the index of the char in the string, it's easy to figure out the required length of the input string: math.ceil(math.log(target)/math.log(42)) which gives us 9. I then worked my way from the end to the start of the string, because the last character has the most effect on the sum.

You can try to bruteforce it.
Just make a while loop where the input is encrypted by the function until you have the same hash.
But with no informations about the length of the input and so on it could take a while.

Related

One-liner to print two statements in a for-loop in one line

How can I write this code in one line? The concept is that you should:
get one input from the user and check if the (ASCII form of the character) - 97 is divisible by 2 or not
if yes, you should print the original form of the character
else, you should print the upper case form of the character.
last, you should reverse the answer.
Example, if the input is alexander, the output should be e e a a X R N L D
But it should be in one line and only one, I came up with a solution but it was 3 lines, I don't know what to do next.
This is the code I came up with so far:
h = []
for i in input() : h.append(i.lower() if (ord(i) - 97) % 2 == 0 else i.upper())
print(*sorted(h, reverse=True))
The original code for the question, which you should convert to one line is:
input_string = str(input())
array = []
for i in range(len(input_string)):
if (ord(input_string[i]) - 97) % 2 == 0:
array.append(input_string[i])
else:
array.append(input_string[i].upper())
array.sort(reverse=True)
answer = ' '.join(array)
print(answer)
List comprehension (I did not check your code, only rewrite it):
h = []
for i in input() : h.append(i.lower() if (ord(i) - 97) % 2 == 0 else i.upper())
print(*sorted(h, reverse=True))
print(*sorted([i.lower() if (ord(i)-97)%2 == 0 else i.upper() for i in input() ], reverse=True))
To quote your question:
you should print the original form of the character
That is not what the code does at the moment, just saying so you are aware.
after reading your deleted comment:
And if you are wondering about the if and else in list comprehension:
You can put it in your list, but if that was your problem (the actual question apparently) then I would suggest to use google, there are plenty of examples where this is used.: if/else in a list comprehension, https://towardsdatascience.com/a-gentle-introduction-to-flow-control-loops-and-list-comprehensions-for-beginners-3dbaabd7cd8a, https://pythonguides.com/python-list-comprehension-using-if-else/

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).

Decrypting a string into a number

Last week, I was assigned to encrypt numeric PINs into pronounceable strings (made up of vowel-consonant pairs). This went well.
This week, I've been assigned to decrypt the strings my function produced back into their original PIN form. I'm trying to reverse-engineer my code, but I don't know where to start.
Global variables:
CONSONANTS = "bcdfghjklmnpqrstvwyz"
VOWELS = "aeiou"
Encryption code:
def alphapinEncode(pin):
'''(num) -> string
Takes user input (pin) and converts it into a pronounceable string.
Returns the string (codedPin)
Examples:
>>> alphainEncode(4327)
'lohi'
>>> alphainEncode(3463470)
'bomejusa'
'''
codedPin = ""
while pin > 0:
last_two_digits = pin % 100
codedPin = VOWELS[last_two_digits % 5] + codedPin
codedPin = CONSONANTS[last_two_digits // 5] + codedPin
pin = pin // 100
return codedPin
Decryption code:
def alphapinDecode(codedPin):
'''(string) -> num
DOCSTRING PLACEHOLDER
'''
#This while loop checks validity of input string (string).
testPin = codedPin
while len(testPin) > 0:
if testPin[-1] in VOWELS and testPin[-2] in CONSONANTS:
testPin = testPin[:-2]
else:
print ("Your string is incorrectly formatted. Please use consonant-vowel pairs.")
return None
#Decryption code goes here!
return #pin
Problem with your spec: What happens when the string to be decoded is not even in length?
Ignoring that case, your approach should resemble this:
Split the codedPin into groups of 2, or take 2 characters at a time from the input. Keep these values so we can decode the current group of 2.
Reverse the algorithm you used to cypher the pins. Many comments are saying you cannot reverse a modulo operation - which may be true in the generic case, but since we are only dealing with positive integers, we can certainly reverse the value. Here's a hint: find the index of each character in the original CONSONANTS and VOWELS string to give yourself a number to start from. If you get stuck with the math, try manually decoding one of the examples on a piece of paper by hand. Pay close attention to the relationship between the index of the characters and the original PIN number.
Store the values for each pair of numbers until the end of the string has been reached.
Return or output the complete value.
I will not code the answer for you because I believe it would be best for you to come up with something on your own. Take these steps as a pointer and start to code! See what you come up with and come back, and you will have some code that you can use to ask an actual question.
That's an interesting problem. I think this works:
pin = 123456789
c = alphapinEncode(pin)
c
'begomariwu'
sum([n*100**(len(c)/2-i-1) for i,n in enumerate([CONSONANTS.index(p[0])*5 + VOWELS.index(p[1]) for p in [c[i:i+2] for i in range(0, len(c), 2)]])])
123456789
Or, with thanks to #mata for suggesting reduce, this improved one-line version:
reduce(lambda a,b:100*a + b, [CONSONANTS.index(consonant)*5 + VOWELS.index(vowel) for consonant, vowel in [c[i:i+2] for i in range(0, len(c), 2)]], 0)
Now to get serious. One-liners can make for interesting puzzles, but real code should be readable. Here's my real answer:
def alphapinDecode(codedPin):
pin = 0
pairs = [codedPin[i:i+2] for i in range(0, len(codedPin), 2)]
for consonant, vowel in pairs:
pin = pin*100 + CONSONANTS.index(consonant)*5 + VOWELS.index(vowel)
return pin
I think this is reasonably clear without comments. As always, good variable names help a lot.
Your implementation of alphapinEncode is good, but still I rewrote it mostly with style changes, but also to use divmod:
def alphapinEncode(pin):
codedPin = ''
while 0 < pin:
pin, current_digits = divmod(pin, 100)
codedPin = (
CONSONANTS[current_digits // 5] +
VOWELS[current_digits % 5] +
codedPin
)
return codedPin

How to convert str in a list to int without using int() in Python

The question is pretty straightforward. Sorry I am very new to this. I'm not allowed to use int().
The algorithm you are looking for is:
initialise number to 0
for each character in str
multiply number by 10
find number of character (ascii value, avaiable from ord(), - 48 is simplest)
add character number to number
Of course, you should check that the string is actually all numbers first.
Here is how to do it for binary (base2) numbers:
>>> bin(12)
'0b1100'
>>> sum([2**i * (ord(ch)-ord('0')) for i, ch in enumerate(reversed('1100'))])
12
Here is a nice, Pythonic way of doing it.
def str_to_int(s):
if type(s) != str:
raise TypeError("invalid type for str_to_int: {}".format(type(s)))
if not s.isdigit():
raise ValueError("invalid literal for str_to_int: '{}'".format(s))
return sum((ord(char) - 48) * 10**(len(s) - n) for n, char in enumerate(s, start=1))
If you don't care about checking the argument to make sure it's a string and contains only digits, then it can be reduced down to the following one-liner:
str_to_int = lambda s: sum((ord(char) - 48) * 10**(len(s) - n) for n, char in enumerate(s, start=1))
This answer depends on the numbers' ASCII values being in sequential order. You want to simply reverse the string, then turn each digit to an int and add it's weighted value to the string. So, if your number is "12345", your first addend would be 5*1, or 5. Your second would be 4*10 or 40. Keep adding them and multiplying your weight by 10 until you reach the end of the string, and you are done!
def str_to_int(string):
if not string.isdigit():
raise ValueError("String must be a digit")
weight = 1
ans = 0
for c in reversed(string):
i = ord(c) - ord('0')
ans = ans + i*weight
weight = weight * 10
return ans
If you can do it for one string, you can do it for all strings! Either use a for loop or the handy map function:
l = ["12312" , '32355']
print(list(map(str_to_int, l)))
print([str_to_int(i) for i in l])

Made a recursive program to convert the column position into an Excel column (1 = A, 27 = AA), getting #'s in my output

I made this program to familiarize myself with recursion and for all intents and purposes it is working.
def alpha_covert(to_print):
if to_print is 0:
return 'Z'
else:
return chr(int(to_print) + 64)
def order(to_print):
if to_print <= 26:
return alpha_covert(to_print)
else:
return (str(order(to_print % 26))) + (str(order(to_print / 26)))
Some example outputs:
>>print(order(1))
>>print(order(100))
>>print(order(443))
>>print(order(9001))
>>print(order(9999999999999999))
A
VC
AQ
EHM
O#JIHYHMTURB
For the last output why is there a #? I assumed there was no issue as int isn't declared until I use alpha_covert which by then should only be less than or equal to 26.
Is this some kind of float rounding error?
Some additional samples while I'm trying to self-solve this. I don't know what this means:
>>print(order(9999999999999997))
>>print(order(9999999999999998))
>>print(order(9999999999999999))
M#JIHYHMTURB
N#JIHYHMTURB
O#JIHYHMTURB
The problem here is that:
if to_print is 0:
happens before you've converted to_print to an integer. Also, you should really be using equality (==) not identity (is); small integers are interned in CPython, but this is an implementation detail you shouldn't rely on.
The simplest fix is:
if to_print == '0': # compare to string, and by equality
but a better way is to convert the number first, and use the fact that zero numerical values evaluate false-y:
def alpha_convert(to_print): # note typo in function name
"""A docstring would be nice, too!"""
to_print = int(to_print)
return chr(to_print + 64) if to_print else 'Z'

Categories