0

As part of an assignment I've been given some code written in python that was used to encrypt a message, and I have to try and understand the code and decrypt the ciphertext. I've never used python before and am somewhat out of my depth.

I understand most of it and the overall gist of what the code is trying to accomplish, however there are a few lines near the end tripping me up. Here's the entire thing (the &&& denotes sections of code which are supposed to be "damaged", while testing the code I've set secret to "test" and count to 3):

import string
import random
from base64 import b64encode, b64decode

secret = '&&&&&&&&&&&&&&' # We don't know the original message or length

secret_encoding = ['step1', 'step2', 'step3']

def step1(s):
    _step1 = string.maketrans("zyxwvutsrqponZYXWVUTSRQPONmlkjihgfedcbaMLKJIHGFEDCBA","mlkjihgfedcbaMLKJIHGFEDCBAzyxwvutsrqponZYXWVUTSRQPON")
    return string.translate(s, _step1)

def step2(s): return b64encode(s)

def step3(plaintext, shift=4):
    loweralpha = string.ascii_lowercase
    shifted_string = loweralpha[shift:] + loweralpha[:shift]
    converted = string.maketrans(loweralpha, shifted_string)
    return plaintext.translate(converted)

def make_secret(plain, count):
    a = '2{}'.format(b64encode(plain))
    for count in xrange(count):
        r = random.choice(secret_encoding)
        si = secret_encoding.index(r) + 1
        _a = globals()[r](a)
        a = '{}{}'.format(si, _a)
    return a

if __name__ == '__main__':
    print make_secret(secret, count=&&&)

Essentially, I assume the code is meant to choose randomly from the three encryption methods step1, step2 and step3, then apply them to the cleartext a number or times as governed by whatever the value of "count" is.

The "make_secret" method is the part that's bothering me, as I'm having difficulty working out how it ties everything together and what the overall purpose of it is. I'll go through it line by line and give my reasons on each part, so someone can correct me if I'm mistaken.

a = '2{}'.format(b64encode(plain))

This takes the base64 encoding of whatever the "plain" variable corresponds to and appends a 2 to the start of it, resulting in something like "2VGhpcyBpcyBhIHNlY3JldA==" using "this is a secret" for plain as a test. I'm not sure what the 2 is for.

r = random.choice(secret_encoding)
si = secret_encoding.index(r) + 1

r is a random selection from the secret_encoding array, while si corresponds to the next array element after r.

_a = globals()[r](a)

This is one of the parts that has me stumped. From researching global() it seems that the intention here is to turn "r" into a global dictionary consisting of the characters found in "a", ie somewhere later in the code a's characters will be used as a limited character set to choose from. Is this correct or am I way off base?

I've tried printing _a, which gives me what appears to be the letters and numbers found in the final output of the code.

a = '{}{}'.format(si, _a)

It seems as if this is creating a string which is a concatenation of the si and _a variables, however I'll admit I don't understand the purpose of doing this.

I realize this is a long question, but I thought it would be best to put the parts that are bothering me into context.

3
  • You could understand a lot by running the code in an environment that has a debugger, e.g. Eclipse with PyDev. That's what I would do if I tried to answer this question. Commented Nov 7, 2015 at 17:15
  • Or just insert a print statement after each transformation. Commented Nov 7, 2015 at 18:22
  • I've tried both of those things. I'm afraid it's not bringing me any closer to understanding what's going on overall with the code. Commented Nov 8, 2015 at 11:21

1 Answer 1

2

I will refrain from commenting on the readability of the code. I daresay it was all intentional, anyway, for purposes of obfuscation. Your professor is an evil bastard and I want to take his or her course :)

r = random.choice(secret_encoding)
...
_a = globals()[r](a)

You're way off base. This is essentially an ugly and hard-to-read way to randomly choose one of the three functions and run it on a. The function globals() returns a dict that maps names to identifiers; it includes the three functions and other things. globals()[r] looks up one of the three functions based on the name r. Putting (a) after that runs the function with a as the argument.

a = '{}{}'.format(si, _a)

The idea here is to prepend each interim result with the number of the function that encrypted it, so you know which function you need to reverse to decrypt that step. They all accumulate at the beginning, and get encrypted and re-encrypted with each step, except for the last one.

a = '2{}'.format(b64encode(plain))

Essentially, this is applying step2 first. Each encryption with step2 prepends a 2.

So, the program applies count encryptions to the plaintext, with each step using a randomly-chosen transformation, and the choice appears in plaintext before the ciphertext. Your task is to read each prepended number and apply the inverse transformation to the rest of the message. You stop when the first character is not in "123".

One problem I see is that if the plaintext begins with a digit in "123", it will look like we should perform another decryption step. In practice, however, I feel sure that the professor's choice of plaintext does not begin with such a digit (unless they're really evil).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.