Skip to main content
CryptoXo - pronounce crypt oxo, is a simple XOR encryption/decryption.

MicroPython includes two security modules out of the box; 1) cryptolib and, 2) ussl.

$ micropython
MicroPython v1.11-631-gb76f0a73b-kaki5 on 2019-12-09; linux version
Use Ctrl-D to exit, Ctrl-E for paste mode
>>> import cryptolib
>>> dir(cryptolib)
['__class__', '__name__', 'aes']
>>> import ussl
>>> dir(ussl)
['__class__', '__name__', 'wrap_socket']
>>>

CryptoXo is meant as an experiment and not as a replacement to cryptolib.aes. Please use it in your codes if you find it to be good enough.


Basically, there are two ways to do xor encryption in micropython:

1) ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(s1, s2))
2) bytes([_a ^ _b for _a, _b in zip(ba1, ba2)])

Bytes xor is faster than strings xor. We use bytes xor in the CryptoXo module.

XOR provides is a very week encryption scheme, but it is fast and uses little resources. It just uses xor. But there are problems.

Alice and Bob share secrets. Hacker Hank wants to break their secret messages. Alice and Bob use XOR encryption, and Bob's message to Alice always starts with 'Dear Alice'. What can Hank do when he knew about these facts? He can easily break Bob's encrypted message to Alice.

Lets defined our encryption function as:

>>> def xor(s1, s2):
... return ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(s1, s2))

Problem 1: The encrypted text has the same length as the key used.

>>> key = '1234567890987654321'
>>> text = 'Dear Alice, Meet me at 23 Almond Street.'
>>> e = xor(key, text)
>>> print(len(key), len(e))
19 19
>>> d = xor(key, e)
>>> d
'Dear Alice, Meet me'

To encrypt the message, it needs to be split into chunks of size equal to the length of the key used.

Problem 2: It is easy to find the length of the key if the massage has repeated characters.

>>> from binascii import hexlify as hx
>>> p = '+++++++++++++++++++++++++++++++++++++'
>>> e0 = xor(key, p[:len(key)])
>>> e1 = xor(key, p[len(key):])
>>> hx(e0)
b'1a19181f1e1d1c13121b12131c1d1e1f18191a'
>>> hx(e1)
b'1a19181f1e1d1c13121b12131c1d1e1f1819'

Problem 3: The key can be easily cracked using a known plain-text attack. Hank knows that 'Dear Alice' always starts a massage from Bob to Alice.

Lets defined our encryption procedure, crypt as:

def crypt(k, t):
  p = 0
  e = ''
  while 1:
    if p+len(k)>len(t):
       b = t[p:]
    else:
       b = t[p:p+len(k)]
    if b:
       e += xor(k, b)
    if len(b)<len(k):
       break
    else:
       p += len(k)
return e

Now, lets try to crack an encrypted message:

>>> t = 'Dear Alice, Meet me at 23 Almond Street, 10:15 next Saturday'
>>> k = '1234567890'
>>> e = crypt(k,t)
>>> hx(e)
b'7557524615775b515a551d127e51504217555c105046130606167654545f5f5613674144525d4d1c1103030e040317565c48451260554143455c5849'
>>> h = crypt('Dear Alice', e)
>>> h
"1234567890Yw\x1f#p\x03{<?u\x14#rt&W\x1a=7:\x1b3r\x15a\x05>4.yUfb|$B{??-\x01w\x01'a\x02)5;,"
>>> d = crypt('1234567890', e)
>>> d
'Dear Alice, Meet me at 23 Almond Street, 10:15 next Saturday'

Problem 4: Many data files contain null bytes (\x00) as padding. Xor a key to bytes array of null bytes, gives the key as the result.

>>> def xor(ba1, ba2):
... return bytes([_a ^ _b for _a, _b in zip(ba1, ba2)])
...
>>> key = b'1234567890987654321'
>>> data = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> e = xor(key, data)
>>> e
b'1234567890987654321'

The CryptoXo implements this cryptographic scheme.

1) k{0}=sha(sha(s0)+sha(s1))
2) k{n}=sha(sha(s0)+k{n-1}+d{n-1})
3) e{n}=k{n}^d{n}


where k{n} is key at block n, s0 is the seed, s1 is salt, d{n} is data and e{n} is encrypted data at block n. s0 and s1 need to be kept secret. The sha in the hash function sha265 of hashlib.

The initial key, the k{0} is a hash of the sum of the hash of seed and the hash of salt. The encrypted value of block 0 is e{0} = k{0}^d{0}.

Using a (known or guessed) plain-text attack, Hank will get hold of k{0} by e{0}^plain-text. Nonetheless, Hank needs the seed s0 to calculate the next key, k{1}. Since k{0} is a hash of sum of hashes, Hank needs to find two bytes arrays where the hash of their concatenation equal to k{0}. The length of these arrays is 32, len(sha(t))=32. Given that Hank managed to figure out both sha(s0) and sha(s1), he still needs to find s0.

You find CryptoXo under the 'extra' directory when you unpack the custommade.zip file.

Comments

Post a Comment

Popular posts from this blog

Custom made - Sometimes, it is nice to be able to build custom made things. Thanks to Damien George and all the clever people at micropython.org for making it easy. So that, to custom build a micropython is not that difficult. Why do you want to build customize firmware anyway? Well, you might want to include some functionalities of your own and removed some functionalities from the standard distribution. The choices are there for you to make. I have a few changes that I want to make in micropython. version header -kaki5 (pronounce kaki-lima) an additional thread cleanup function for esp32 add frozen modules CryptoXo and uasyncio remove help, upip, and webrepl camera C module for esp32 camera board These are accomplished by modifying and adding files. py/makeversionhdr.py py/modthread.c extra/CryptoXo.py, extra/uasyncio.py, and manifest.py mpconfigport.h main.c and modcamera.c I also want to remove some modules specifically, help, upip, and webrepl from esp32
Multi-threading : I previously used an uasyncio webcam server. This time around, I am testing a multi-thread webcam server. The result is promising. A multi-thread server seems to give a better throughput. The program logic is simpler when compared to the server based on uasyncio. The server is a four threaded application, two for port 80, one for port 81, and port 82 runs on the main thread, which blocks the REPL.  You can start a dedicated thread for port 82 if you do not want to block the REPL. I found pix/7 gives the best performance. A spe/2 will reduce the image size but give you a grayscale image. Please see my previous blog if all these seem mysterious to you. I have compiled a new firmware, MicroPython v1.11-571-g7e374d231.  You can download the new firmware from my repository at GitHub. The soft reset is not functioning properly. You need to do a hard reset. I also include four new functions in modcamera.c: pixformat agcgain aelevels aecvalue The pixformat chan
Micropython - v1.11-498 introduced a few changes in the source-code directory structure. Changes to specific files, as previously described in esp32-cam , will no longer work. A simple working summary on how to build esp32-cam firmware is described below: Make a recursive copy of ports/esp32 to ports/esp32-cam, and you will need to modify the following files: 1) ports/esp32-cam/main.c 2) ports/esp32-cam/Makefile 3) ports/esp32-cam/modcamera.c 4) ports/esp32-cam/mpconfigport.h 5) ports/esp32-cam/boards/manifest.py 6) ports/esp32-cam/boards/sdkconfig.base Or, you can just download a precompiled Micropython v1.11-498 from firmware.bin at GitHub if you want to save some work. However, I encourage you to compile the firmware yourself. You will learn a lot and you can choose to modify anything to your liking. The modcamera.c includes something new. The still photo was taken using these settings; pix =8, con =2, qua =10, and spe =2. You will understand those parameters, later