summaryrefslogtreecommitdiff
path: root/pygost/gost3413.py
diff options
context:
space:
mode:
authorSergey Matveev <stargrave@stargrave.org>2017-06-04 17:33:49 +0300
committerSergey Matveev <stargrave@stargrave.org>2017-06-04 18:04:27 +0300
commit43fdce36120844bc0fc38e0d5664dfc7090c119a (patch)
tree3047666942b42214b1db708008551cc3021dd73d /pygost/gost3413.py
parenta4cd7ad62f89899aaf702d179531ad284bc72a40 (diff)
downloadpygost-43fdce36120844bc0fc38e0d5664dfc7090c119a.tar.xz
34.13-2015 cipher modes of operation implementation3.2
Diffstat (limited to 'pygost/gost3413.py')
-rw-r--r--pygost/gost3413.py205
1 files changed, 204 insertions, 1 deletions
diff --git a/pygost/gost3413.py b/pygost/gost3413.py
index dc7339e..bd8cc49 100644
--- a/pygost/gost3413.py
+++ b/pygost/gost3413.py
@@ -19,9 +19,14 @@
This module currently includes only padding methods.
"""
+from pygost.utils import bytes2long
+from pygost.utils import long2bytes
+from pygost.utils import strxor
+from pygost.utils import xrange
+
def pad_size(data_size, blocksize):
- """Calculate required pad size to full up BLOCKSIZE
+ """Calculate required pad size to full up blocksize
"""
if data_size < blocksize:
return blocksize - data_size
@@ -46,9 +51,207 @@ def pad2(data, blocksize):
return data + b"\x80" + b"\x00" * pad_size(len(data) + 1, blocksize)
+def unpad2(data, blocksize):
+ """Unpad method 2
+ """
+ last_block = bytearray(data[-blocksize:])
+ pad_index = last_block.rfind(b"\x80")
+ if pad_index == -1:
+ raise ValueError("Invalid padding")
+ for c in last_block[pad_index + 1:]:
+ if c != 0:
+ raise ValueError("Invalid padding")
+ return data[:-(blocksize - pad_index)]
+
+
def pad3(data, blocksize):
"""Padding method 3
"""
if pad_size(len(data), blocksize) == 0:
return data
return pad2(data, blocksize)
+
+
+def ecb_encrypt(encrypter, bs, pt):
+ """ECB encryption mode of operation
+
+ :param encrypter: Encrypting function, that takes block as an input
+ :param int bs: cipher's blocksize
+ :param bytes pt: already padded plaintext
+ """
+ if not pt or len(pt) % bs != 0:
+ raise ValueError("Plaintext is not blocksize aligned")
+ ct = []
+ for i in xrange(0, len(pt), bs):
+ ct.append(encrypter(pt[i:i + bs]))
+ return b"".join(ct)
+
+
+def ecb_decrypt(decrypter, bs, ct):
+ """ECB decryption mode of operation
+
+ :param decrypter: Decrypting function, that takes block as an input
+ :param int bs: cipher's blocksize
+ :param bytes ct: ciphertext
+ """
+ if not ct or len(ct) % bs != 0:
+ raise ValueError("Ciphertext is not blocksize aligned")
+ pt = []
+ for i in xrange(0, len(ct), bs):
+ pt.append(decrypter(ct[i:i + bs]))
+ return b"".join(pt)
+
+
+def ctr(encrypter, bs, data, iv):
+ """Counter mode of operation
+
+ :param encrypter: Encrypting function, that takes block as an input
+ :param int bs: cipher's blocksize
+ :param bytes data: plaintext/ciphertext
+ :param bytes iv: half blocksize-sized initialization vector
+
+ For decryption you use the same function again.
+ """
+ if len(iv) != bs // 2:
+ raise ValueError("Invalid IV size")
+ stream = []
+ ctr_value = 0
+ for _ in xrange(0, len(data) + pad_size(len(data), bs), bs):
+ stream.append(encrypter(iv + long2bytes(ctr_value, bs // 2)))
+ ctr_value += 1
+ return strxor(b"".join(stream), data)
+
+
+def ofb(encrypter, bs, data, iv):
+ """OFB mode of operation
+
+ :param encrypter: Encrypting function, that takes block as an input
+ :param int bs: cipher's blocksize
+ :param bytes data: plaintext/ciphertext
+ :param bytes iv: double blocksize-sized initialization vector
+
+ For decryption you use the same function again.
+ """
+ if len(iv) != 2 * bs:
+ raise ValueError("Invalid IV size")
+ r = [iv[:bs], iv[bs:]]
+ result = []
+ for i in xrange(0, len(data) + pad_size(len(data), bs), bs):
+ r = [r[1], encrypter(r[0])]
+ result.append(strxor(r[1], data[i:i + bs]))
+ return b"".join(result)
+
+
+def cbc_encrypt(encrypter, bs, pt, iv):
+ """CBC encryption mode of operation
+
+ :param encrypter: Encrypting function, that takes block as an input
+ :param int bs: cipher's blocksize
+ :param bytes pt: already padded plaintext
+ :param bytes iv: double blocksize-sized initialization vector
+ """
+ if not pt or len(pt) % bs != 0:
+ raise ValueError("Plaintext is not blocksize aligned")
+ if len(iv) != 2 * bs:
+ raise ValueError("Invalid IV size")
+ r = [iv[:bs], iv[bs:]]
+ ct = []
+ for i in xrange(0, len(pt), bs):
+ ct.append(encrypter(strxor(r[0], pt[i:i + bs])))
+ r = [r[1], ct[-1]]
+ return b"".join(ct)
+
+
+def cbc_decrypt(decrypter, bs, ct, iv):
+ """CBC decryption mode of operation
+
+ :param decrypter: Decrypting function, that takes block as an input
+ :param int bs: cipher's blocksize
+ :param bytes ct: ciphertext
+ :param bytes iv: double blocksize-sized initialization vector
+ """
+ if not ct or len(ct) % bs != 0:
+ raise ValueError("Ciphertext is not blocksize aligned")
+ if len(iv) != 2 * bs:
+ raise ValueError("Invalid IV size")
+ r = [iv[:bs], iv[bs:]]
+ pt = []
+ for i in xrange(0, len(ct), bs):
+ blk = ct[i:i + bs]
+ pt.append(strxor(r[0], decrypter(blk)))
+ r = [r[1], blk]
+ return b"".join(pt)
+
+
+def cfb_encrypt(encrypter, bs, pt, iv):
+ """CFB encryption mode of operation
+
+ :param encrypter: Encrypting function, that takes block as an input
+ :param int bs: cipher's blocksize
+ :param bytes pt: plaintext
+ :param bytes iv: double blocksize-sized initialization vector
+ """
+ if len(iv) != 2 * bs:
+ raise ValueError("Invalid IV size")
+ r = [iv[:bs], iv[bs:]]
+ ct = []
+ for i in xrange(0, len(pt) + pad_size(len(pt), bs), bs):
+ ct.append(strxor(encrypter(r[0]), pt[i:i + bs]))
+ r = [r[1], ct[-1]]
+ return b"".join(ct)
+
+
+def cfb_decrypt(encrypter, bs, ct, iv):
+ """CFB decryption mode of operation
+
+ :param encrypter: Encrypting function, that takes block as an input
+ :param int bs: cipher's blocksize
+ :param bytes ct: ciphertext
+ :param bytes iv: double blocksize-sized initialization vector
+ """
+ if len(iv) != 2 * bs:
+ raise ValueError("Invalid IV size")
+ r = [iv[:bs], iv[bs:]]
+ pt = []
+ for i in xrange(0, len(ct) + pad_size(len(ct), bs), bs):
+ blk = ct[i:i + bs]
+ pt.append(strxor(encrypter(r[0]), blk))
+ r = [r[1], blk]
+ return b"".join(pt)
+
+
+def _mac_shift(bs, data, xor_lsb=0):
+ num = (bytes2long(data) << 1) ^ xor_lsb
+ return long2bytes(num, bs)[-bs:]
+
+
+def _mac_ks(encrypter, bs):
+ Rb = 0b10000111 if bs == 16 else 0b11011
+ _l = encrypter(bs * b'\x00')
+ k1 = _mac_shift(bs, _l, Rb) if bytearray(_l)[0] & 0x80 > 0 else _mac_shift(bs, _l)
+ k2 = _mac_shift(bs, k1, Rb) if bytearray(k1)[0] & 0x80 > 0 else _mac_shift(bs, k1)
+ return k1, k2
+
+
+def mac(encrypter, bs, data):
+ """MAC (known here as CMAC, OMAC1) mode of operation
+
+ :param encrypter: Encrypting function, that takes block as an input
+ :param int bs: cipher's blocksize
+ :param bytes data: data to authenticate
+
+ Implementation is based on PyCrypto's CMAC one, that is in public domain.
+ """
+ k1, k2 = _mac_ks(encrypter, bs)
+ if len(data) % bs == 0:
+ tail_offset = len(data) - bs
+ else:
+ tail_offset = len(data) - (len(data) % bs)
+ prev = bs * b'\x00'
+ for i in xrange(0, tail_offset, bs):
+ prev = encrypter(strxor(data[i:i + bs], prev))
+ tail = data[tail_offset:]
+ return encrypter(strxor(
+ strxor(pad3(tail, bs), prev),
+ k1 if len(tail) == bs else k2,
+ ))