Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Peer review: Python encyphering script

0 views
Skip to first unread message

A. Jones

unread,
Feb 27, 2002, 10:18:01 PM2/27/02
to
It's my second day Pythoning and I've written this code to encypher
text (or files, ostensibly) into two pieces, neither of which can be
used to determine the message.

Any problems pointed out, clunky lines spotted, or security problems
would be most appreciated. Also, if you've seen something like this,
if you'd tell me, please.

For now, no interface, just C&P between sessions of IDLE.

ASCII converts to numbers because of future security measures (such as
inserting chunks of random data to pad small messages, which would
then automatically delete themselves in the process of decrypting
using multiplication by 0)

Source below.

---

#encyphering tool
plaintext = "Isn't it awfully nice to have a penis; isn't it
frightfully good to have it on!" #The variable makes it obvious.
blarg = [] #To hold translation of ASCII to num
for b in plaintext:
# print b #Was here for development purposes.
blarg.append(ord(b)) #Turn it into a list of ASCII numbers
#nums make sorting easier&append/ord makes a
nice list
plaintext = "Text purged."

blurb = {} #To hold 1:1 value/offset references
b = 0 #reuse variable so's to destroy evidence/make my life easier
for z in blarg:
if z in blurb: #duplicate key fixer
if type(blurb[z]) == "<type 'list'>": #Stops nesting
strings
blurb[z].append(b)
else:
temp = blurb[z] #Used to create first list entry
temp.append(b)
blurb[z] = temp
else: blurb[z] = [b] #sets dictionary with values as keys, offsets
as objects
b = b+1 #Next step in the offset counter... clunky.

valuelist = blurb.keys() #extract keys of dictionary
valuelist.sort() #to be randomized later(date)

finval = [] #to hold final value list
finoff = [] #to hold final offset list
for r in valuelist:
z = 0
while z < len(blurb[r]): #Writes number of values equal to number
of offsets
finval.append(r)
z = z+1
for q in blurb[r]: #Decomposes my clunky nesting lists/dicts
finoff.append(q)

blurb = {} #Destroy the evidence!
valuelist = []

--
#decyphering tool
text = [] #finval
key = [] #finoff
numdict = {}
z = 0
for r in key: #This here is the heart of the decryption process.
Simply coalates the lists.
numdict[r] = text[z]
z = z + 1
readlist = numdict.keys()
readlist.sort() #puts the keys/offsets in order
plaintext = ""
for r in readlist:
plaintext = plaintext + chr(numdict[r])
print plaintext #prints plaintext
---

Aubrey Jones
______________________________________________________________________________
Posted Via Binaries.net = SPEED+RETENTION+COMPLETION = http://www.binaries.net

Paul Rubin

unread,
Feb 27, 2002, 11:14:46 PM2/27/02
to
netz...@magicstar.net (A. Jones) writes:
> It's my second day Pythoning and I've written this code to encypher
> text (or files, ostensibly) into two pieces, neither of which can be
> used to determine the message.
>
> Any problems pointed out, clunky lines spotted, or security problems
> would be most appreciated. Also, if you've seen something like this,
> if you'd tell me, please.

What the heck? There's no encryption function in there anywhere.

If you want convenient low-to-medium security encryption, use the
rotor module already supplied Python.

If you want higher security, use one of the various crypto modules
available through Vaults of Parnassus or elsewhere.

A. Jones

unread,
Feb 27, 2002, 11:19:53 PM2/27/02
to
On 27 Feb 2002 20:14:46 -0800, Paul Rubin <phr-n...@nightsong.com>
wrote:

I didn't claim it was encryption, only encyphering. But, you've
answered my major question as to whether or not something similar
existed.

Thank you.

Nick Mathewson

unread,
Feb 27, 2002, 11:46:31 PM2/27/02
to
In article <3c7c1a91....@menuhin.netfront.net>, A. Jones wrote:
> It's my second day Pythoning and I've written this code to encypher
> text (or files, ostensibly) into two pieces, neither of which can be
> used to determine the message.
>
> Any problems pointed out, clunky lines spotted, or security problems
> would be most appreciated. Also, if you've seen something like this,
> if you'd tell me, please.
[...]

Okay; A code review follows. Not bad for two days of Pythoning!

BTW, I would strongly disrecommend using this cipher for real purposes;
it is not as secure as you think. See my closing comments below.

===================================================================



> #encyphering tool
> plaintext = "Isn't it awfully nice to have a penis; isn't it
> frightfully good to have it on!" #The variable makes it obvious.

#Actually, this won't work: you need to use triple quotes for a string
#to span multiple lines. Try it like this:

plaintext = '''I would have used the "Bruces' Philosophers Song" myself,
but examples are a matter of taste. BTW, the final words are
'a dong'.'''

> blarg = [] #To hold translation of ASCII to num
> for b in plaintext:
> # print b #Was here for development purposes.
> blarg.append(ord(b)) #Turn it into a list of ASCII numbers
> #nums make sorting easier&append/ord makes a
> nice list

#Naming your variables 'blarg', 'blurb', and so forth can make your
#code harder to read.

plaintext = map(ord, plaintext)

> plaintext = "Text purged."

#It won't do any good to 'purge' the plaintext from memory at this point;
#if you're worried about an attacker getting it from RAM, they've already
#had an opportunity to have done so. If you're worried about them reading
#from swap, your program may have already swapped out.

> blurb = {} #To hold 1:1 value/offset references
> b = 0 #reuse variable so's to destroy evidence/make my life easier

Rather than keeping an index, you might want to use the 'range' or
'xrange' method.

> for z in blarg:
> if z in blurb: #duplicate key fixer
> if type(blurb[z]) == "<type 'list'>": #Stops nesting
> strings

#It's bad style to rely in the string value of a type. Intstead, you
#might want to import types, then say
# 'if type(blurb[z]) == types.ListType:'.
#If you're in Python 2.2, you can say,
# 'if type(blurb[z]) == list:'

> blurb[z].append(b)
> else:
> temp = blurb[z] #Used to create first list entry
> temp.append(b)
> blurb[z] = temp

#The two cases above do exactly the same thing; there's no need for the
# type check.

> else: blurb[z] = [b] #sets dictionary with values as keys, offsets
> as objects
> b = b+1 #Next step in the offset counter... clunky.

# Map from each character to an in-order list of its positions in
# the original map.
posMap = {}
for idx in xrange(len(plaintext)):
character = plaintext[idx]
if character in posMap:
posMap[character].append(idx)
else:
posMap[character] = [idx]

> valuelist = blurb.keys() #extract keys of dictionary
> valuelist.sort() #to be randomized later(date)

# These lines are reasonable, but see below for thoughts on security.
valuelist = posMap.keys()
valuelist.sort()

> finval = [] #to hold final value list

> finoff = [] #to hold fial offset list


> for r in valuelist:
> z = 0
> while z < len(blurb[r]): #Writes number of values equal to number
> of offsets
> finval.append(r)
> z = z+1
> for q in blurb[r]: #Decomposes my clunky nesting lists/dicts
> finoff.append(q)
>
> blurb = {} #Destroy the evidence!
> valuelist = []

# If we use a nested loop, we can eliminate the index variable.

finval = []
finoff = []

for val in valuelist:
for pos in posMap[val]:
finval.append(val)
finoff.append(pos)

# It's too late to destroy the evidence, but if you'd like, del works
# just fine.
del posMap, valuelist, plaintext

============================================================
To summarize, and introduce a function:

def encipher(plaintext):
plaintext = map(ord, plaintext)

posMap = {}
for idx in xrange(len(plaintext)):
character = plaintext[idx]
if posMap.has_hey(character):
posMap[character].append(idx)
else:
posMap[character] = [idx]

valuelist = posMap.keys()
valuelist.sort()

finval = []
finoff = []

for val in valuelist:
for pos in posMap[val]:
finval.append(val)
finoff.append(pos)

return (finval, finoff)

>
> --
> #decyphering tool
> text = [] #finval
> key = [] #finoff
> numdict = {}
> z = 0
> for r in key: #This here is the heart of the decryption process.
> Simply coalates the lists.
> numdict[r] = text[z]
> z = z + 1
> readlist = numdict.keys()
> readlist.sort() #puts the keys/offsets in order
> plaintext = ""
> for r in readlist:
> plaintext = plaintext + chr(numdict[r])
> print plaintext #prints plaintext

#This is my first attempt, an the one that changes your approach least:

def decipher(text, key):
numdict = {}

# It's easiest to use range when you want a list of indices
for idx in xrange(len(key)):
numdict[key[idx]] = text[idx]

readlist = numdict.keys()
readlist.sort()

# Using "+" to build strings can be very slow; it needs to build
# a new string every time you use it.

plaintext = [ chr(numdict[r]) for r in readlist ]

return "".join(plaintext)


# This is my second appempt...
def decipher(text, key):
result = [None]*len(text)
for val,pos in zip(text,key):
result[pos] = val
result = map(chr, result)
return "".join(result)

# This is my third:
def decipher(text, key):
temp = zip(key, text)
temp.sort()
return "".join(map(lambda x: chr(x[1]),
temp))

============================================================

And now, some notes on security. (I'm not trying to be harsh here,
and I know this was probably not written for serious use, but it's
important to understand that cryptography is a lot harder than it
looks, and I wouldn't want you to accidentally leak anything private
through trusting inadequate security.)

First of all, this cipher is a bit suspicious. It has the undesirable
property that the frequency counts of the characters in the ciphertext
are the same as in the plaintext. That is to say, if I know you're
sending either "attack at dawn" or "run for your lives", it's easy for
me to tell which one you sent.

Secondly, this cipher doesn't buy you a lot: The key is as long as the
plaintext. If you have a secure place to store the key, you would
presumably use that place to store the plaintext as well.

Third, the scrambling step is more crucial than you think. Without
scrambling, if I see the key-portion of a message, I'll have a pretty
good idea which parts of the message go where: indices for low letters
earlier than indices for high ones. But with scrambling, the quality
of your cipher is limited by the power of your random number
generator.

For a deeper introduction to cryptography, you may want to get a copy
of Schneier's _Applied Cryptography_, or read the FAQs from sci.crypt.

============================================================

Well, I hope this that didn't dampen your entusiasm for Python!
You're off to a good start (esp. for two days!), and I hope we'll hear
more of you in the future!

Welcome-to-comp-lang-python-ly Yrs,

--
Nick Mathewson <Q nick Q m at alum dot mit dot edu>
Remove Q's to respond. No spam.

A. Jones

unread,
Feb 28, 2002, 1:30:15 AM2/28/02
to
On Thu, 28 Feb 2002 04:46:31 GMT, Qni...@alum.mit.edu (Nick
Mathewson) wrote:

>First of all, this cipher is a bit suspicious. It has the undesirable
>property that the frequency counts of the characters in the ciphertext
>are the same as in the plaintext. That is to say, if I know you're
>sending either "attack at dawn" or "run for your lives", it's easy for
>me to tell which one you sent.

Yeah. I've designed dozens of ciphers before, all of which fail
horribly the second I think about frequency analysis. On the other
hand, I've never tried actual encryption... I don't quite grok modular
mathematics yet enough to do anything but monkey-see-monkey-do copies
of other opensource ciphers.

>Secondly, this cipher doesn't buy you a lot: The key is as long as the
>plaintext. If you have a secure place to store the key, you would
>presumably use that place to store the plaintext as well.

This is one of the things I noticed as I played with it (not that I
didn't know it before I started, but it seemed to matter less at the
time).

>Third, the scrambling step is more crucial than you think. Without
>scrambling, if I see the key-portion of a message, I'll have a pretty
>good idea which parts of the message go where: indices for low letters
>earlier than indices for high ones. But with scrambling, the quality
>of your cipher is limited by the power of your random number
>generator.

I figured as much. But, I don't know enough Python yet to want to
play with the offsets randomly, ya' know?

>For a deeper introduction to cryptography, you may want to get a copy
>of Schneier's _Applied Cryptography_, or read the FAQs from sci.crypt.

Thanks.

>============================================================
>
>Well, I hope this that didn't dampen your entusiasm for Python!
>You're off to a good start (esp. for two days!), and I hope we'll hear
>more of you in the future!

Good Lord, no, it didn't dampen my enthusiasm. Hell, it only
heightens it knowing that there're people in this community who're
willing to take a look at a total newbie's code and then spend the
time not only commenting but actually *re-writing* it. Thank you.

Courageous

unread,
Feb 28, 2002, 1:47:16 AM2/28/02
to

>Yeah. I've designed dozens of ciphers before, all of which fail
>horribly the second I think about frequency analysis. On the other
>hand, I've never tried actual encryption... I don't quite grok modular
>mathematics yet enough to do anything but monkey-see-monkey-do copies
>of other opensource ciphers.

Start with basic Linear hashing algorithms. Move your way up
the ladder to CRC calculations and then MD5. While you may
never be a whacky mathematician, you should be able to create
your own novel ciphers in short order after that. They'll all
be breakable probably, but that's a different issue.

Hashing is a baby step towards encryption: it's an attempt
to create a non random thing which is undecypherable from random.

Of course, the randomness of hashing is used for some purpose
other than to hide things, usually. :-)

C//

Carel Fellinger

unread,
Feb 28, 2002, 9:44:40 AM2/28/02
to
Nick Mathewson <Qni...@alum.mit.edu> wrote:
> In article <3c7c1a91....@menuhin.netfront.net>, A. Jones wrote:
...

>> if type(blurb[z]) == "<type 'list'>": #Stops nesting
>> strings

> #It's bad style to rely in the string value of a type. Intstead, you

Not only is it bad style, it does do what he thinks it does:)

>>> type(1)
<type 'int'>
>>> type(1) == type("<type 'int'>")
0
>>> type("<type 'int'>")
<type 'str'>
>>> type(1)
<type 'int'>
>>> type(1) == "<type 'int'>"
0

but

>>> str(type(1)) == "<type 'int'>"
1

Type() returns a type object, the <type 'int'> thingy is merely a
string representtation of that object. Ofcourse they are not equal.
To make this test work, one has to force the type object to return
its string representation, either with str(), repr() or backticks.

And one of the reasons it's bad style, is that in a next version of
Python the string representation might change. (not likely though)
--
groetjes, carel

Nick Mathewson

unread,
Feb 28, 2002, 10:27:12 AM2/28/02
to
In article <a5lfoo$38q$1...@animus.fel.iae.nl>, Carel Fellinger wrote:
> Nick Mathewson <Qni...@alum.mit.edu> wrote:
>> In article <3c7c1a91....@menuhin.netfront.net>, A. Jones wrote:
> ...
>>> if type(blurb[z]) == "<type 'list'>": #Stops nesting
>>> strings
>
>> #It's bad style to rely in the string value of a type. Intstead, you
>
> Not only is it bad style, it does do what he thinks it does:)

Yeah, so it seems. I should have resisted my "assume it somehow
works" instincts and checked whether type([]) had an __eq__ method
that did string coercion.

Apparently not; Python is not C++.

And-what-a-good-thing-that-is-ly Y'rs,
Nick

Nick Mathewson

unread,
Feb 28, 2002, 10:46:09 AM2/28/02
to
In article <3c7dcc76....@mammoth.usenet-access.com>, A. Jones wrote:
> On Thu, 28 Feb 2002 04:46:31 GMT, Qni...@alum.mit.edu (Nick
> Mathewson) wrote:
>
> Yeah. I've designed dozens of ciphers before, all of which fail
> horribly the second I think about frequency analysis. On the other
> hand, I've never tried actual encryption... I don't quite grok modular
> mathematics yet enough to do anything but monkey-see-monkey-do copies
> of other opensource ciphers.

Well, if you're going to copy, you may as well spend a while trying to
implement ciphers the crypto community already believes to be secure.

See:
http://www.faqs.org/faqs/cryptography-faq/part04
http://www.faqs.org/faqs/cryptography-faq/part05
http://www.faqs.org/faqs/cryptography-faq/part06

and have fun! But see...
http://www.faqs.org/faqs/cryptography-faq/snake-oil

[...]


>>Well, I hope this that didn't dampen your entusiasm for Python!
>>You're off to a good start (esp. for two days!), and I hope we'll hear
>>more of you in the future!
>
> Good Lord, no, it didn't dampen my enthusiasm. Hell, it only
> heightens it knowing that there're people in this community who're
> willing to take a look at a total newbie's code and then spend the
> time not only commenting but actually *re-writing* it. Thank you.

No trouble at all. That's one of Python's Sneaky Advantages: because
it's so fun to write, people love to tweak it, and so they wind up
giving good advice over python-list/c.l.py.

It's-all-part-of-Guido's-master-plan-ly Yrs,

0 new messages