Created
August 2, 2015 01:06
-
-
Save lnsp/ea8a047cccdef99c9820 to your computer and use it in GitHub Desktop.
super small and simple encryption / decryption library using sponge functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # a super simple encryption / decryption library using sponge functions | |
| # all credit for RC4 / Spritz goes to Ron Rivest / RSA | |
| # Spritz implementation based on http://people.csail.mit.edu/rivest/pubs/RS14.pdf | |
| import math | |
| class RC4: | |
| def initializeState(self): | |
| self.i = self.j = 0 | |
| def ksa(self, K): | |
| l = len(K) | |
| self.S = bytearray(256) | |
| self.i = 0 | |
| while self.i < 256: | |
| self.S[self.i] = self.i | |
| self.i += 1 | |
| self.j = self.i = 0 | |
| while self.i < 256: | |
| self.j = (self.j + self.S[self.i] + K[self.i % l]) % 256 | |
| self.S[self.i], self.S[self.j] = self.S[self.j], self.S[self.i] | |
| self.i += 1 | |
| def prg(self): | |
| self.i = (self.i + 1) % 256 | |
| self.j = (self.j + self.S[self.i]) % 256 | |
| self.S[self.i], self.S[self.j] = self.S[self.j], self.S[self.i] | |
| z = self.S[(self.S[self.i] + self.S[self.j]) % 256] | |
| return z | |
| def sqz(self, M): | |
| l = len(M) | |
| C = bytearray(l) | |
| self.i = self.j = 0 | |
| for n in range(l): | |
| C[n] = M[n] ^ self.prg() | |
| return C | |
| def encrypt(self, K, M): | |
| self.initializeState() | |
| self.ksa(K) | |
| C = self.sqz(M) | |
| return bytes(C) | |
| def decrypt(self, K, C): | |
| self.initializeState() | |
| self.ksa(K) | |
| M = self.sqz(C) | |
| return bytes(M) | |
| class Plop: | |
| def initializeState(self): | |
| self.i = self.j = 0 | |
| self.z = 1 | |
| def lcg(self, m, a, b, y): | |
| return (a * y + b) % m | |
| def ksa(self, K): | |
| l = len(K) | |
| self.S = bytearray(256) | |
| i = 0 | |
| while i < 256: | |
| self.S[i] = i | |
| i += 1 | |
| j = i = 0 | |
| while i < 256: | |
| j = self.lcg(256, i, (i + j) % 256, self.S[K[j % l]]) | |
| self.S[j], self.S[i] = self.S[i], self.S[j] | |
| i += 1 | |
| def prg(self): | |
| self.i = (self.i + 1) % 256 | |
| self.j = (self.j + self.S[self.i]) % 256 | |
| self.z = (self.S[self.z] + self.S[self.i] + self.S[self.j] + self.j) % 256 | |
| return self.z | |
| def sqz(self, r): | |
| l = len(r) | |
| O = bytearray(l) | |
| for n in range(l): | |
| O[n] = r[n] ^ self.prg() | |
| return O | |
| def encrypt(self, K, M): | |
| self.initializeState() | |
| self.ksa(K) | |
| C = self.sqz(M) | |
| return bytes(C) | |
| def decrypt(self, K, C): | |
| self.initializeState() | |
| self.ksa(K) | |
| M = self.sqz(C) | |
| return bytes(M) | |
| class Spritz: | |
| def __init__(self): | |
| self.N = 256 | |
| self.N_MINUS_1 = self.N - 1 | |
| self.N_OVER_TWO_FLOOR = math.floor(self.N / 2) | |
| self.TWO_N = self.N * 2 | |
| def initializeState(self): | |
| self.i = self.j = self.k = self.z = self.a = 0 | |
| self.w = 1 | |
| self.S = bytearray(self.N) | |
| for v in range(self.N): | |
| self.S[v] = v | |
| def absorb(self, I): | |
| l = len(I) | |
| for v in range(l): | |
| self.absorbByte(I[v]) | |
| def absorbByte(self, b): | |
| self.absorbNibble(self.low(b)) | |
| self.absorbNibble(self.high(b)) | |
| def absorbNibble(self, x): | |
| if self.a == self.N_OVER_TWO_FLOOR: | |
| self.shuffle() | |
| self.swap(self.a, self.madd(self.N_OVER_TWO_FLOOR, x)) | |
| self.a = self.madd(self.a, 1) | |
| def absorbStop(self): | |
| if self.a == self.N_OVER_TWO_FLOOR: | |
| self.shuffle() | |
| self.a = self.madd(self.a, 1) | |
| def shuffle(self): | |
| self.whip(self.TWO_N) | |
| self.crush() | |
| self.whip(self.TWO_N) | |
| self.crush() | |
| self.whip(self.TWO_N) | |
| self.a = 0 | |
| def whip(self, r): | |
| for v in range(r): | |
| self.update() | |
| while True: | |
| self.w = self.madd(self.w, 1) | |
| if self.gcd(self.w, self.N) == 1: | |
| break | |
| def crush(self): | |
| index = 0 | |
| for v in range(self.N_OVER_TWO_FLOOR): | |
| index = self.N_MINUS_1 - v | |
| if self.S[v] > self.S[index]: | |
| self.swap(v, index) | |
| def squeeze(self, r): | |
| if self.a > 0: | |
| self.shuffle() | |
| P = bytearray(r) | |
| for v in range(r): | |
| P[v] = self.drip() | |
| return P | |
| def drip(self): | |
| if self.a > 0: | |
| self.shuffle() | |
| self.update() | |
| return self.output() | |
| def update(self): | |
| self.i = self.madd(self.i, self.w) | |
| self.j = self.madd(self.k, self.S[self.madd(self.j, self.S[self.i])]) | |
| self.j = self.madd(self.i + self.k, self.S[self.j]) | |
| self.swap(self.i, self.j) | |
| def output(self): | |
| self.z = self.S[ | |
| self.madd(self.j, self.S[ | |
| self.madd(self.i, self.S[ | |
| self.madd(self.z, self.k) | |
| ]) | |
| ] | |
| ) | |
| ] | |
| return self.z | |
| def hash(self, M, r): | |
| self.initializeState(); | |
| self.absorb(M); | |
| self.absorbStop(); | |
| self.absorb([r & 0xff]) | |
| return self.squeeze(r) | |
| def encrypt(self, K, M): | |
| l = len(M) | |
| C = bytearray(l) | |
| self.keySetup(K) | |
| O = self.squeeze(l) | |
| for i in range(l): | |
| C[i] = M[i] ^ O[i] | |
| return bytes(C) | |
| def decrypt(self, K, C): | |
| l = len(C) | |
| M = bytearray(l) | |
| self.keySetup(K) | |
| O = self.squeeze(l) | |
| for i in range(l): | |
| M[i] = C[i] ^ O[i] | |
| return bytes(M) | |
| def keySetup(self, K): | |
| self.initializeState(); | |
| self.absorb(K) | |
| def madd(self, a, b): | |
| return (a + b) % self.N | |
| def msub(self, a, b): | |
| return self.madd(N, a - b) | |
| def low(self, b): | |
| return b & 0xf | |
| def high(self, b): | |
| return b >> 4 & 0xf | |
| def swap(self, p1, p2): | |
| self.S[p1], self.S[p2] = self.S[p2], self.S[p1] | |
| def gcd(self, a, b): | |
| t = 0 | |
| while b != 0: | |
| t = b | |
| b = a % b | |
| a = t | |
| return a |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment