diff --git a/crack.py b/crack.py new file mode 100644 index 0000000..494b940 --- /dev/null +++ b/crack.py @@ -0,0 +1,11 @@ +from prince64 import prince + +plainfile = open("pt.dat", "rb") +cipherfile = open("ct.dat", "rb") + +pbyte = plainfile.read(8) +cbyte = cipherfile.read(8) +pbytearay = [((a&0x0F)<<4)|((a&0xF0)>>4) for a in list(pbyte)][::-1] +cbytearay = [((a&0x0F)<<4)|((a&0xF0)>>4) for a in list(cbyte)][::-1] +a = prince(pbytearay,[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]) +a.start() \ No newline at end of file diff --git a/ct.dat b/ct.dat new file mode 100644 index 0000000..544326d Binary files /dev/null and b/ct.dat differ diff --git a/ecc.py b/ecc.py new file mode 100644 index 0000000..5e91f75 --- /dev/null +++ b/ecc.py @@ -0,0 +1,73 @@ +from random import * +import math +A = 0 +B = 7 +P = 2**21 - 1# 2**256 - 2**224 + 2**192 + 2**96 - 1 + +def doubleing(point): + x1 = point[0] + y1 = point[1] + + lamda = ((3*(x1**2)+A) % P) * ((2*y1)**(P-2) % P) % P + x2 = (lamda**2 - 2*x1) % P + y2 = ((x1 - x2)*lamda - y1) % P + + return (x2, y2) + +def adding(point1, point2): + x1 = point1[0] + y1 = point1[1] + x2 = point2[0] + y2 = point2[1] + + lamda = (((y2 - y1) % P) * ((x2 - x1)**(P-2) % P)) % P + x3 = (lamda**2 - x1 - x2) % P + y3 = ((x1 - x3) * lamda - y1) % P + + return (x3, y3) + +def muling(point, n): + double = doubleing(point) + if(n==2): + return double + elif(n==1): + return point + else: + log2n = int(math.log(n, 2)) + mul = {} + ddowl = double + num = 2 + count = 1 + mul[2] = double + mul[1] = point + + while(log2n>count): + tmp = doubleing(ddowl) + num = num*2 + mul[num] = tmp + ddowl = tmp + count += 1 + + what = 1 + whatadd = [] + + while(n): + if(n % 2 == 1): + whatadd.append(what) + what = what * 2 + n = int(n / 2) + + result = mul[whatadd[0]] + del(whatadd[0]) + if len(whatadd) >= 1: + for i in whatadd: + result = adding(result,mul[i]) + + return result + +if __name__=="__main__": + count = randint(0,2**1000) + x1 = randint(0,2**1000) + y1 = randint(0,2**1000) + res = muling((x1,y1),count) + print(hex(count), res, sep='\n') \ No newline at end of file diff --git a/elas.py b/elas.py new file mode 100644 index 0000000..8634ffc --- /dev/null +++ b/elas.py @@ -0,0 +1,209 @@ +from dataclasses import dataclass +from random import randint + + +@dataclass +class PrimeGaloisField: + prime: int + + def __contains__(self, field_value: "FieldElement") -> bool: + + return 0 <= field_value.value < self.prime + + +@dataclass +class FieldElement: + value: int + field: PrimeGaloisField + + def __repr__(self): + return "0x" + f"{self.value:x}".zfill(64) + + @property + def P(self) -> int: + return self.field.prime + + def __add__(self, other: "FieldElement") -> "FieldElement": + return FieldElement( + value=(self.value + other.value) % self.P, + field=self.field + ) + + def __sub__(self, other: "FieldElement") -> "FieldElement": + return FieldElement( + value=(self.value - other.value) % self.P, + field=self.field + ) + + def __rmul__(self, scalar: int) -> "FieldValue": + return FieldElement( + value=(self.value * scalar) % self.P, + field=self.field + ) + + def __mul__(self, other: "FieldElement") -> "FieldElement": + return FieldElement( + value=(self.value * other.value) % self.P, + field=self.field + ) + + def __pow__(self, exponent: int) -> "FieldElement": + return FieldElement( + value=pow(self.value, exponent, self.P), + field=self.field + ) + + def __truediv__(self, other: "FieldElement") -> "FieldElement": + other_inv = other ** -1 + return self * other_inv + + +@dataclass +class EllipticCurve: + a: int + b: int + + field: PrimeGaloisField + + def __contains__(self, point: "Point") -> bool: + x, y = point.x, point.y + return y ** 2 == x ** 3 + self.a * x + self.b + + def __post_init__(self): + + self.a = FieldElement(self.a, self.field) + self.b = FieldElement(self.b, self.field) + + if self.a not in self.field or self.b not in self.field: + raise ValueError + + +@dataclass +class Point: + x: int + y: int + + curve: EllipticCurve + + def __post_init__(self): + + if self.x is None and self.y is None: + return + + self.x = FieldElement(self.x, self.curve.field) + self.y = FieldElement(self.y, self.curve.field) + + if self not in self.curve: + raise ValueError + + def __add__(self, other): + + if self == I: + return other + + if other == I: + return self + + if self.x == other.x and self.y == (-1 * other.y): + return I + + if self.x != other.x: + x1, x2 = self.x, other.x + y1, y2 = self.y, other.y + + s = (y2 - y1) / (x2 - x1) + x3 = s ** 2 - x1 - x2 + y3 = s * (x1 - x3) - y1 + + return self.__class__( + x=x3.value, + y=y3.value, + curve=secp256k1 + ) + + if self == other and self.y == inf: + return I + + if self == other: + x1, y1, a = self.x, self.y, self.curve.a + + s = (3 * x1 ** 2 + a) / (2 * y1) + x3 = s ** 2 - 2 * x1 + y3 = s * (x1 - x3) - y1 + + return self.__class__( + x=x3.value, + y=y3.value, + curve=secp256k1 + ) + + def __rmul__(self, scalar: int) -> "Point": + + current = self + result = I + while scalar: + if scalar & 1: + result = result + current + current = current + current + scalar >>= 1 + return result + + +@dataclass +class Signature: + r: int + s: int + + def verify(self, z: int, pub_key: Point) -> bool: + s_inv = pow(self.s, -1, N) + u = (z * s_inv) % N + v = (self.r * s_inv) % N + + return (u*G + v*pub_key).x.value == self.r + + +@dataclass +class PrivateKey: + secret: int + + def sign(self, z: int) -> Signature: + e = self.secret + k = randint(0, N) + R = k * G + r = R.x.value + k_inv = pow(k, -1, N) + s = ((z + r*e) * k_inv) % N + + return Signature(r, s) + + +if __name__ == "__main__": + + P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F + A = 0 + B = 7 + + field = PrimeGaloisField(prime=P) + secp256k1 = EllipticCurve(a=A, b=B, field=field) + + G = Point( + x=0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, + y=0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, + curve=secp256k1) + + N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + + I = Point(x=None, y=None, curve=secp256k1) + + inf = float("inf") + + priv: int = 0xea11d6ada978a0b491aa5cbbe4df17a65c2fecc24448e95d1ccd854b43991bec + e = PrivateKey(priv) + + pub = e.secret * G + print(pub) + z = 0x7e240de74fb1ed08fa08d38063f6a6a91462a815 + + signature: Signature = e.sign(z) + print(e.sign(z)) + assert signature.verify(z, pub) \ No newline at end of file diff --git a/prince64.py b/prince64.py new file mode 100644 index 0000000..deb66f1 --- /dev/null +++ b/prince64.py @@ -0,0 +1,114 @@ +class prince: + def __init__(self, inL: list, inK: list): + self.SBox = [0x0B, 0x0F, 0x03, 0x02, 0x0A, 0x0C, 0x09, 0x01, 0x06, 0x07, 0x08, 0x00, 0x0E, 0x05, 0x0D, 0x04] + self.InvSBox = [0x0B, 0x07, 0x03, 0x02, 0x0F, 0x0D, 0x08, 0x09, 0x0A, 0x06, 0x04, 0x00, 0x05, 0x0E, 0x0C, 0x01] + + self.RC = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x91, 0xa8, 0xe2, 0x30, 0x07, 0x37, 0x44, + 0x4a, 0x90, 0x83, 0x22, 0x92, 0xf9, 0x13, 0x0d, + 0x80, 0xe2, 0xaf, 0x89, 0xce, 0xe4, 0xc6, 0x98 + ] + + self._plaintext = inL + self.Key = inK + + def cipher(self, ExKey): + + self._plaintext = [self._plaintext[i] ^ ExKey[i] for i in range(8)] + + self._AddKey(ExKey) + self._RCLayer(0) + + + self._SLayer() + self._MLayer() + self._RCLayer(1) + self._AddKey(ExKey) + + self._SLayer() + self._MPrimeLayer() + self._InvSLayer() + + self._AddKey(ExKey) + self._RCLayer(2) + self._InvMLayer() + self._InvSLayer() + + self._RCLayer(3) + self._AddKey(ExKey) + + self._plaintext = [self._plaintext[i] ^ ExKey[i+8] for i in range(8)] + + def ExtendKey(self, Key): + newKey = [0x00] * 24 + + for i in range(8): + newKey[i] = Key[i] + newKey[i + 8] = (Key[i] >> 1) | (Key[(i + 1) % 8] << 7 & 0x80) + newKey[i + 16] = Key[i + 8] + + newKey[15] ^= (Key[7] & 0x10) + + return newKey + + def _SR(self): + temp = self._plaintext[:] + perm = [0, 5, 2, 7, 4, 1, 6, 3] + + self._plaintext = [ (temp[perm[i]] & 0x0F) | (self._plaintext[perm[(i+2)%8]] & 0xF0) for i in range(8)] + + + def _InvSR(self): + temp = self._plaintext[:] + perm = [0, 5, 2, 7, 4, 1, 6, 3] + + self._plaintext = [ (temp[perm[i]] & 0x0F) | (self._plaintext[perm[(i+6)%8]] & 0xF0) for i in range(8)] + + def _SLayer(self): + self._plaintext = [ (self.SBox[self._plaintext[i] >> 4] << 4) | self.SBox[self._plaintext[i] & 0x0F] for i in range(8) ] + + + def _InvSLayer(self): + self._plaintext = [ (self.InvSBox[self._plaintext[i] >> 4] << 4) | self.InvSBox[self._plaintext[i] & 0x0F] for i in range(8) ] + + def _MPrimeLayer(self): + temp = self._plaintext[0] + self._plaintext[0] = (temp & 0xD7) ^ (self._plaintext[1] & 0x7D) ^ (temp >> 4 & 0x0B) ^ (self._plaintext[1] >> 4 & 0x0E) ^ (temp << 4 & 0xB0) ^ (self._plaintext[1] << 4 & 0xE0) + self._plaintext[1] = (temp & 0x7D) ^ (self._plaintext[1] & 0xD7) ^ (temp >> 4 & 0x0E) ^ (self._plaintext[1] >> 4 & 0x0B) ^ (temp << 4 & 0xE0) ^ (self._plaintext[1] << 4 & 0xB0) + temp = self._plaintext[2] + self._plaintext[2] = (temp & 0xEB) ^ (self._plaintext[3] & 0xBE) ^ (temp >> 4 & 0x0D) ^ (self._plaintext[3] >> 4 & 0x07) ^ (temp << 4 & 0xD0) ^ (self._plaintext[3] << 4 & 0x70) + self._plaintext[3] = (temp & 0xBE) ^ (self._plaintext[3] & 0xEB) ^ (temp >> 4 & 0x07) ^ (self._plaintext[3] >> 4 & 0x0D) ^ (temp << 4 & 0x70) ^ (self._plaintext[3] << 4 & 0xD0) + temp = self._plaintext[4] + self._plaintext[4] = (temp & 0xEB) ^ (self._plaintext[5] & 0xBE) ^ (temp >> 4 & 0x0D) ^ (self._plaintext[5] >> 4 & 0x07) ^ (temp << 4 & 0xD0) ^ (self._plaintext[5] << 4 & 0x70) + self._plaintext[5] = (temp & 0xBE) ^ (self._plaintext[5] & 0xEB) ^ (temp >> 4 & 0x07) ^ (self._plaintext[5] >> 4 & 0x0D) ^ (temp << 4 & 0x70) ^ (self._plaintext[5] << 4 & 0xD0) + temp = self._plaintext[6] + self._plaintext[6] = (temp & 0xD7) ^ (self._plaintext[7] & 0x7D) ^ (temp >> 4 & 0x0B) ^ (self._plaintext[7] >> 4 & 0x0E) ^ (temp << 4 & 0xB0) ^ (self._plaintext[7] << 4 & 0xE0) + self._plaintext[7] = (temp & 0x7D) ^ (self._plaintext[7] & 0xD7) ^ (temp >> 4 & 0x0E) ^ (self._plaintext[7] >> 4 & 0x0B) ^ (temp << 4 & 0xE0) ^ (self._plaintext[7] << 4 & 0xB0) + + def _MLayer(self): + self._MPrimeLayer() + self._SR() + + def _InvMLayer(self): + self._InvSR() + self._MPrimeLayer() + + def _RCLayer(self, round): + self._plaintext = [ self._plaintext[i] ^ self.RC[8 * round + i] for i in range(8)] + + def _AddKey(self, ExKey): + self._plaintext = [ self._plaintext[i] ^ ExKey[i + 16] for i in range(8) ] + + def prince(self): + ExKey = self.ExtendKey(self.Key) + self.cipher(ExKey) + return [nibble >> 4 | (nibble << 4 & 0xF0) for nibble in self._plaintext] + + def start(self): + ExKey = self.ExtendKey(self.Key) + self.cipher(ExKey) + print('0x', end='') + for i in range(8): + print(format(self._plaintext[i] >> 4 | (self._plaintext[i] << 4 & 0xF0), '02x'), end='') + print() \ No newline at end of file diff --git a/princecc.py b/princecc.py new file mode 100644 index 0000000..34a657d --- /dev/null +++ b/princecc.py @@ -0,0 +1,142 @@ + + +SBox = [0x0B, 0x0F, 0x03, 0x02, 0x0A, 0x0C, 0x09, 0x01, 0x06, 0x07, 0x08, 0x00, 0x0E, 0x05, 0x0D, 0x04] +# Inverse substitution box used on individual nibbles +InvSBox = [0x0B, 0x07, 0x03, 0x02, 0x0F, 0x0D, 0x08, 0x09, 0x0A, 0x06, 0x04, 0x00, 0x05, 0x0E, 0x0C, 0x01] + +RC = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x91, 0xa8, 0xe2, 0x30, 0x07, 0x37, 0x44, + 0x4a, 0x90, 0x83, 0x22, 0x92, 0xf9, 0x13, 0x0d, + 0x80, 0xe2, 0xaf, 0x89, 0xce, 0xe4, 0xc6, 0x98 +] + +_State = [0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE] +Key = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01] + +def cipher(ExtendedKey): + """ The complete PRINCE forward encryption on the 64-bit state performed through nibble calculations """ + global _State + + _State = [_State[i] ^ ExtendedKey[i] for i in range(8)] + + _AddKey(ExtendedKey) + _AddRoundConstant(0) + + + _SubNibbles() + _MLayer() + _AddRoundConstant(1) + _AddKey(ExtendedKey) + + _SubNibbles() + _MPrimeLayer() + _InvSubNibbles() + + _AddKey(ExtendedKey) + _AddRoundConstant(2) + _InvMLayer() + _InvSubNibbles() + + _AddRoundConstant(3) + _AddKey(ExtendedKey) + + _State = [_State[i] ^ ExtendedKey[i+8] for i in range(8)] + +def ExtendKey(Key): + """ PRINCE's version of a key schedule, which extends our 128-bit key to a 192-bit key """ + newKey = [0x00] * 24 + + for i in range(8): + # k_0 stays the same + newKey[i] = Key[i] + # k'_0 is a k_0 rotated right one bit and XORed with the last bit + newKey[i + 8] = (Key[i] >> 1) | (Key[(i + 1) % 8] << 7 & 0x80) + # k_1 stays the same + newKey[i + 16] = Key[i + 8] + + newKey[15] ^= (Key[7] & 0x10) + + return newKey + +def _ShiftRows(): + """ Helper method which distinguishes our two linear layers M from M' """ + global _State + + temp = _State[:] # copy the state into a temporary holder + perm = [0, 5, 2, 7, 4, 1, 6, 3] + + _State = [ (temp[perm[i]] & 0x0F) | (_State[perm[(i+2)%8]] & 0xF0) for i in range(8)] + + +def _InvShiftRows(): + """ Inverse of our ShiftRows() function which allows us to distinguish M'^-1 from M^-1 """ + global _State + + temp = _State[:] # copy the state into a temporary holder + perm = [0, 5, 2, 7, 4, 1, 6, 3] + + _State = [ (temp[perm[i]] & 0x0F) | (_State[perm[(i+6)%8]] & 0xF0) for i in range(8)] + + +def _SubNibbles(): + """ Send the state through a substitution layer nibble-by-nibble """ + global _State + _State = [ (SBox[_State[i] >> 4] << 4) | SBox[_State[i] & 0x0F] for i in range(8) ] + + +def _InvSubNibbles(): + """ Inverse of our substitution layer which sends each substituted nibble back to the original nibble """ + global _State + _State = [ (InvSBox[_State[i] >> 4] << 4) | InvSBox[_State[i] & 0x0F] for i in range(8) ] + + +def _MPrimeLayer(): + """ Our linear layer, designed to use as little space as possible and prevent wasted clock-cycles. Recall that this method is in fact its own inverse. """ + global _State + + # M0 + temp = _State[0] # we only need 1 storage variable here + _State[0] = (temp & 0xD7) ^ (_State[1] & 0x7D) ^ (temp >> 4 & 0x0B) ^ (_State[1] >> 4 & 0x0E) ^ (temp << 4 & 0xB0) ^ (_State[1] << 4 & 0xE0) + _State[1] = (temp & 0x7D) ^ (_State[1] & 0xD7) ^ (temp >> 4 & 0x0E) ^ (_State[1] >> 4 & 0x0B) ^ (temp << 4 & 0xE0) ^ (_State[1] << 4 & 0xB0) + # M1 + temp = _State[2] + _State[2] = (temp & 0xEB) ^ (_State[3] & 0xBE) ^ (temp >> 4 & 0x0D) ^ (_State[3] >> 4 & 0x07) ^ (temp << 4 & 0xD0) ^ (_State[3] << 4 & 0x70) + _State[3] = (temp & 0xBE) ^ (_State[3] & 0xEB) ^ (temp >> 4 & 0x07) ^ (_State[3] >> 4 & 0x0D) ^ (temp << 4 & 0x70) ^ (_State[3] << 4 & 0xD0) + # M1 + temp = _State[4] + _State[4] = (temp & 0xEB) ^ (_State[5] & 0xBE) ^ (temp >> 4 & 0x0D) ^ (_State[5] >> 4 & 0x07) ^ (temp << 4 & 0xD0) ^ (_State[5] << 4 & 0x70) + _State[5] = (temp & 0xBE) ^ (_State[5] & 0xEB) ^ (temp >> 4 & 0x07) ^ (_State[5] >> 4 & 0x0D) ^ (temp << 4 & 0x70) ^ (_State[5] << 4 & 0xD0) + # M0 + temp = _State[6] + _State[6] = (temp & 0xD7) ^ (_State[7] & 0x7D) ^ (temp >> 4 & 0x0B) ^ (_State[7] >> 4 & 0x0E) ^ (temp << 4 & 0xB0) ^ (_State[7] << 4 & 0xE0) + _State[7] = (temp & 0x7D) ^ (_State[7] & 0xD7) ^ (temp >> 4 & 0x0E) ^ (_State[7] >> 4 & 0x0B) ^ (temp << 4 & 0xE0) ^ (_State[7] << 4 & 0xB0) + +def _MLayer(): + """ The adjusted linear layer which is utilized each regular round """ + _MPrimeLayer() + _ShiftRows() + +def _InvMLayer(): + """ The inverse adjusted linear layer which is utilized each inverse regular round """ + _InvShiftRows() + _MPrimeLayer() + +def _AddRoundConstant(round): + """ Function which simply applies a given round's constant to the state """ + global _State + _State = [ _State[i] ^ RC[8 * round + i] for i in range(8)] + +def _AddKey(ExtendedKey): + """ Function which adds k_1 to the state """ + global _State + _State = [ _State[i] ^ ExtendedKey[i + 16] for i in range(8) ] + + +if __name__ == "__main__": + ExtendedKey = ExtendKey(Key) + cipher(ExtendedKey) + print('0x', end='') + for i in range(8): + print(format(_State[i] >> 4 | (_State[i] << 4 & 0xF0), '02x'), end='') + print() \ No newline at end of file diff --git a/pt.dat b/pt.dat new file mode 100644 index 0000000..5689c26 Binary files /dev/null and b/pt.dat differ diff --git a/teat.py b/teat.py new file mode 100644 index 0000000..b96943b --- /dev/null +++ b/teat.py @@ -0,0 +1,25 @@ +G = Point( + x=0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, + y=0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, + curve=secp256k1) + +N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + +I = Point(x=None, y=None, curve=secp256k1) + +assert N * G == I + +pub = Point( + x=0x9577FF57C8234558F293DF502CA4F09CBC65A6572C842B39B366F21717945116, + y=0x10B49C67FA9365AD7B90DAB070BE339A1DAF9052373EC30FFAE4F72D5E66D053, + curve=secp256k1 +) +d: int = 2 ** 240 + 2 ** 31 +print(d * G) +print(pub) + +e = PrivateKey(randint(0, N)) +pub = e.secret * G +z = randint(0, 2 ** 256) +signature: Signature = e.sign(z) +assert signature.verify(z, pub) diff --git a/tmp.py b/tmp.py new file mode 100644 index 0000000..e86650d --- /dev/null +++ b/tmp.py @@ -0,0 +1,175 @@ + + +SBox = [0x0B, 0x0F, 0x03, 0x02, 0x0A, 0x0C, 0x09, 0x01, 0x06, 0x07, 0x08, 0x00, 0x0E, 0x05, 0x0D, 0x04] +# Inverse substitution box used on individual nibbles +InvSBox = [0x0B, 0x07, 0x03, 0x02, 0x0F, 0x0D, 0x08, 0x09, 0x0A, 0x06, 0x04, 0x00, 0x05, 0x0E, 0x0C, 0x01] + +RC = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x91, 0xa8, 0xe2, 0x30, 0x07, 0x37, 0x44, + 0x4a, 0x90, 0x83, 0x22, 0x92, 0xf9, 0x13, 0x0d, + 0x80, 0xe2, 0xaf, 0x89, 0xce, 0xe4, 0xc6, 0x98 +] + + +def cipher(ExtendedKey): + """ The complete PRINCE forward encryption on the 64-bit state performed through nibble calculations """ + global _State + + _State = [_State[i] ^ ExtendedKey[i] for i in range(8)] + + _AddKey(ExtendedKey) + _AddRoundConstant(0) + + + _SubNibbles() + _MLayer() + _AddRoundConstant(1) + _AddKey(ExtendedKey) + + _SubNibbles() + _MPrimeLayer() + _InvSubNibbles() + + _AddKey(ExtendedKey) + _AddRoundConstant(2) + _InvMLayer() + _InvSubNibbles() + + _AddRoundConstant(3) + _AddKey(ExtendedKey) + + _State = [_State[i] ^ ExtendedKey[i+8] for i in range(8)] + +def ExtendKey(Key): + """ PRINCE's version of a key schedule, which extends our 128-bit key to a 192-bit key """ + newKey = [0x00] * 24 + + for i in range(8): + # k_0 stays the same + newKey[i] = Key[i] + # k'_0 is a k_0 rotated right one bit and XORed with the last bit + newKey[i + 8] = (Key[i] >> 1) | (Key[(i + 1) % 8] << 7 & 0x80) + # k_1 stays the same + newKey[i + 16] = Key[i + 8] + + newKey[15] ^= (Key[7] & 0x10) + + return newKey + +def _ShiftRows(): + """ Helper method which distinguishes our two linear layers M from M' """ + global _State + + temp = _State[:] # copy the state into a temporary holder + perm = [0, 5, 2, 7, 4, 1, 6, 3] + + _State = [ (temp[perm[i]] & 0x0F) | (_State[perm[(i+2)%8]] & 0xF0) for i in range(8)] + + +def _InvShiftRows(): + """ Inverse of our ShiftRows() function which allows us to distinguish M'^-1 from M^-1 """ + global _State + + temp = _State[:] # copy the state into a temporary holder + perm = [0, 5, 2, 7, 4, 1, 6, 3] + + _State = [ (temp[perm[i]] & 0x0F) | (_State[perm[(i+6)%8]] & 0xF0) for i in range(8)] + + +def _SubNibbles(): + """ Send the state through a substitution layer nibble-by-nibble """ + global _State + _State = [ (SBox[_State[i] >> 4] << 4) | SBox[_State[i] & 0x0F] for i in range(8) ] + + +def _InvSubNibbles(): + """ Inverse of our substitution layer which sends each substituted nibble back to the original nibble """ + global _State + _State = [ (InvSBox[_State[i] >> 4] << 4) | InvSBox[_State[i] & 0x0F] for i in range(8) ] + + +def _MPrimeLayer(): + """ Our linear layer, designed to use as little space as possible and prevent wasted clock-cycles. Recall that this method is in fact its own inverse. """ + global _State + + # M0 + temp = _State[0] # we only need 1 storage variable here + _State[0] = (temp & 0xD7) ^ (_State[1] & 0x7D) ^ (temp >> 4 & 0x0B) ^ (_State[1] >> 4 & 0x0E) ^ (temp << 4 & 0xB0) ^ (_State[1] << 4 & 0xE0) + _State[1] = (temp & 0x7D) ^ (_State[1] & 0xD7) ^ (temp >> 4 & 0x0E) ^ (_State[1] >> 4 & 0x0B) ^ (temp << 4 & 0xE0) ^ (_State[1] << 4 & 0xB0) + # M1 + temp = _State[2] + _State[2] = (temp & 0xEB) ^ (_State[3] & 0xBE) ^ (temp >> 4 & 0x0D) ^ (_State[3] >> 4 & 0x07) ^ (temp << 4 & 0xD0) ^ (_State[3] << 4 & 0x70) + _State[3] = (temp & 0xBE) ^ (_State[3] & 0xEB) ^ (temp >> 4 & 0x07) ^ (_State[3] >> 4 & 0x0D) ^ (temp << 4 & 0x70) ^ (_State[3] << 4 & 0xD0) + # M1 + temp = _State[4] + _State[4] = (temp & 0xEB) ^ (_State[5] & 0xBE) ^ (temp >> 4 & 0x0D) ^ (_State[5] >> 4 & 0x07) ^ (temp << 4 & 0xD0) ^ (_State[5] << 4 & 0x70) + _State[5] = (temp & 0xBE) ^ (_State[5] & 0xEB) ^ (temp >> 4 & 0x07) ^ (_State[5] >> 4 & 0x0D) ^ (temp << 4 & 0x70) ^ (_State[5] << 4 & 0xD0) + # M0 + temp = _State[6] + _State[6] = (temp & 0xD7) ^ (_State[7] & 0x7D) ^ (temp >> 4 & 0x0B) ^ (_State[7] >> 4 & 0x0E) ^ (temp << 4 & 0xB0) ^ (_State[7] << 4 & 0xE0) + _State[7] = (temp & 0x7D) ^ (_State[7] & 0xD7) ^ (temp >> 4 & 0x0E) ^ (_State[7] >> 4 & 0x0B) ^ (temp << 4 & 0xE0) ^ (_State[7] << 4 & 0xB0) + +def _MLayer(): + """ The adjusted linear layer which is utilized each regular round """ + _MPrimeLayer() + _ShiftRows() + +def _InvMLayer(): + """ The inverse adjusted linear layer which is utilized each inverse regular round """ + _InvShiftRows() + _MPrimeLayer() + +def _AddRoundConstant(round): + """ Function which simply applies a given round's constant to the state """ + global _State + _State = [ _State[i] ^ RC[8 * round + i] for i in range(8)] + +def _AddKey(ExtendedKey): + """ Function which adds k_1 to the state """ + global _State + _State = [ _State[i] ^ ExtendedKey[i + 16] for i in range(8) ] + + +if __name__ == "__main__": + # _State = [0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE] + # Key = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01] + + # ExtendedKey = ExtendKey(Key) + # cipher(ExtendedKey) + # print('0x', end='') + # for i in range(8): + # print(format(_State[i] >> 4 | (_State[i] << 4 & 0xF0), '02x'), end='') + # print() + + plainfile = open("pt.dat", "rb") + cipherfile = open("ct.dat", "rb") + + key = [] + for i in range(16): + key.append(bytearray(8)) + + active = [15,14,13,12,11] + for t in range(5): + # for each SET + A = [] + for i in range(16): + A.append(bytearray(16)) + + for j in range(t*16,(t+1)*16): + pbyte = plainfile.read(8) + cbyte = cipherfile.read(8) + pbytearay = [((a&0x0F)<<4)|((a&0xF0)>>4) for a in list(pbyte)][::-1] + cbytearay = [((a&0x0F)<<4)|((a&0xF0)>>4) for a in list(cbyte)][::-1] + nibarray = [] + for dibble in [(str(hex(i))).replace('0x','') for i in cbytearay]: + dibble = dibble.zfill(2) + nibarray.append(dibble[0]) + nibarray.append(dibble[1]) + + for i in range(16): + x = int('0x'+str(nibarray[i]),16) + A[i][x] = 1 + + for i in range(16): + print([[a[0:4],a[4:8],a[8:12],a[12:16]] for a in A]) \ No newline at end of file