s5.md (19453B)
1 #### 5.33 2 3 The basic Diffie-Hellman algorithm for key exchange between Alice and 4 Bob goes as follows. Alice and Bob agree on a finite cyclic group of 5 some particular order to use. They each randomly and independently pick 6 some number of times to perform the group operation (which must be 7 greater than zero, and less than the order of the group) and perform the 8 group operation on the generator that number of times, publishing their 9 results. For generator g, Alice's number x, and Bob's number y: Alice 10 publishes g ^ x, and Bob publishes g ^ y. 11 12 Each then performs his or her own secret number of additional group 13 operations on the other's public result, establishing the key g ^ xy. 14 Since the discrete logarithm problem is hard, an eavesdropper can't (for 15 an appropriate group) determine how many times either has performed the 16 individual group operations, even given g; he can only naïvely calculate 17 g ^ (x + y). 18 19 For the initial illustration here we're using a decidedly insecure 20 group, i.e. the multiplicative group of 16-bit words modulo 37: 21 22 > gen <- MWC.create 23 > let p = 37 24 > let g = 5 25 > a <- fmap (`mod` p) (MWC.uniformR (1, p - 1) gen) :: IO Word16 26 > b <- fmap (`mod` p) (MWC.uniformR (1, p - 1) gen) :: IO Word16 27 > let bigA = g ^ a `mod` p 28 > let bigB = g ^ b `mod` p 29 > let s = bigB ^ a `mod` p 30 > let t = bigA ^ b `mod` p 31 > s == t 32 True 33 > let k = S.sha1 . BP.runPut $ BP.putWord16be s 34 > k 35 ace6f761db204030c1a65c0930bd01fd55ecc429 36 37 For the big guns, we can pull out GHC's Natural type, for which 38 mwc-random (our preferred random library) can generate something in a 39 range. First a modular exponentiation routine: 40 41 -- modified from https://gist.github.com/trevordixon/6788535 42 modexp :: Natural -> Natural -> Natural -> Natural 43 modexp b e m 44 | e == 0 = 1 45 | otherwise = 46 let t = if B.testBit e 0 then b `mod` m else 1 47 in t * modexp ((b * b) `mod` m) (B.shiftR e 1) m `mod` m 48 49 and given that (and appropriate p, g), the key exchange: 50 51 > gen <- MWC.create 52 > a <- fmap (`mod` p) (MWC.uniformRM (1, p - 1) gen) 53 > b <- fmap (`mod` p) (MWC.uniformRM (1, p - 1) gen) 54 > let bigA = modexp g a p 55 > let bigB = modexp g b p 56 > let s = modexp bigB a p 57 > let t = modexp bigA b p 58 > s == t 59 True 60 61 That's all well and good, but let's have a bit of fun. 62 63 Cryptopals.DH implements the Diffie-Hellman protocol over TCP. Two 64 functions, 'bob' and 'alice', will initiate a TCP server and client, 65 respectively. Each takes as argument a port to bind to and a protocol to 66 follow, with Alice also taking an argument specifying the initial action 67 she'll take. The two will then perform the specified protocol. 68 69 The 'dh' protocol specifies Diffie-Hellman, and for the lulz Alice and 70 Bob will exchange a message, AES128-encrypted with the shared key, à la 71 the initial illustration in the next challenge. 72 73 Opening two instances of GHCi, we can run e.g.: 74 75 > bob "3000" dh 76 77 in one, and: 78 79 > alice "3000" dh 80 81 in the other and watch the logs for fun. Here I'll interleave the 82 relevant parts of the logs for readability: 83 84 (cryptopals) bob: listening.. 85 (cryptopals) alice: session established 86 (cryptopals) alice: sending group parameters and public key 87 (cryptopals) bob: received group parameters and public key 88 (cryptopals) bob: sending public key 89 (cryptopals) alice: received public key 90 (cryptopals) alice: sending ciphertext fQwcd2smXxBojyEDLrIdySNLAV3UHhl8/X2F/x4FIb0= 91 (cryptopals) bob: received ciphertext fQwcd2smXxBojyEDLrIdySNLAV3UHhl8/X2F/x4FIb0= 92 (cryptopals) bob: decrypted ciphertext: "attack at 10pm" 93 (cryptopals) bob: replying with ciphertext NaObgnz4rNShMKRGdGv+OGnI2gZnWOoXuYmCZhlcyymRbHPaprFVOz4Ls5eH9y/W 94 (cryptopals) alice: received ciphertext NaObgnz4rNShMKRGdGv+OGnI2gZnWOoXuYmCZhlcyymRbHPaprFVOz4Ls5eH9y/W 95 (cryptopals) alice: decrypted ciphertext: "confirmed, attacking at 10pm" 96 (cryptopals) bob: ending session 97 98 #### 5.34 99 100 If B = p in s = B ^ a mod p, then s = p ^ a mod p, which is zero for any 101 'a' in the group. Our shared key is thus going to be the first 16 bytes 102 of the SHA1 hash of an appropriately-serialized 0x00. 103 104 Cryptopals.DH includes a 'mallory' agent that requires a port to listen 105 on, a port to bind to, and a protocol to follow. By using the 'dhmitm' 106 protocol, we get our man-in-the-middle attack on Alice and Bob's DH key 107 exchange. 108 109 You can get this going by opening three GHCi's, then launching e.g.: 110 111 > bob "3000" dh 112 113 in one, then: 114 115 > mallory "3001" "3000" dhmitm 116 117 in another, and finally: 118 119 > alice "3001" dh sendParams 120 121 in the third. Again, I'm interleaving the logs for readability: 122 123 (cryptopals) bob: listening.. 124 (cryptopals) mallory: LiSteNIng.. 125 (cryptopals) alice: session established 126 (cryptopals) mallory: eStabLisHed coNNecTion 127 (cryptopals) alice: sending group parameters and public key 128 (cryptopals) mallory: reCEiVed GRoUp pArAmeTErs And pUBliC kEy 129 (cryptopals) mallory: sEnDinG BOguS paRaMeTeRs 130 (cryptopals) bob: received group parameters and public key 131 (cryptopals) bob: sending public key 132 (cryptopals) mallory: REceIvED pUBlic keY 133 (cryptopals) mallory: seNDINg boGus kEy 134 (cryptopals) alice: received public key 135 (cryptopals) alice: sending ciphertext tM4Y5fpafrsf9+A4UB5UaudkAVzwMtjsjDIwShPKHcU= 136 (cryptopals) mallory: rECeIveD CiPHeRTexT tM4Y5fpafrsf9+A4UB5UaudkAVzwMtjsjDIwShPKHcU= 137 (cryptopals) mallory: DEcRyptEd cIPheRTeXt: "attack at 10pm" 138 (cryptopals) mallory: reLayINg cIpheRtExt 139 (cryptopals) bob: received ciphertext tM4Y5fpafrsf9+A4UB5UaudkAVzwMtjsjDIwShPKHcU= 140 (cryptopals) bob: decrypted ciphertext: "attack at 10pm" 141 (cryptopals) bob: replying with ciphertext ux4PoPTCS7pz5H4IQ11AuZkMBHmEcT9Waz68y/a9nggIY38Z6mbwSrCwNO3OKcDQ 142 (cryptopals) mallory: reCeiVeD CipHeRtExt ux4PoPTCS7pz5H4IQ11AuZkMBHmEcT9Waz68y/a9nggIY38Z6mbwSrCwNO3OKcDQ 143 (cryptopals) mallory: DeCrYpteD cIphErteXt: "confirmed, attacking at 10pm" 144 (cryptopals) mallory: ReLaYINg CiPHeRTexT 145 (cryptopals) alice: received ciphertext ux4PoPTCS7pz5H4IQ11AuZkMBHmEcT9Waz68y/a9nggIY38Z6mbwSrCwNO3OKcDQ 146 (cryptopals) alice: decrypted ciphertext: "confirmed, attacking at 10pm" 147 148 #### 5.35 149 150 Cryptopals.DH.dhng implements the negotiated-groups DH protocol, so that 151 can be run by firing off e.g.: 152 153 > bob "3000" dhng 154 155 in one GHCi session, and: 156 157 > alice "3000" dhng sendGroup 158 159 in the other. In the meantime, we can figure out the outcomes of using 160 the different malicious group parameters analytically. 161 162 For g = 1, the MITM attack starts as follows: 163 164 alice sends p, g 165 bob gets p, 1 166 167 If Bob receives g = 1, then Mallory knows his public key will equal 1 ^ 168 b mod p = 1, as will the shared key from Alice's perspective (since 1 ^ 169 a mod p = 1). Mallory thus needs to forward a 1 as Alice's public key in 170 order for Bob to agree on the shared key. 171 172 For g = p, Bob computes B = p ^ b mod p = 0, so Mallory can forward p as 173 Alice's public key in order for them to agree on the shared key. 174 175 Finally, the case of g = p - 1. Note that for any p > 1 and any even b, we 176 have (for appropriate coefficients a, c, etc.): 177 178 (p - 1) ^ b mod p 179 = (p^b + .. + ap^2 + cp + 1) mod p 180 = (p^b mod p + .. + ap^2 mod p + cp mod p + 1 mod p) mod p 181 = (0 + .. + 0 + 1 mod p) mod p 182 = 1 183 184 whereas for any odd b, we have: 185 186 (p - 1) ^ b mod p 187 = (p^b - .. - cp^2 + dp - 1) mod p 188 = (p^b mod p - .. - ap^2 mod p + cp mod p - 1 mod p) mod p 189 = (0 + .. + 0 - 1 mod p) mod p 190 = p - 1. 191 192 So Bob's public key will be either 1 or p - 1 depending on whether his 193 secret key is even or odd. Alice will thus compute: 194 195 s = B ^ a mod p 196 = 1 } b even or a even (p = 3/4) 197 p - 1 } b odd and a odd. (p = 1/4). 198 199 If Mallory then forwards A = g = p - 1 to Bob, we have: 200 201 t = A ^ b mod p 202 = 1 } b even (p = 1/2) 203 = p - 1 } b odd (p = 1/2). 204 205 This all yields the following table: 206 207 a 208 even odd 209 even s = 1, t = 1 s = 1, t = 1 210 b 211 odd s = 1, t = p - 1 s = p - 1, t = p - 1 212 213 such that Alice and Bob will agree on the shared key with probability 214 3/4. Mallory also has to choose which shared key value he uses; if he 215 uses 1, then the attack succeeds with probability 1/2, and if he uses 216 p - 1, then it succeeds with probability 1/4. 217 218 Here are the interleaved logs of a successful attack. Start mallory with 219 e.g. the `dhngmitm 1`, `dhngmitm p`, or `dhngmitm (p - 1)` protocol: 220 221 (cryptopals) bob: listening.. 222 (cryptopals) mallory: LiSteNIng.. 223 (cryptopals) alice: session established 224 (cryptopals) mallory: eStabLisHed MiTm coNNecTion 225 (cryptopals) alice: sending group parameters 226 (cryptopals) mallory: reCEiVed GRoUp pArAmeTErs 227 (cryptopals) mallory: sEnDinG BOguS GRoUp paRaMeTeRs 228 (cryptopals) bob: received group parameters 229 (cryptopals) bob: acking group parameters 230 (cryptopals) mallory: rECeiVed aCK 231 (cryptopals) mallory: ReLaYINg ACk 232 (cryptopals) alice: received ack 233 (cryptopals) alice: sending public key 3f44f49421cbb3b2ed40aa8f068236affba15335 234 (cryptopals) mallory: REceIvED pUBlic keY 3f44f49421cbb3b2ed40aa8f068236affba15335 235 (cryptopals) mallory: SeNDing BoGuS kEy d14952314d5de233ef0dd0a178617f7f07ea082c 236 (cryptopals) bob: received public key d14952314d5de233ef0dd0a178617f7f07ea082c 237 (cryptopals) bob: sending public key 238 (cryptopals) mallory: REceIvED pUBlic keY d14952314d5de233ef0dd0a178617f7f07ea082c 239 (cryptopals) mallory: ReLAyINg pUbliC KeY d14952314d5de233ef0dd0a178617f7f07ea082c 240 (cryptopals) alice: received public key d14952314d5de233ef0dd0a178617f7f07ea082c 241 (cryptopals) alice: sending ciphertext +nbU0t3nLX3WmKoY3+pdmilVcd2I6fJfGuC3RTn0h5E= 242 (cryptopals) mallory: rECeIveD CiPHeRTexT +nbU0t3nLX3WmKoY3+pdmilVcd2I6fJfGuC3RTn0h5E= 243 (cryptopals) mallory: DEcRyptEd cIPheRTeXt: "attack at 10pm" 244 (cryptopals) mallory: reLayINg cIpheRtExt 245 (cryptopals) bob: received ciphertext +nbU0t3nLX3WmKoY3+pdmilVcd2I6fJfGuC3RTn0h5E= 246 (cryptopals) bob: decrypted ciphertext: "attack at 10pm" 247 (cryptopals) bob: replying with ciphertext 3i7fLAZXJv7+cr3qrI8KDKhfe6FpJq62yVtaCt9dlrUodMiRVtJ7ZmKtJ8ku0r4x 248 (cryptopals) mallory: reCeiVeD CipHeRtExt 3i7fLAZXJv7+cr3qrI8KDKhfe6FpJq62yVtaCt9dlrUodMiRVtJ7ZmKtJ8ku0r4x 249 (cryptopals) mallory: DeCrYpteD cIphErteXt: "confirmed, attacking at 10pm" 250 (cryptopals) mallory: ReLaYINg CiPHeRTexT 251 (cryptopals) alice: received ciphertext 3i7fLAZXJv7+cr3qrI8KDKhfe6FpJq62yVtaCt9dlrUodMiRVtJ7ZmKtJ8ku0r4x 252 (cryptopals) alice: decrypted ciphertext: "confirmed, attacking at 10pm" 253 (cryptopals) mallory: ending session 254 255 #### 5.36 256 257 SRP (Secure Remote Password) is an authentication protocol for which 258 a client authenticates with a server via a zero-knowledge proof. 259 Cryptopals.SRP implements it much in the same way that Cryptopals.DH 260 implements Diffie-Hellman; here one can perform the protocol via the 261 'server' and 'client' functions analogously. 262 263 Some interleaved logs for 'server "3000" srp' and 'client "3000" srp 264 auth': 265 266 (cryptopals) server: listening.. 267 (cryptopals) client: session established 268 (cryptopals) client: sending authentication request 269 (cryptopals) server: received authentication request for l33th4x0r@hotmail.com 270 (cryptopals) server: acking authentication request for l33th4x0r@hotmail.com 271 (cryptopals) client: received authentication request ack 272 (cryptopals) client: sending MAC 6p7eE/pTSijdReePtswOKDZZUFYhLkJfeKps0GD4Yc4= 273 (cryptopals) server: received MAC 6p7eE/pTSijdReePtswOKDZZUFYhLkJfeKps0GD4Yc4= 274 (cryptopals) server: OK 275 276 #### 5.37 277 278 If the client forwards A = 0 (or anything congruent modulo N to 0) as 279 its public key, then the server will compute S = 0 as its shared secret. 280 Whoops! The client can then just pass along the appropriate MAC to 281 authenticate. 282 283 Example, with the client using the 'srpzero' protocol and 'authZero' 284 initial action: 285 286 -- GHCi instance one 287 > server "3000" srp 288 -- GHCi instance two 289 > client "3000" srpZero authZero 290 (cryptopals) server: listening.. 291 (cryptopals) client: session established 292 (cryptopals) client: sending authentication request with a zero key 293 (cryptopals) server: received authentication request for l33th4x0r@hotmail.com 294 (cryptopals) server: acking authentication request for l33th4x0r@hotmail.com 295 (cryptopals) client: received authentication request ack 296 (cryptopals) client: sending MAC 5xO9hEUJOTX5EIU+DmYV0QOs1L1oVp3fphREooN/8L4= 297 (cryptopals) server: received MAC 5xO9hEUJOTX5EIU+DmYV0QOs1L1oVp3fphREooN/8L4= 298 (cryptopals) server: OK 299 300 #### 5.38 301 302 The simplified protocol can be run with the 'server' and 'client' 303 functions in Cryptopals.SRP.Simple. 304 305 For the MITM attack, the idea is that, posing as the server, Mallory has 306 control over the parameters 'salt', 'b', 'B', and 'u', but doesn't know 307 anything to do with 'x', and so has to guess at that. 308 309 If Mallory supplies salt = mempty, B = g mod n, and u = 1, then the 310 client will compute: 311 312 S = g ^ (a + x) mod n 313 314 and forward him MAC = HMAC-SHA256(SHA256(S), mempty). Duly supplied with 315 the client's public key ('A') and MAC, and using a trivial b = 1 as a 316 secret key, Mallory can guess x = SHA256(password) to compute: 317 318 S' = (A v) mod n 319 = (A g ^ x) mod n 320 321 and then check if HMAC-SHA256(SHA256(S'), mempty) = MAC. If it verifies, 322 then he knows the password. 323 324 To not make this too annoying, I'll draw the password to be cracked from 325 /usr/share/dict/words. Once Mallory provides the public key and MAC 326 from the client, we'll generate our dictionary and check if the MAC is 327 present in the keyspace using a compiled, optimized binary. 328 329 Here's a run of the MITM protocol (`mallory "3000" mitm` and `client 330 "3000" srpsimple auth`): 331 332 (cryptopals) mallory: LiSteNiNG.. 333 (cryptopals) client: session established 334 (cryptopals) client: sending authentication request 335 (cryptopals) mallory: rECeIvEd aUTheNtICaTioN ReQUesT fOr l33th4x0r@hotmail.com 336 (cryptopals) mallory: wiTh PuBLiC kEy 4992116105881074929461308645820763003777270799868975573291 337 (cryptopals) mallory: aCKiNg AuTheNTicAtIon ReQueST FOr l33th4x0r@hotmail.com 338 (cryptopals) client: received authentication request ack 339 (cryptopals) client: sending MAC f20ac41224b4054d2f89a7c319ed5bf3f8bb68cf4169f620f45e49acb4dd179c 340 (cryptopals) mallory: rECeIvEd MAC f20ac41224b4054d2f89a7c319ed5bf3f8bb68cf4169f620f45e49acb4dd179c 341 (cryptopals) mallory: USiNg PaRaMeTeRs 4992116105881074929461308645820763003777270799868975573291 aNd f20ac41224b4054d2f89a7c319ed5bf3f8bb68cf4169f620f45e49acb4dd179c 342 (cryptopals) mallory: GoINg ofFLinE.. 343 344 Now taking those parameters to the `offline-dictionary-attack` binary we get 345 the result pretty quickly: 346 347 $ PK="4992116105881074929461308645820763003777270799868975573291" 348 $ MAC="f20ac41224b4054d2f89a7c319ed5bf3f8bb68cf4169f620f45e49acb4dd179c" 349 $ offline-dictionary-attack "$PK" "$MAC" 350 (cryptopals) success 351 (cryptopals) password: omniana 352 353 #### 5.39 354 355 A note on primegen for RSA: I didn't bother with it, as recommended, but 356 looked at how it should be done. It seems straightforward; one generates 357 a sufficiently large random number, then tests that it isn't divisible 358 by the first several hundred primes, then performs a probabilistic 359 primality test sufficiently many times that the error probability is 360 very small. A reference suggested that the error probability should be 361 less than 1 / 2^128. 362 363 I used cryptonite's Crypto.Number.Prime module, which implements the 364 above procedure. 365 366 In any case, RSA: one finds two k-bit primes, 'p' and 'q', and uses 367 their product to construct a public modulus n = pq and value 368 `t = (p - 1) (q - 1)`. The public key is (n, e) for 'e' a number 369 relatively prime to 't', and the private key is (n, d), for d such that 370 `ed = 1 mod t` (i.e., 'd' is congruent mod 't' to the inverse of 'e'). 371 "Relatively prime" or "coprime" means, for two numbers 'a' and 'b', that 372 they have a greatest common denominator of 1. 373 374 Encryption and decryption are then just modular exponentiation 375 operations using the keys. To go from Natural to ByteString and back, 376 I used some old functions -- roll and unroll -- that I wrote for 377 [urbit-hob](http://git.jtobin.io/urbit-hob) a few years back (though 378 actually I think I cribbed them from the Data.Binary internals): 379 380 data Key = Key Natural Natural 381 deriving (Eq, Show) 382 383 data Keypair = Keypair { 384 sec :: Key 385 , pub :: Key 386 } deriving (Eq, Show) 387 388 keygen :: Int -> IO Keypair 389 keygen siz = loop where 390 loop = do 391 p <- fromIntegral <$> P.generatePrime siz 392 q <- fromIntegral <$> P.generatePrime siz 393 let n = p * q 394 et = pred p * pred q 395 e = 3 396 md = modinv e et 397 case md of 398 Nothing -> loop 399 Just d -> pure $ Keypair (Key d n) (Key e n) 400 401 encrypt :: Key -> BS.ByteString -> BS.ByteString 402 encrypt (Key e n) m = unroll (DH.modexp (roll m) e n) 403 404 decrypt :: Key -> BS.ByteString -> BS.ByteString 405 decrypt = encrypt 406 407 Works fine: 408 409 > Keypair sec pub <- keygen 1024 410 > let cip = encrypt pub "secret!" 411 > TIO.putStrLn $ T.take 32 $ B64.encodeBase64 cip 412 zZFjaw7BR6rP0EaNsTFRnCInwsghANrE 413 > let msg = decrypt sec cip 414 > msg 415 "secret!" 416 417 The Cryptopals.RSA module exports the keygen, encrypt, and decrypt 418 functions, as well as modinv and roll & unroll. 419 420 #### 5.40 421 422 The problem behind the Chinese Remainder Theorem, as formulated by one 423 孫子 (not *that* 孫子) and passed on to me via Wikipedia, is: 424 425 > There are certain things whose number is unknown. If we count them by 426 > threes, we have two left over; by fives, we have three left over; and 427 > by sevens, two are left over. How many things are there? 428 429 I.e. what is x, if x is congruent to 2 mod 3, 3 mod 5, and 2 mod 7? 430 431 In Sunzi's case the answer is congruent to 23 mod 105, and the Chinese 432 Remainder Theorem states that such a solution always exists given 433 certain conditions (notably that the moduli are pairwise coprime). 434 435 In our case, for plaintext 'm' and ciphertexts c0, c1, and c2, we have 436 that m ^ 3 is congruent to c0 mod n0, c1 mod n1, and c2 mod n2. The 437 Chinese Remainder Theorem asserts that there exists some 'c' that is 438 congruent to m ^ 3 modulo `n0 * n1 * n2`. The trick here (this is known 439 as Håstad's attack, apparently) is that since m is smaller than every n, 440 we have that m ^ 3 is smaller than `n0 * n1 * n2`, and so that 'c' is 441 precisely equal to m ^ 3, rather than congruent to it. 442 443 So, following the CRT construction: 444 445 > let msg = "attack at 10pm" 446 > 447 > Keypair _ p0@(Key e0 n0) <- keygen 1024 448 > Keypair _ p1@(Key e1 n1) <- keygen 1024 449 > Keypair _ p2@(Key e2 n2) <- keygen 1024 450 > 451 > let c0 = encrypt p0 msg 452 > let c1 = encrypt p1 msg 453 > let c2 = encrypt p2 msg 454 > 455 > let ms0 = n1 * n2 456 > let ms1 = n0 * n2 457 > let ms2 = n0 * n1 458 > 459 > :{ 460 > let res = (roll c0 * ms0 * modinv' ms0 n0 461 > + roll c1 * ms1 * modinv' ms1 n1 462 > + roll c2 * ms2 * modinv' ms2 n2) 463 > `mod` 464 > (n0 * n1 * n2) 465 > :} 466 467 The 'integer-roots' package provides a helpful cube root function: 468 469 > unroll $ R.integerCubeRoot res 470 "attack at 10pm" 471