def run(self): assert self.outbox is not None self.outbox.put((self.mel, self.KA)) salt, B = self.inbox.get() u = int.from_bytes(SHA256.new(int_to_bytes(self.KA)+int_to_bytes(B)).digest(), 'big') # using the salt, retrieve the server db entry key Kx = int.from_bytes(SHA256.new(salt+self.pwd).digest(), 'big') KX = pow(self.g, Kx, self.N) sec = pow(B-self.k*KX, self.Ka+u*Kx, self.N) # sec = (B-k*KX)^(Ka+u*Kx) = KB^(Ka+u*Kx) = g^(Kb*(Ka+u*Kx)) key = SHA256.new(int_to_bytes(sec)).digest() print('Client: key', key.hex()) mac = HMAC.new(key, salt, SHA256).digest() self.outbox.put(mac) ok = self.inbox.get() assert ok == b'OK' print('Client: ok, done')
def run(self): assert self.outbox is not None # get client id and retrieve its entry mel, KA = self.inbox.get() assert mel in self.DB salt, KX = self.DB[mel] # B = k*KX + KB # linear mix of the pwd "pub." key with the server "pub." key B = (self.k*KX + self.KB) % self.N self.outbox.put((salt, B)) u = int.from_bytes(SHA256.new(int_to_bytes(KA)+int_to_bytes(B)).digest(), 'big') sec = pow(KA*pow(KX, u, self.N), self.Kb, self.N) # sec = g^((Ka+Kx*u)*Kb) key = SHA256.new(int_to_bytes(sec)).digest() print('Server: key', key.hex()) mac = self.inbox.get() HMAC.new(key, salt, SHA256).verify(mac) print('Server: ok') self.outbox.put(b'OK') print('Server: done')
def run(self): assert self.outbox is not None p, g, KA = self.inbox.get() Kb, KB = dhlib.gen_key(p, g) self.outbox.put(KB) s = pow(KA, Kb, p) key = SHA1.new(int_to_bytes(s)).digest()[:BS] ciph, iv = self.inbox.get() msg = unpad(AES.new(key, AES.MODE_CBC, iv).decrypt(ciph), BS) print('Bob: ', msg) iv = get_random_bytes(BS) ciph = AES.new(key, AES.MODE_CBC, iv).encrypt(pad(msg, BS)) self.outbox.put((ciph, iv)) print('Bob: done')
def run(self): assert self.outbox is not None self.outbox.put((self.p, self.g, self.KA)) KB = self.inbox.get() s = pow(KB, self.Ka, self.p) key = SHA1.new(int_to_bytes(s)).digest()[:BS] iv = get_random_bytes(BS) msg = b'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' ciph = AES.new(key, AES.MODE_CBC, iv).encrypt(pad(msg, BS)) self.outbox.put((ciph, iv)) ciph, iv = self.inbox.get() msgB = unpad(AES.new(key, AES.MODE_CBC, iv).decrypt(ciph), BS) assert msg == msgB print('Alice: msg ok, done')
def attack3_active(self, ciphA, ivA): sA = self.p - self.s keyA = SHA1.new(int_to_bytes(sA)).digest()[:BS] print("Eve: Alice's key", keyA.hex()) msg = unpad(AES.new(keyA, AES.MODE_CBC, ivA).decrypt(ciphA), BS) print('Eve: ', msg) ciph = AES.new(self.key, AES.MODE_CBC, ivA).encrypt(pad(msg, BS)) self.outB.put((ciph, ivA)) ciph, iv = self.inB.get() msg = unpad(AES.new(self.key, AES.MODE_CBC, iv).decrypt(ciph), BS) print('Eve: ', msg) ciphA = AES.new(keyA, AES.MODE_CBC, iv).encrypt(pad(msg, BS)) self.outA.put((ciphA, iv)) print('Eve: done')
def run(self): assert self.outbox is not None self.outbox.put((self.p, self.g)) assert self.inbox.get() == 'ACK' self.outbox.put(self.KA) KB = self.inbox.get() s = pow(KB, self.Ka, self.p) key = SHA1.new(int_to_bytes(s)).digest()[:BS] print('Alice: key', key.hex()) iv = get_random_bytes(BS) msg = b'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.' ciph = AES.new(key, AES.MODE_CBC, iv).encrypt(pad(msg, BS)) self.outbox.put((ciph, iv)) ciph, iv = self.inbox.get() msgB = unpad(AES.new(key, AES.MODE_CBC, iv).decrypt(ciph), BS) assert msg == msgB print('Alice: msg ok, done')
def attack3(self): print('Eve: attack 3, g = p-1') # In this case, KB = ±1 depending on the parity of Kb. # If Kb is even, then KB = 1, s_A = 1 and we can # arbitrarily inject KA_B = ±1. # If Kb is odd, KB = -1, s_A = ±1 depending on the # parity of Ka, and we should inject KA_B = s_A. # Prop.: If g is a generator of Zₚ*, # Ka is even <=> KA (= g^Ka) is a square mod p # => trivial # <= KA = x², ∃n / x = gⁿ, KA = g²ⁿ = g^Ka, # hence Ka = 2n mod p-1, with p-1 even, # hence Ka is even # In that case, we can use Euler's criterion to test # whether KA is a square (and then choose KA_B = ±1). # https://en.wikipedia.org/wiki/Euler%27s_criterion # Problem: In practice, the given g = 2, probably for # optimized computation purposes, and is NOT a primitive # root. Even worse, its order might be odd, in which case # two Ka of different parity might generate the same KA # but different secrets when given KB = -1. # Solution: We pick KA_B at random, there is a 1/4 chance # to fail (Kb odd & bad choice), in which case we detect # if by a padding error and switch to a fully active # attack (as A and B do not share the same key). self.outB.put((self.p, self.p - 1)) # g_B := p-1 (= -1) ack = self.inB.get() assert ack == 'ACK' self.outA.put(ack) KA = self.inA.get() # primitive root deterministic case: #even_Ka = pow(KA, (p-1)//2, p)==1 # randomized case: even_Ka = randint(0, 1) == 0 self.outB.put(1 if even_Ka else self.p - 1) KB = self.inB.get() assert KB == 1 or KB == self.p - 1 self.outA.put(KB) self.s = 1 if KB == 1 or even_Ka else self.p - 1 self.key = SHA1.new(int_to_bytes(self.s)).digest()[:BS]