Vector Verschlüsselung

Ich will erst mal eine einfache (symmetrische) Verschlüsselung machen.


In [1]:
class Vector(object):
    def __init__(self, length, values = []):
        if(length % 4):
            raise ValueError("Length has to be a multiple of 8")
        self._value = [None for i in range(length)]
        self._lock = [False for i in range(length)]
        self._length = length
        for pos, val in enumerate(values):
            self[pos] = val
        
    def __setitem__(self, iter_, item):
        if(not isinstance(item, int)):
            raise ValueError("Vector does not support {} items".format(type(item)))
        if(not isinstance(iter_, int)):
            raise ValueError("Vector indices have to be int, not {}".format(type(iter_)))
        if(iter_ >= self._length or iter_ < 0):
            raise IndexError("{} out of {}".format(iter_, range(self._length)))
            
        if(self._lock[iter_]):
            raise IndexError("Index {} already locked".format(iter_))
        self._value[iter_] = item
        self._lock[iter_] = True
    def __getitem__(self, iter_):
        if(isinstance(iter_, int)):
            if(not iter_ in range(self._length)):
                raise IndexError("Index {} out of {}".format(iter_, range(self._length)))
            return self._value[iter_]
        if(isinstance(iter_, slice)):
            try:
               return self._value[iter_]
            except:
                pass
            raise IndexError("Slice {} out of {}".format(iter_, range(self._length)))   
        
        raise ValueError("Vector indices have to be int or slice, not {}".format(type(iter_)))
    def __str__(self):
        return "<Vector>({})".format(self._value)
    def __repr__(self):
        return "Vector({}, values = {})".format(self._length, repr(self._value))
    def __len__(self):
        return self._length
    
    
    def __mul__(self, other):
        if(isinstance(other, int)):
            return Vector(self._length, values = [v * other for v in self._value])
        raise ValueError("Cannot __mul__ Vector and {}".format(type(other)))
    def __matmul__(self, other):
        if(isinstance(other, Vector)):
            return sum([ v1 * v2 for v1, v2 in zip(other._value, self._value)])
        raise ValueError("Cannor __matmul__ Vector and {}".format(type(other)))
    def _rmatmul__(self, other):
        return self.__matmul__(other)
    
    def __rmul__(self, other):
        return self.__mul__(other)
    
    def bytes_to_vectors(self, bytes_):
        numbytes = len(bytes_)
        numvectors = numbytes // (self._length // 8)
        for vect in range(numvectors):
            yield self.bytes_to_vector(bytes_)
            bytes_ = bytes_[self._length // 8:]
        if(len(bytes_) != 0):
            yield self.bytes_to_vector(bytes_)
            
    def bytes_to_vector(self, bytes_):
        if(len(bytes_) < self._length // 8):
            bytes_ = bytes_ + b"\x00" * ((self._length // 8) - len(bytes_))
        vect = Vector(self._length)
        for bytepos, byte in enumerate(bytes_[:self._length // 8]):
            for shift in range(8):
                vect[bytepos * 8 + shift] = ((byte & (1 << shift)) >> shift)
        return vect

Das ist schonmal die erste Vectorimplementierung. (__matmul__ geht ab 3.5.2)

Einen ersten Verschlüsselungsvector kann man ganz leicht erzeugen.

Für einen Verschlüsselungsvector gilt:

    v[k] > sum(v[:k])

Also kann man einen Vektor mit Zufallselementen so erzeugen:

    v[k] = v[k - 1] * 2 + r
    für alle k > 0; r sei eine Zufallszahl

Dann lässt sich ein einfacher Vectorgenerator so implementieren


In [2]:
def vgen(vect, rng):
    vect[0] = rng()
        
    for k in range(1, vect._length):
        vect[k] = vect[k - 1] * 2 + rng()

Zunächst allerdings ein einfacherer Vector, bei dem

b * v = b

gilt.


In [3]:
vect = Vector(8)

current = 1
for pos in range(8):
    vect[pos] = current
    current *= 2
    
vect


Out[3]:
Vector(8, values = [1, 2, 4, 8, 16, 32, 64, 128])

Dann kann das Verschlüsseln ja anfangen...


In [4]:
data = [ v for v in vect.bytes_to_vectors(b"Hallo, Welt!")]
for vector in data:
    print(vector)

encrypted = [vect @ d for d in data]
for enc in encrypted:
    print(enc)


<Vector>([0, 0, 0, 1, 0, 0, 1, 0])
<Vector>([1, 0, 0, 0, 0, 1, 1, 0])
<Vector>([0, 0, 1, 1, 0, 1, 1, 0])
<Vector>([0, 0, 1, 1, 0, 1, 1, 0])
<Vector>([1, 1, 1, 1, 0, 1, 1, 0])
<Vector>([0, 0, 1, 1, 0, 1, 0, 0])
<Vector>([0, 0, 0, 0, 0, 1, 0, 0])
<Vector>([1, 1, 1, 0, 1, 0, 1, 0])
<Vector>([1, 0, 1, 0, 0, 1, 1, 0])
<Vector>([0, 0, 1, 1, 0, 1, 1, 0])
<Vector>([0, 0, 1, 0, 1, 1, 1, 0])
<Vector>([1, 0, 0, 0, 0, 1, 0, 0])
72
97
108
108
111
44
32
87
101
108
116
33

In [5]:
def long_to_bytes(long_, frame_size):
    res = []
    for frame in range(frame_size):
        res.append( (long_ & (0xff << (frame * 8))) >> (frame * 8))
    return  bytes(res)

def bytes_to_long(bytes_):
    res = 0
    for shift, byte in enumerate(bytes_):
          res += byte << (shift * 8)
    return res

def bytes_to_longs(bytes_, frame_size):
    for frame in range(len(bytes_) // frame_size):
        yield bytes_to_long(bytes_[:frame_size])
        bytes_ = bytes_[frame_size:]

In [6]:
def decrypt(int_, vect):
    res = []
    for k in range(len(vect) + 1):
        pos = len(vect) - k
        if(int_ > sum(vect[:pos])):
            res.append(1 << pos)
            int_ -= vect[pos]
    return long_to_bytes(sum(res), vect._length // 8)

decrypted = b"".join([decrypt(enc, vect) for enc in encrypted])
print(decrypted)


b'Hallo, Welt!'

Zusammenschluss

Einen Verschlüsselungsvektor erzeugen und Daten über das Internet verschicken:


In [7]:
import socket, random
# WARNING: DO NOT USE random for real cryptographics!

sender, receiver = socket.socketpair()

vector = Vector(8)


rnd = lambda: random.randint(100, 900)
vgen(vector, rnd)
print(vector)

text = b"Attack in the dawn!"

data = vector.bytes_to_vectors(text)

encrypted = [vector @ d for d in data]

frame_size = max([e.bit_length() for e in encrypted]) // 8 + 1

print(frame_size)


to_send = b"".join([long_to_bytes(enc, frame_size) for enc in encrypted])

print("SENDING", to_send)
l = sender.send(to_send)

received = receiver.recv(l)

received = bytes_to_longs(received, frame_size)


decrypted = b"".join([decrypt(r, vector) for r in received])

print(decrypted)
print(decrypted == text)


<Vector>([539, 1581, 3360, 7182, 14509, 29358, 59169, 119179])
3
SENDING b'<\xe9\x00\x9c\x9f\x01\x9c\x9f\x01\xea[\x01\x17b\x01%~\x01\xaer\x00\xf8w\x01*\x89\x01\xaer\x00\x9c\x9f\x01\xddu\x01\ni\x01\xaer\x00\xeff\x01\xea[\x01\xe4\xa7\x01*\x89\x01\xc9t\x00'
b'Attack in the dawn!'
True

Natürlich muss man die frame_size auch mit verschicken!

By The Way

Je größer frame_size und je länger der Verschlüsselungsvector, desto (rechnerisch) sicherer ist die Verschlüsselung.

Wichtig: Der Text muss immer ein Vielfaches von

    vector._length // 8

lang sein!

Dazu ein kleines Beispiel:


In [8]:
import socket, random
# WARNING: DO NOT USE random for real cryptographics!

sender, receiver = socket.socketpair()

vector = Vector(64)


rnd = lambda: random.randint(100, 2000)
vgen(vector, rnd)
print(vector)

text = b"Attack in the dawn!\n\nIt is extremely important that no enemies can read this text!\n they could organize a counter strike! padbytes                      "

data = vector.bytes_to_vectors(text)

encrypted = [vector @ d for d in data]

frame_size = max([e.bit_length() for e in encrypted]) // 8 + 1

print(frame_size)


to_send = b"".join([long_to_bytes(enc, frame_size) for enc in encrypted])

print("SENDING", to_send)
l = sender.send(to_send)

received = receiver.recv(l)

received = bytes_to_longs(received, frame_size)


decrypted = b"".join([decrypt(r, vector) for r in received])

print(decrypted)
print(decrypted == text)


<Vector>([128, 882, 2170, 5671, 11444, 23662, 48962, 98372, 197726, 396493, 794771, 1591184, 3184030, 6369705, 12739634, 25480782, 50963541, 101927929, 203856600, 407714803, 815431496, 1630863619, 3261727509, 6523455177, 13046912046, 26093825914, 52187652707, 104375305767, 208750613073, 417501227415, 835002456101, 1670004912562, 3340009825306, 6680019651310, 13360039303451, 26720078608879, 53440157218386, 106880314437538, 213760628876169, 427521257752808, 855042515505866, 1710085031012926, 3420170062027707, 6840340124056388, 13680680248114217, 27361360496229524, 54722720992459275, 109445441984919745, 218890883969840175, 437781767939681524, 875563535879364750, 1751127071758731254, 3502254143517462651, 7004508287034926101, 14009016574069852426, 28018033148139705686, 56036066296279411515, 112072132592558823596, 224144265185117649148, 448288530370235300239, 896577060740470601949, 1793154121480941204010, 3586308242961882409197, 7172616485923764819184])
10
SENDING b"\x04\xd5\xc5\xf2\xd0\xa5mX?\x01z\xf6\xcd\xd8<\xb3\xd9\xd8'\x01\xdc\x17\xaa\x85\xe47A\x96b\x00\x16d\xbb\x97\x14\xaf\x00+4\x01\xf7\xd0\x1e\xe1\xbb\x08\xe7\x85U\x01V\x1b1\x98\x975\xc2\xc2`\x01\x0b3\xaf\xbdw\x87\xd913\x01\x1bW.\xb6\xcf`\x95\x1e-\x01%\x05)\x18\xbb\xb3\xf0eb\x00\xe2\xb7\x978T\xfd\xfb\xbam\x01}\x87\x1e\x85\x08\xa1\x80\xc4p\x01p\x07jk9\x16I\x92Q\x01 \x93\x02:ZYFib\x001LM\x01Y\xbc\x0114\x01;\x96\x86F\x1e\x9a\x9b\x154\x01\xb3{\x9f4:\xda\xe6\xd0a\x01]\x8e\xdb\xb9\xb9\xc6\x99\x96a\x00&\xbb\xde\xb8\xb9\xc6\x99\x96a\x00&\xbb\xde\xb8\xb9\xc6\x99\x96a\x00"
b'Attack in the dawn!\n\nIt is extremely important that no enemies can read this text!\n they could organize a counter strike! padbytes                      '
True

In [9]:
print(text.decode("UTF-8"))
print("#####################")
print(decrypted.decode("UTF-8"))


Attack in the dawn!

It is extremely important that no enemies can read this text!
 they could organize a counter strike! padbytes                      
#####################
Attack in the dawn!

It is extremely important that no enemies can read this text!
 they could organize a counter strike! padbytes                      

Probleme

Das hier ist natürlich kein sicheres Verschlüsselungssystem. Dafür gibt es einige Gründe, nur ein paar davon sind

  • Statistische Angriffe sind sehr leicht.
    • Es is nicht schwer die Frame Size über einen statistischen Angriff heraus zu finden
    • Wenn padbytes nicht zufällig sind(so wie hier) kann man den geheimen Schlüssel leicht herausfinden
  • Es sollten niemals Texte, die größer sind als der geheime Schlüssel verschickt werden, weil der geheime Schlüssel nicht mutiert.
  • Es ist prinzipiell möglich vom verschlüsselten Text auf den geheimen Schlüssel zu schließen.