Attacks.hs (6465B)
1 module Cryptopals.RSA.Attacks ( 2 e3BroadcastAttack 3 ) where 4 5 import Control.Monad (forever, when) 6 import Control.Monad.Primitive 7 import Control.Monad.IO.Class 8 import Control.Monad.Trans.Class 9 import Control.Monad.Trans.State 10 import qualified Cryptopals.DH as DH 11 import Cryptopals.RSA 12 import qualified Cryptopals.Digest.Pure.SHA as CS 13 import qualified Data.Bits as B 14 import qualified Data.ByteString as BS 15 import qualified Data.ByteString.Base16 as B16 16 import qualified Data.ByteString.Char8 as B8 17 import qualified Data.ByteString.Lazy as BL 18 import qualified Data.Maybe as M 19 import qualified Data.HashSet as HS 20 import qualified Data.Text as T 21 import qualified Data.Text.Encoding as TE 22 import qualified Data.Text.IO as TIO 23 import qualified Math.NumberTheory.Roots as R 24 import Numeric.Natural 25 import Pipes 26 import qualified Pipes.Prelude as P 27 import qualified System.Random.MWC as MWC 28 29 -- e=3 broadcast attack 30 31 e3BroadcastAttack 32 :: (BS.ByteString, Key) -- ciphertext / pubkey 33 -> (BS.ByteString, Key) -- ciphertext / pubkey 34 -> (BS.ByteString, Key) -- ciphertext / pubkey 35 -> BS.ByteString -- plaintext 36 e3BroadcastAttack (c0, p0) (c1, p1) (c2, p2) = case (p0, p1, p2) of 37 (Pub _ n0, Pub _ n1, Pub _ n2) -> 38 let ms0 = n1 * n2 39 ms1 = n0 * n2 40 ms2 = n0 * n1 41 42 s = roll c0 * ms0 * modinv' ms0 n0 43 + roll c1 * ms1 * modinv' ms1 n1 44 + roll c2 * ms2 * modinv' ms2 n2 45 46 c = s `mod` (n0 * n1 * n2) 47 48 in unroll (R.integerCubeRoot c) 49 50 _ -> error "e3BroadcastAttack: require public keys" 51 52 -- unpadded message recovery oracle 53 54 type Digests = HS.HashSet Integer 55 56 umrClient :: MonadIO m => Key -> Producer BS.ByteString m () 57 umrClient pub = case pub of 58 Sec {} -> error "umrClient: need public key" 59 Pub {} -> do 60 liftIO $ do 61 TIO.putStrLn "(cryptopals) umr-oracle: running with public key" 62 TIO.putStrLn (T.pack $ show pub) 63 forever $ do 64 lin <- liftIO $ do 65 TIO.putStrLn "(cryptopals) umr-oracle: awaiting hex-encoded input" 66 BS.getLine 67 yield (B16.decodeBase16Lenient lin) 68 69 umrServer :: Key -> Consumer BS.ByteString (StateT Digests IO) () 70 umrServer sec = case sec of 71 Pub {} -> error "umrServer: need secret key" 72 Sec {} -> forever $ do 73 cip <- await 74 digests <- lift get 75 76 let has = CS.integerDigest . CS.sha512 $ BL.fromStrict cip 77 78 if HS.member has digests 79 then liftIO $ TIO.putStrLn "(cryptopals) umr-oracle: rejecting request" 80 else do 81 lift $ modify (HS.insert has) 82 let msg = decrypt sec cip 83 liftIO $ do 84 TIO.putStrLn "(cryptopals) umr-oracle: decrypted text" 85 TIO.putStrLn (B16.encodeBase16 msg) 86 87 umrOracle :: Keypair -> Effect (StateT Digests IO) () 88 umrOracle (Keypair sec pub) = umrClient pub >-> umrServer sec 89 90 umrperturb 91 :: Key 92 -> BS.ByteString -- original ciphertext 93 -> MWC.Gen RealWorld 94 -> IO (Natural, BS.ByteString) -- (random s, perturbed ciphertext) 95 umrperturb key cip gen = case key of 96 Sec {} -> error "umrperturb: need public key" 97 Pub e n -> do 98 s <- MWC.uniformRM (1, n - 1) gen 99 let c = roll cip 100 c' = (DH.modexp s e n * c) `mod` n 101 pure (s, unroll c') 102 103 umrrecover 104 :: Key 105 -> Natural 106 -> BS.ByteString 107 -> BS.ByteString 108 umrrecover key s msg = case key of 109 Sec {} -> error "umrrecover: need public key" 110 Pub e n -> unroll $ (roll msg `mod` n * modinv' s n) `mod` n 111 112 -- bleichenbacher's e=3 signature forgery 113 114 fencode :: Natural -> BS.ByteString -> BS.ByteString 115 fencode mod msg = 116 let has = BL.toStrict $ CS.bytestringDigest (CS.sha512 (BL.fromStrict msg)) 117 len = bitl mod `quot` 8 118 pad = 119 BS.cons 0x00 120 . BS.cons 0x01 121 . BS.cons 0xff 122 $ BS.cons 0x00 asnSha512 123 vil = pad <> has 124 in vil <> BS.replicate (len - BS.length vil) 0 125 126 forge :: Natural -> BS.ByteString -> BS.ByteString 127 forge mod msg = 128 let f = fencode mod msg 129 in unroll $ R.integerCubeRoot (roll f) + 1 130 131 -- parity attack 132 133 consistentKey :: Keypair 134 consistentKey = Keypair { 135 sec = Sec 17123352828014333155624438024036760971684155055395178750326166116221921534834757334258805831433671108747515574930784033716009753162288853697798226497143603784063672293784689339725292980717759302559416192505022202607060043180747993307152813641965271101487768850534996446308519974161336757521350033549104638323502861457159133823648406287066941450810841565848911430015280485845523895713183178201477186740322834886881520321163855222966200390877773389398001466822114489027189069065611644814402176315409188376507981912063223328698296264072987777394439869807029983108333829414790214696124608366420616926584028341835718008171 25685029242021499733436657036055141457526232583092768125489249174332882302252136001388208747150506663121273362396176050574014629743433280546697339745715405676095508440677034009587939471076638953839124288757533303910590064771121989960729220462947906652231653275802494669462779961242005136282025050323656957485575104768964364403120315473688233575506565199853941740698324759332741496556795318816219056943528386602087313223192513906581768759460447708758904161995531418020160091893731652698334419244087283089646693366368274960752450233540634283787034263316102286260474903106332924146000298152373432597583736507887518612367 136 , pub = Pub 3 25685029242021499733436657036055141457526232583092768125489249174332882302252136001388208747150506663121273362396176050574014629743433280546697339745715405676095508440677034009587939471076638953839124288757533303910590064771121989960729220462947906652231653275802494669462779961242005136282025050323656957485575104768964364403120315473688233575506565199853941740698324759332741496556795318816219056943528386602087313223192513906581768759460447708758904161995531418020160091893731652698334419244087283089646693366368274960752450233540634283787034263316102286260474903106332924146000298152373432597583736507887518612367 137 } 138 139 -- true if odd 140 parityOracle :: BS.ByteString -> Bool 141 parityOracle cip = 142 let msg = decrypt (sec consistentKey) cip 143 in B.testBit (roll msg) 0 144 145 parityAttack :: Key -> BS.ByteString -> IO BS.ByteString 146 parityAttack (Pub e n) cip = loop 0 n (roll cip) where 147 loop i j c 148 | j == i || j - i == 1 = pure (unroll j) 149 | otherwise = do 150 B8.putStrLn (unroll j) 151 let d = (c * DH.modexp 2 e n) `mod` n 152 if parityOracle (unroll d) 153 then loop (i + (j - i) `quot` 2) j d 154 else loop i (j - (j - i) `quot` 2) d 155