0

I am trying to implement the OFB mode of AES encryption using the pycryptodome library and I'm having issues with understanding what input should i give to the cipher. I know that pycryptodome already has OFB implemented but I need to take the plain text break it into bytes, apply the proper steps of OFB to encrypt it with an AES cipher with ECB mode and then decrypt it.

How do byte strings such as b'\x16\xa8W\xed.)\xc4\xb8x\xd6\xcf\x7f\xf3\xe3;^' work in python?

I need to take such a byte string, encrypt it, then break it in half and XOR it with the 8 bytes of the plain text. Easiest way for me to understand and do this would be to have the IV (the byte string from above) encrypted with AES, then convert it and the plain text to binary and xor them and then convert them back to bytestrings. How can I do that?

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes

plainText = b"Lorem ipsum dolor sit amet, consectetur adipiscing e"
key = b"ANAAREMEREAAAAAA"
iv = get_random_bytes(32)

print("iv: ", iv)

cipher = AES.new(key, AES.MODE_ECB)
toXor = cipher.encrypt(iv)

print("toXor: ", toXor)

"""
toXorF=first half of toXor
ivS=second half of iv

cipherText=toXorF xored to first 8 bytes of plainText
iv=ivS + toXorS
"""

Output of prints:

iv:  b"v'xg;\xd7h\xfc\xf2\xa4[\r\xee]J\x1eu\xa5\xe68\xa3a\xde\x02(\xc1\xe0\xc9z\x0f\xc3n"
toXor:  b'\x97\xbex\xfc\xb6\xbb\x85\xccZ\xc4\xe4\x9d\xd6M\xf2\xd7\xb7\xbf\xd0\xbe\xa5A\xd6\xee\x07U\xe0S\x7f\x88\xea\xcd'

If you have any suggestions for a better architecture of my program/approach to my problem feel free to enlighten me.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • 1
    Bytestrings are binary data, you don't have to convert them. If you're asking how to xor the keystream with the plaintext, you can use `ciphertext = bytes([ks[i] ^ pt[i] for i in range(16)])`. For the next block use the keystream as IV (see the [diagram](https://en.wikipedia.org/wiki/File:OFB_encryption.svg)). Note that you'll have to use a 16 byte IV and 'cut' your plaintext in blocks of 16 bytes. – t.m.adam Jan 09 '19 at 10:47
  • I've found examples where it says that i should use the keystream as iv for the next block and others that say that the new iv is second half of old iv + first half of keystream. When i run the standard code for encrypting a text with pycryptodome's AES with OFB i get the following output for the final iv: OI83f0NCCXSVhDT5bQkb/A== which is 24 bytes in size. Should i break the keystream or should i keep it as it is and keep encrypting it? (Just to make sure, keystream= iv encrypted with provided key, right?) – NeuroTheGreat Jan 09 '19 at 11:19
  • 1
    I don't think that would work, can you share a link to those examples? The IV you get seems to be base64-encoded; if you decode it you'll get a 16 byte string. If it is encrypted with AES-_ECB_, base64 decode it and XOR with the plaintext to get ciphertext. (Yes, keystream= iv encrypted with provided key) – t.m.adam Jan 09 '19 at 11:40
  • First 3 examples of breaking the keystream that come to mind are these: https://www.youtube.com/watch?v=GsSJt6NZF9E https://www.youtube.com/watch?v=Q_hi2jWg6dc https://slideplayer.com/slide/1497990/ – NeuroTheGreat Jan 09 '19 at 11:46
  • 1
    Hmm I wasn't aware of this implementation, I'll have to study it, thanks! However I think only the second video uses this method and I doubt that it is used by PyCryptodome or most other encryption libraries. – t.m.adam Jan 09 '19 at 12:18
  • I will try to implement your approach to the problem and come back to this post. – NeuroTheGreat Jan 09 '19 at 12:32

1 Answers1

3

I applied what t.m.adam suggested and this is the final code which works perfectly.

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes

plainText = b"Lorem ipsum dolor sit amet, consectetur adipiscing e"
key = b"ANAAREMEREAAAAAA"


def ofbEnc(plainText, key):
    pos = 0
    cipherTextChunks = []
    iv = get_random_bytes(16)
    originalIV = iv
    cipher = AES.new(key, AES.MODE_ECB)
    if len(plainText) % 16 != 0:
        plainText += b"1"
    while len(plainText) % 16 != 0:
        plainText += b"0"
    while pos + 16 <= len(plainText):
        toXor = cipher.encrypt(iv)
        nextPos = pos + 16
        toEnc = plainText[pos:nextPos]
        cipherText = bytes([toXor[i] ^ toEnc[i] for i in range(16)])
        cipherTextChunks.append(cipherText)
        pos += 16
        iv = toXor
    return (originalIV, cipherTextChunks)


def ofbDec(cipherTextChunks, key, iv):
    plainText = b""
    cipher = AES.new(key, AES.MODE_ECB)
    for chunk in cipherTextChunks:
        toXor = cipher.encrypt(iv)
        plainText += bytes([toXor[i] ^ chunk[i] for i in range(15)])
        iv = toXor
    while plainText[-1] == 48:
        plainText = plainText[0:-1]
    if plainText[-1] == 49:
        plainText = plainText[0:-1]
    return plainText


iv, result = ofbEnc(plainText, key)
print(iv, result)

plain = ofbDec(result, key, iv)
print(plain)