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.
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.
Nice
ReplyDelete