Bottleneck: easy obscurity "encryption" via xor

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Tino Lange

    Bottleneck: easy obscurity "encryption" via xor

    Hi!

    I identified a bottleneck in my programs.

    I just want to "encrypt" data by easy xoring. Ok - that's no
    encryption at all - I know. But it's hardly readable - and that's
    enough :-) Just some quick obscurity.

    It turns out not to be quick at all. I really didn't expect this to be
    a bottleneck, but it takes quite some time.

    Here's the code:
    [color=blue]
    >$ cat python/EasyCrypt.py
    >#! /usr/bin/env python
    >import operator
    >def xorcrypt(str, salt = 255):
    > if salt > 255:
    > raise "Invalid salt! Must be < 255!"
    > return reduce(lambda x,y: operator.add(x, chr(y)), map(lambda char, _salt = salt: operator.xor(or d(char), _salt), str), "")[/color]

    xor'ing medium sized-files takes long time. For example a 360
    kByte-File takes:
    [color=blue]
    >$ time ./just_crypt.py Userdatan/ScanImage01.jpg > bert
    >real 1m52.138s
    >user 0m40.320s
    >sys 1m6.030s[/color]

    on my 2.66 GHz P4 machine!

    Hmmm, do you have some better implementation ideas? Some optimizing
    tricks? (Besides coding in C to avoid immutable string problems)
    I already took the operator module to speed up a bit - but it seems
    that's not enough...

    Thanks

    Tino

  • Irmen de Jong

    #2
    Re: Bottleneck: easy obscurity &quot;encryptio n&quot; via xor

    Tino Lange wrote:
    [color=blue]
    > It turns out not to be quick at all. I really didn't expect this to be
    > a bottleneck, but it takes quite some time.[/color]
    [color=blue][color=green]
    >> return reduce(lambda x,y: operator.add(x, chr(y)), map(lambda char, _salt = salt: operator.xor(or d(char), _salt), str), "")[/color][/color]

    Running this on a large string builds up a huge list of ints,
    that you are converting to chars and then concatenating them
    together using +... this creates a HUGE number of temporary
    string objects.
    The usual pattern of fast string joining is:

    ''.join(list-of-fragments)

    So first try:

    return ''.join(map(lam bda char, _salt = salt: chr(operator.xo r(ord(char), _salt)), string))

    This runs MUCH faster already.

    But the version I'd recommend is:

    def xorcrypt(string , salt = 255):
    if salt <0 or salt> 255:
    raise "Invalid salt! Must be 0<=salt<=255!"
    return ''.join( [ chr(ord(c) ^ salt) for c in string ] )

    because
    1) salt must be 0..255 not only <=255
    2) forget about map & lambda, use a list comprehension.

    That implementation runs about 20 times faster than your original one;
    0.11 seconds for 100 Kb source data. (python 2.3)

    HTH,
    --Irmen de Jong


    Comment

    • Bengt Richter

      #3
      Re: Bottleneck: easy obscurity &quot;encryptio n&quot; via xor

      On Wed, 30 Jul 2003 00:25:59 +0200, Irmen de Jong <[email protected]> wrote:
      [color=blue]
      >Tino Lange wrote:
      >[color=green]
      >> It turns out not to be quick at all. I really didn't expect this to be
      >> a bottleneck, but it takes quite some time.[/color]
      >[color=green][color=darkred]
      >>> return reduce(lambda x,y: operator.add(x, chr(y)), map(lambda char, _salt = salt: operator.xor(or d(char), _salt), str), "")[/color][/color]
      >
      >Running this on a large string builds up a huge list of ints,
      >that you are converting to chars and then concatenating them
      >together using +... this creates a HUGE number of temporary
      >string objects.
      >The usual pattern of fast string joining is:
      >
      >''.join(list-of-fragments)
      >
      >So first try:
      >
      > return ''.join(map(lam bda char, _salt = salt: chr(operator.xo r(ord(char), _salt)), string))
      >
      >This runs MUCH faster already.
      >
      >But the version I'd recommend is:
      >
      >def xorcrypt(string , salt = 255):[/color]
      def xorcrypt(s, salt = 255): # better name choice, even though string module may not be used[color=blue]
      > if salt <0 or salt> 255:
      > raise "Invalid salt! Must be 0<=salt<=255!"
      > return ''.join( [ chr(ord(c) ^ salt) for c in string ] )[/color]
      return s.translate(''. join([chr(ic^salt) for ic in xrange(256)]))[color=blue]
      >
      >because
      >1) salt must be 0..255 not only <=255
      >2) forget about map & lambda, use a list comprehension.[/color]
      forget about list comprehension, use str.translate ;-)[color=blue]
      >
      >That implementation runs about 20 times faster than your original one;
      >0.11 seconds for 100 Kb source data. (python 2.3)
      >[/color]
      s.translate ought to a good deal faster yet ;-)

      Regards,
      Bengt Richter

      Comment

      • Paul Rubin

        #4
        Re: Bottleneck: easy obscurity &quot;encryptio n&quot; via xor

        Tino Lange <tl_news@nexgo. de> writes:[color=blue]
        > Hmmm, do you have some better implementation ideas? Some optimizing
        > tricks? (Besides coding in C to avoid immutable string problems)
        > I already took the operator module to speed up a bit - but it seems
        > that's not enough...[/color]

        Use the array module. See <http://www.nightsong.c om/phr/crypto/p2.py>.

        Comment

        • Oren Tirosh

          #5
          Re: Bottleneck: easy obscurity &quot;encryptio n&quot; via xor

          On Wed, Jul 30, 2003 at 12:03:06AM +0200, Tino Lange wrote:[color=blue]
          > Hi!
          >
          > I identified a bottleneck in my programs.
          >
          > I just want to "encrypt" data by easy xoring. Ok - that's no
          > encryption at all - I know. But it's hardly readable - and that's
          > enough :-) Just some quick obscurity.
          >
          > It turns out not to be quick at all. I really didn't expect this to be
          > a bottleneck, but it takes quite some time.[/color]

          If you want higher performance always try to use things that operate
          on larger chunks. When you do things byte-by-byte you start to notice the
          fact that Python is really an interpreter.

          As noted by Bengt Richter xoring with a constant value can be done by
          str.translate. It doesn't work for variable values, though.

          This code does around 250kb/second on a Pentium 800. XORing is done 32
          bits at a time. Conversion to and from character strings is done in even
          larger chunks using the array module instead of using ord() and chr().

          Oren


          from __future__ import generators

          import sha

          def xor_stream_to_a rrays(fin, seed, hashfunc=sha):
          """ fin is a file-like object.
          yields arrays that may be written to a stream """
          from array import array

          h = hashfunc.new(se ed)
          maskchunk = h.digest()
          chunksize = len(maskchunk)

          while True:
          datachunk = fin.read(chunks ize)
          if len(datachunk) < chunksize:
          break
          yield array('l', [x^y for (x,y) in zip(
          array('l', maskchunk),
          array('l', datachunk))])

          h.update('x')
          maskchunk = h.digest()

          maskchunk = maskchunk[:len(datachunk)] # trim to length of remainder

          # do the rest by bytes:
          yield array('b', [x^y for (x,y) in zip(
          array('b', maskchunk),
          array('b', datachunk))])

          def xor_stream_to_s tream(fin, fout, seed):
          """ fin, fout are file-like objects """
          for a in xor_stream_to_a rrays(fin, seed):
          fout.write(buff er(a))

          def xor_string_to_s tring(s, seed):
          """ gets a string, returns a string """
          from cStringIO import StringIO
          fin = StringIO(s)
          fout = StringIO()
          xor_stream_to_s tream(fin, fout, seed)
          return fout.getvalue()


          Comment

          • Paul Rubin

            #6
            Re: Bottleneck: easy obscurity &quot;encryptio n&quot; via xor

            Tino Lange <tl_news@nexgo. de> writes:[color=blue]
            > And it seems that Bengt's reciepe is the fastest. For very small strings
            > (<255 chars) the method irmen2 should be the best choice - it doesn' have
            > to pre-create the translation-table and does everything on-the-fly.[/color]

            You should be able to use the array module to do the xor's 4 bytes at
            a time and get a speedup over the 1-byte version. The
            string.translat e version is the fastest, of course, but depends on
            using the same translation table for every char in the string.

            If you want to encrypt in python, try the p2.py that I posted; it's
            been carefully designed with good algorithms and fairly well optimized
            and should give much better security than some roll-your-own method.

            Comment

            • Bob Gailer

              #7
              Re: Bottleneck: easy obscurity &quot;encryptio n&quot; via xor

              At 12:25 AM 7/30/2003 +0200, Irmen de Jong wrote:
              [color=blue]
              >Tino Lange wrote:
              >[color=green]
              >>It turns out not to be quick at all. I really didn't expect this to be
              >>a bottleneck, but it takes quite some time.[/color]
              >[color=green][color=darkred]
              >>> return reduce(lambda x,y: operator.add(x, chr(y)), map(lambda char,
              >>> _salt = salt: operator.xor(or d(char), _salt), str), "")[/color][/color]
              >
              >Running this on a large string builds up a huge list of ints,
              >that you are converting to chars and then concatenating them
              >together using +... this creates a HUGE number of temporary
              >string objects.
              >The usual pattern of fast string joining is:
              >
              >''.join(list-of-fragments)
              >
              >So first try:
              >
              > return ''.join(map(lam bda char, _salt = salt:
              > chr(operator.xo r(ord(char), _salt)), string))
              >
              >This runs MUCH faster already.
              >
              >But the version I'd recommend is:
              >
              >def xorcrypt(string , salt = 255):
              > if salt <0 or salt> 255:
              > raise "Invalid salt! Must be 0<=salt<=255!"
              > return ''.join( [ chr(ord(c) ^ salt) for c in string ] )[/color]

              Great minds think alike? I came up with (independently! ):
              return ''.join([chr(ord(char) ^ salt) for char in txt])
              I also favor comprehension because it is more readable.
              [color=blue]
              >because
              >1) salt must be 0..255 not only <=255
              >2) forget about map & lambda, use a list comprehension.
              >
              >That implementation runs about 20 times faster than your original one;
              >0.11 seconds for 100 Kb source data. (python 2.3)
              >
              >HTH,
              >--Irmen de Jong
              >
              >
              >--
              >http://mail.python.org/mailman/listinfo/python-list
              >
              >
              >
              >
              >---
              >Incoming mail is certified Virus Free.
              >Checked by AVG anti-virus system (http://www.grisoft.com).
              >Version: 6.0.500 / Virus Database: 298 - Release Date: 7/10/2003[/color]

              Bob Gailer
              [email protected] i.edu
              303 442 2625


              ---
              Outgoing mail is certified Virus Free.
              Checked by AVG anti-virus system (http://www.grisoft.com).
              Version: 6.0.500 / Virus Database: 298 - Release Date: 7/10/2003

              Comment

              • Paul Rubin

                #8
                Re: Bottleneck: easy obscurity &quot;encryptio n&quot; via xor

                Tino Lange <tl_news@nexgo. de> writes:[color=blue]
                > Thanks! But BTW your "time-bomb" and your comments in the file tell me that
                > this script must not be used anymore...[/color]

                Oh yeah. The code is ok, I just want to rename the function and
                release it as p3.py. I haven't gotten around to that because nobody
                seems to be using it. I keep forgetting. Anyway I'd appreciate it if
                you don't distribute p2.py to other people with the time bomb removed,
                but feel free to remove it for your own use.

                Comment

                Working...