WringTwistree 0.0.1.1 → 0.1.0.0
raw patch · 11 files changed
+403/−121 lines, 11 filesdep +terminal-progress-barPVP ok
version bump matches the API change (PVP)
Dependencies added: terminal-progress-bar
API changes (from Hackage documentation)
+ Cryptography.Wring: encryptN :: Wring -> Int -> Vector Word8 -> (Vector Word8, [Int])
+ Cryptography.Wring: findMaxOrder :: Integer -> Integer
Files
- CHANGELOG.md +13/−1
- README.md +6/−3
- WringTwistree.cabal +5/−4
- app/Cryptanalysis.hs +221/−62
- app/Stats.hs +31/−0
- src/Cryptography/Twistree.hs +8/−1
- src/Cryptography/Wring.hs +34/−2
- src/Cryptography/WringTwistree/Mix3.hs +5/−3
- src/Cryptography/WringTwistree/RotBitcount.hs +17/−2
- src/Cryptography/WringTwistree/Sboxes.hs +1/−1
- test/Spec.hs +62/−42
CHANGELOG.md view
@@ -7,7 +7,19 @@ [Haskell Package Versioning Policy](https://pvp.haskell.org/) for Haskell and [Semantic Versioning](https://semver.org/) for Rust. -## Unreleased+## 0.1.0.0 - 1.0.0 - 2024-05-30++### Added++- -O2 option+- Progress bar for cryptanalysis+- In Rust, initialize sbox of new Wring and Twistree+- 15-byte test vectors++### Changed++- Increased number of rounds in Wring, because cryptanalysis showed it was not sufficient for security of large messages+- Make Rust modules and functions private ## 0.0.1.1 - 0.1.0 - 2024-01-11
README.md view
@@ -12,8 +12,9 @@ 2. The three key-dependent 8×8 S-boxes provide confusion and resist linear cryptanalysis. 3. Rotating by the population count thwarts integral and differential cryptanalysis by moving the difference around to a different part of the message.-4. Wring's round constant, which is dependent on the byte position as well as the round number, is added to every byte to prevent slide attacks and ensure that an all-zero message doesn't stay that way.-4. Twistree runs a CRC backwards to make the four bytes about to be dropped affect all the other bytes.+4. + * Wring's round constant, which is dependent on the byte position as well as the round number, is added to every byte to prevent slide attacks and ensure that an all-zero message doesn't stay that way.+ * Twistree runs a CRC backwards to make the four bytes about to be dropped affect all the other bytes. # Rust To run the program, type `cargo run`. The first time you run it, Cargo may download a quarter-gigabyte of data, which is the index to all crates. You can also test the program with `cargo test`.@@ -21,6 +22,8 @@ # Haskell You can run WringTwistree in the REPL with `stack ghci`, compile and run it with `stack run`, or use other Stack commands. You can also test the program with `stack test`. +If you develop this using Cabal, please edit `package.yaml` to match any changes you make to `WringTwistree.cabal`.+ # Command-line options `stack run -- -k key -e plaintext -o ciphertext` enciphers a file. Omit `-o ciphertext` to encipher in place. @@ -28,7 +31,7 @@ `stack run -- -k key -H plaintext -o hash` hashes a file. Omit `-o hash` to output the hash to stdout. -`stack run -- -c foo` runs cryptanalysis of type `foo`, which is currently `relkey` for related-key cryptanalysis. This option is not available in the Rust implementation.+`stack run -- -c foo` runs cryptanalysis of type `foo`, which is currently `relkey` for related-key cryptanalysis, `integral` for integral cryptanalysis, `hashcoll` for hash collisions, or `integral-fixed` or `hashcoll-linear` for variants of the preceding. This option is not available in the Rust implementation. `cargo` instead of `stack` runs the Rust implementation; the options are the same, except that hash is `-h`.
WringTwistree.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: WringTwistree-version: 0.0.1.1+version: 0.1.0.0 synopsis: Whole-message cipher and tree hash description: Please see the README on GitHub at <https://github.com/phma/wring-twistree#readme> category: Cryptography@@ -39,7 +39,7 @@ Cryptography.WringTwistree.Sboxes hs-source-dirs: src- ghc-options: -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints+ ghc-options: -O2 -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints build-depends: arithmoi , array@@ -61,7 +61,7 @@ Paths_WringTwistree hs-source-dirs: app- ghc-options: -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N+ ghc-options: -O2 -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N build-depends: WringTwistree , arithmoi@@ -74,6 +74,7 @@ , parallel , sort , split+ , terminal-progress-bar , utf8-string , vector default-language: Haskell2010@@ -85,7 +86,7 @@ Paths_WringTwistree hs-source-dirs: test- ghc-options: -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N+ ghc-options: -O2 -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N build-depends: WringTwistree , array
app/Cryptanalysis.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE BangPatterns #-} module Cryptanalysis ( key96_0 , key96_1@@ -27,12 +28,15 @@ , wring6_3 , same30_3 , same96_0+ , nopio , match , rot1Bit , bias , convolve , unbiasedConvolve , convolveDiff+ , big3Power+ , thueMorse , priminal , byteArray , b125@@ -63,11 +67,16 @@ , collisions1 , hashColl , hashCollLinear+ , cumRot+ , rotations1+ , rotations256+ , pairs+ , jiggleMatch ) where import Data.Word import Data.Bits-import Data.Sort+import Data.List (sort,sortOn,group,inits,tails,transpose) import Math.NumberTheory.Primes import Data.Array.Unboxed import qualified Data.Sequence as Seq@@ -76,6 +85,8 @@ import Control.DeepSeq import Control.Parallel import Control.Parallel.Strategies+import System.ProgressBar+import System.IO.Unsafe import GHC.Conc (numCapabilities) import qualified Data.ByteString as B import Data.ByteString.UTF8 (fromString)@@ -136,6 +147,12 @@ wring6_2 = keyedWring $ fromString key6_2 wring6_3 = keyedWring $ fromString key6_3 +nopio :: IO () -- for calling functions which have a progress bar update+nopio = return () -- argument from GHCI++dotio :: IO ()+dotio = putStrLn "."+ -- Because of rotBitcount, fixed-position bit differences are inadequate to -- telling whether two outputs of Wring are similar. Convolve them instead. @@ -172,6 +189,30 @@ (sum $ map (^2) $ unbiasedConvolve as bs) / scale where scale = ((1+(bias as))*(1+(bias bs))*(1-(bias as))*(1-(bias bs))) +-- Big numbers used for generating plaintext sequences++-- Big power of 3++big3Power :: Integral a => Int -> a+big3Power n = 3 ^ (n * 53 `div` 84)++-- Thue-Morse word++log2 :: Integral a => a -> Int+log2 0 = (-1)+log2 (-1) = (-1771476585)+log2 n = 1 + (log2 (n `div` 2))++thueMorse_ :: Int -> Integer+thueMorse_ 0 = 1+thueMorse_ n = (((1 .<<. nbits) - 1 - (thueMorse_ (n-1))) .<<. nbits)+ + (thueMorse_ (n-1)) where+ nbits = (1 .<<. (n-1))++thueMorse :: Integral a => Int -> a+-- Returns a Thue-Morse word of at least n bits ending in 1.+thueMorse n = fromIntegral (thueMorse_ ((log2 n)+1))+ -- Multiples of the priminal word for spreading plaintexts around the -- space of plaintext @@ -273,58 +314,68 @@ name2 :: Wring -> String -> Wring -> String name2 w0 s w1 = (wringName w0) ++ s ++ (wringName w1) -diff1Related :: Wring -> Wring -> Word64 -> Word64-diff1Related w0 w1 pt = ct0 .^. ct1 where+{-# NOINLINE diff1Related #-}+diff1Related :: Wring -> Wring -> IO () -> Word64 -> Word64+diff1Related w0 w1 upd pt = seq+ (if mod pt 256 == 0 then unsafePerformIO upd else ()) $ ct0 .^. ct1 where ct0 = makeArrayInt $ encrypt w0 $ eightByteArray pt ct1 = makeArrayInt $ encrypt w1 $ eightByteArray pt -conDiff1Related :: Wring -> Wring -> Word64 -> Double-conDiff1Related w0 w1 pt = par ct0 $ convolveDiff ct0 ct1 where+{-# NOINLINE conDiff1Related #-}+conDiff1Related :: Wring -> Wring -> IO () -> Word64 -> Double+conDiff1Related w0 w1 upd pt = seq+ (if mod pt 256 == 0 then unsafePerformIO upd else ()) $+ par ct0 $ convolveDiff ct0 ct1 where ct0 = V.toList $ encrypt w0 $ eightByteArray pt ct1 = V.toList $ encrypt w1 $ eightByteArray pt -diffRelated :: Wring -> Wring -> [Word64]-diffRelated w0 w1 = traceEvent (name2 w0 "-diff-" w1) $ map ((diff1Related w0 w1) . ((priminal 64) *)) [0..]+diffRelated :: Wring -> Wring -> IO () -> [Word64]+diffRelated w0 w1 upd = traceEvent (name2 w0 "-diff-" w1) $+ map ((diff1Related w0 w1 upd) . ((priminal 64) *)) [0..] -conDiffRelated :: Wring -> Wring -> [Double]-conDiffRelated w0 w1 = traceEvent (name2 w0 "-con-" w1) $ map ((conDiff1Related w0 w1) . ((priminal 64) *)) [0..]+conDiffRelated :: Wring -> Wring -> IO () -> [Double]+conDiffRelated w0 w1 upd = traceEvent (name2 w0 "-con-" w1) $+ map ((conDiff1Related w0 w1 upd) . ((priminal 64) *)) [0..] plaintextHisto :: Histo plaintextHisto = foldl' hCountBits (emptyHisto 64) (take (div samples 2) (map ((priminal 64) *) [0..])) -relatedKeyHisto :: Wring -> Wring -> Histo-relatedKeyHisto w0 w1 = foldl' hCountBits (emptyHisto 64)- (take (div samples 2) (diffRelated w0 w1) `using` parListDeal numCapabilities rdeepseq)+relatedKeyHisto :: Wring -> Wring -> IO () -> Histo+relatedKeyHisto w0 w1 upd = foldl' hCountBits (emptyHisto 64)+ (take (div samples 2) (diffRelated w0 w1 upd)+ `using` parListDeal numCapabilities rdeepseq) -relatedKeyStatBit :: Wring -> Wring -> Double-relatedKeyStatBit w0 w1 = binomial (relatedKeyHisto w0 w1) (div samples 2)+relatedKeyStatBit :: Wring -> Wring -> IO () -> Double+relatedKeyStatBit w0 w1 upd = binomial (relatedKeyHisto w0 w1 upd) (div samples 2) -relatedKeyStatConv :: Wring -> Wring -> (Double,Double)-relatedKeyStatConv w0 w1 = normμσ 1 (sqrt (1/32))- (take (div samples 2) (conDiffRelated w0 w1) `using` parListDeal numCapabilities rdeepseq)+relatedKeyStatConv :: Wring -> Wring -> IO () -> (Double,Double)+relatedKeyStatConv w0 w1 upd = normμσ 1 (sqrt (1/32))+ (take (div samples 2) (conDiffRelated w0 w1 upd)+ `using` parListDeal numCapabilities rdeepseq) -sixStatsBit :: Wring -> Wring -> Wring -> Wring -> [Double]-sixStatsBit w0 w1 w2 w3 = par s01 $ par s23 $ par s02 $ par s13 $ par s03 $ par s12 $+sixStatsBit :: Wring -> Wring -> Wring -> Wring -> IO () -> [Double]+sixStatsBit w0 w1 w2 w3 upd =+ par s01 $ par s23 $ par s02 $ par s13 $ par s03 $ par s12 $ [s01,s23,s02,s13,s03,s12] where- s01 = relatedKeyStatBit w0 w1- s23 = relatedKeyStatBit w2 w3- s02 = relatedKeyStatBit w0 w2- s13 = relatedKeyStatBit w1 w3- s03 = relatedKeyStatBit w0 w3- s12 = relatedKeyStatBit w1 w2+ s01 = relatedKeyStatBit w0 w1 upd+ s23 = relatedKeyStatBit w2 w3 upd+ s02 = relatedKeyStatBit w0 w2 upd+ s13 = relatedKeyStatBit w1 w3 upd+ s03 = relatedKeyStatBit w0 w3 upd+ s12 = relatedKeyStatBit w1 w2 upd -sixStatsConv :: Wring -> Wring -> Wring -> Wring -> [(Double,Double)]-sixStatsConv w0 w1 w2 w3 =+sixStatsConv :: Wring -> Wring -> Wring -> Wring -> IO () -> [(Double,Double)]+sixStatsConv w0 w1 w2 w3 upd = par (force s01) $ par (force s23) $ par (force s02) $ par (force s13) $ par (force s03) $ par (force s12) $ [s01,s23,s02,s13,s03,s12] where- s01 = relatedKeyStatConv w0 w1- s23 = relatedKeyStatConv w2 w3- s02 = relatedKeyStatConv w0 w2- s13 = relatedKeyStatConv w1 w3- s03 = relatedKeyStatConv w0 w3- s12 = relatedKeyStatConv w1 w2+ s01 = relatedKeyStatConv w0 w1 upd+ s23 = relatedKeyStatConv w2 w3 upd+ s02 = relatedKeyStatConv w0 w2 upd+ s13 = relatedKeyStatConv w1 w3 upd+ s03 = relatedKeyStatConv w0 w3 upd+ s12 = relatedKeyStatConv w1 w2 upd tellStat64 :: Double -> String -- 0.635 and 1.456 are 1% tails at 64 degrees of freedom, divided by 64.@@ -350,7 +401,8 @@ relatedKey4Bit :: Wring -> Wring -> Wring -> Wring -> IO () relatedKey4Bit w0 w1 w2 w3 = do- let sixS = sixStatsBit w0 w1 w2 w3+ pb <- newProgressBar defStyle 3 (Progress 0 (div (samples*3) 256) ())+ let sixS = sixStatsBit w0 w1 w2 w3 (incProgress pb 1) putStrLn (show sixS) putStrLn ("0,1: " ++ tellStat64 (sixS !! 0)) putStrLn ("2,3: " ++ tellStat64 (sixS !! 1))@@ -361,7 +413,8 @@ relatedKey4Conv :: Wring -> Wring -> Wring -> Wring -> IO () relatedKey4Conv w0 w1 w2 w3 = do- let sixS = sixStatsConv w0 w1 w2 w3+ pb <- newProgressBar defStyle 3 (Progress 0 (div (samples*3) 256) ())+ let sixS = sixStatsConv w0 w1 w2 w3 (incProgress pb 1) putStrLn (show sixS) putStrLn ("0,1: " ++ tellStatμσ (sixS !! 0)) putStrLn ("2,3: " ++ tellStatμσ (sixS !! 1))@@ -397,38 +450,40 @@ -- and xor all the ciphertexts produced by changing the byte of the plaintext -- to all 256 possibilities. -sum1Wring :: Crypt -> Wring -> Word64 -> Int -> Word64+{-# NOINLINE sum1Wring #-}+sum1Wring :: Crypt -> Wring -> IO () -> Word64 -> Int -> Word64 -- Take pt with all values in the bth byte, encrypt them all, -- and xor the ciphertexts.-sum1Wring enc w pt b = foldl' xor 0 cts where+sum1Wring enc w upd pt b = seq (unsafePerformIO upd) $ foldl' xor 0 cts where pts = map eightByteArray $ zipWith xor (repeat pt) (map (.<<. (b .<<. 3)) [0..255]) cts = map makeArrayInt $ map (enc w) pts -integralHisto :: Crypt -> Wring -> Int -> Histo-integralHisto enc w b = foldl' hCountBits (emptyHisto 64)+integralHisto :: Crypt -> Wring -> IO () -> Int -> Histo+integralHisto enc w upd b = foldl' hCountBits (emptyHisto 64) (take (div samples 256)- (map ((\pt -> sum1Wring enc w pt b) . ((priminal 64) *)) [0..])+ (map ((\pt -> sum1Wring enc w upd pt b) . ((priminal 64) *)) [0..]) `using` parListDeal numCapabilities rdeepseq) -integralStat :: Crypt -> Wring -> Int -> Double-integralStat enc w b = binomial (integralHisto enc w b) (div samples 256)+integralStat :: Crypt -> Wring -> IO () -> Int -> Double+integralStat enc w upd b = binomial (integralHisto enc w upd b) (div samples 256) -eightStats :: Crypt -> Wring -> [Double]-eightStats enc w =+eightStats :: Crypt -> Wring -> IO () -> [Double]+eightStats enc w upd = par s0 $ par s1 $ par s2 $ par s3 $ par s4 $ par s5 $ par s6 $ par s7 $ [s0,s1,s2,s3,s4,s5,s6,s7] where- s0 = integralStat enc w 0- s1 = integralStat enc w 1- s2 = integralStat enc w 2- s3 = integralStat enc w 3- s4 = integralStat enc w 4- s5 = integralStat enc w 5- s6 = integralStat enc w 6- s7 = integralStat enc w 7+ s0 = integralStat enc w upd 0+ s1 = integralStat enc w upd 1+ s2 = integralStat enc w upd 2+ s3 = integralStat enc w upd 3+ s4 = integralStat enc w upd 4+ s5 = integralStat enc w upd 5+ s6 = integralStat enc w upd 6+ s7 = integralStat enc w upd 7 integral1 :: Crypt -> Wring -> IO () integral1 enc w = do- let eightS = eightStats enc w+ pb <- newProgressBar defStyle 10 (Progress 0 (div samples 32) ())+ let eightS = eightStats enc w (incProgress pb 1) putStrLn (show eightS) putStrLn ("Byte 0: " ++ tellStat64 (eightS !! 0)) putStrLn ("Byte 1: " ++ tellStat64 (eightS !! 1))@@ -450,9 +505,9 @@ putStrLn "Linear key, 8-byte data:" integral1 encrypt linearWring putStrLn "96-byte key, byte 7:" -- This byte came out as "too much variation".- putStrLn $ show $ integralHisto encrypt wring96_0 7+ putStrLn $ show $ integralHisto encrypt wring96_0 nopio 7 putStrLn "Linear key, byte 3:" -- This byte came out as "low-discrepancy".- putStrLn $ show $ integralHisto encrypt linearWring 3+ putStrLn $ show $ integralHisto encrypt linearWring nopio 3 integralCrFixed :: IO () integralCrFixed = do@@ -500,27 +555,131 @@ | h0==h1 = (a,b):(dups ((b,h1):xs)) | otherwise = dups ((b,h1):xs) -collisions1 :: SBox -> Integer -> [(V.Vector Word8,V.Vector Word8)]+{-# NOINLINE collisions1 #-}+collisions1 :: SBox -> IO () -> Integer -> [(V.Vector Word8,V.Vector Word8)] -- Given an S-box and a 64-nybble integer, tries compressing all 961 blocks -- made from the integer changed by 0 or 1 nybble, and returns any collisions.-collisions1 sbox n = dups $ sortOn snd $ compressChanges sbox n+collisions1 sbox upd n = seq (unsafePerformIO upd) $ dups+ $ sortOn snd $ compressChanges sbox n -collisions :: SBox -> [(V.Vector Word8,V.Vector Word8)]-collisions sbox = foldl' (++) []+collisions :: SBox -> IO () -> [(V.Vector Word8,V.Vector Word8)]+collisions sbox upd = foldl' (++) [] (take (div samples 961)- (map ((collisions1 sbox) . ((priminal 256) *)) [0..])+ (map ((collisions1 sbox upd) . ((priminal 256) *)) [0..]) `using` parListDeal numCapabilities rdeepseq) hashColl :: IO () hashColl = do putStrLn "Hash collisions, 30-byte key:"- let colls = collisions sbox30_3+ pb <- newProgressBar defStyle 10 (Progress 0 (div samples 961) ())+ let colls = collisions sbox30_3 (incProgress pb 1) putStrLn (show colls) putStrLn ((show (length colls)) ++ " collisions") hashCollLinear :: IO () hashCollLinear = do putStrLn "Hash collisions, linear S-box:"- let colls = collisions linearSbox+ pb <- newProgressBar defStyle 10 (Progress 0 (div samples 961) ())+ let colls = collisions linearSbox (incProgress pb 1) putStrLn (show colls) putStrLn ((show (length colls)) ++ " collisions")++-- Clutch cryptanalysis: find out how often two messages are rotated together++clutchSamples = 1+clutchMsgLen = 10000+clutchRounds = 8+clutchParNum = 2 + numCapabilities `div` 256 -- this 256 is from rotations256++countPairs :: Ord a => [a] -> Int+-- Given a list of numbers representing the amount by which a message has been+-- rotated by some number of rounds, returns the number of pairs of+-- equal numbers.+countPairs ns = sum $ map (\n -> n*(n-1)) $ map length $ group $ sort ns++cumRot :: [Int] -> [Int]+cumRot rotations = map (`mod` (8*clutchMsgLen)) $ map sum $ tail $ inits rotations++messageArray :: Integer -> V.Vector Word8+messageArray pt = byteArray clutchMsgLen pt++rotations1 :: Wring -> Integer -> [Int]+rotations1 wring pt = cumRot $ snd $ encryptN wring clutchRounds $ messageArray pt++rotations256 :: Wring -> Integer -> Int -> [[Int]]+rotations256 wring pt n = (map (rotations1 wring) $ map (xor pt) $+ map (.<<. (8*n)) [0..255])+ `using` parListDeal numCapabilities rdeepseq++type Jiggle =+ ( Integer -- plaintext+ , Int -- byte which is changed+ , Int -- two byte values which give the same sum of first and second+ , Int -- rotations, but different first and second rotations+ )++pairs1 :: [a] -> [(a,a)]+pairs1 [] = []+pairs1 (x:xs) = zip (repeat x) xs++pairs :: [a] -> [(a,a)]+pairs xs = concatMap pairs1 (tails xs)++isJiggly :: (Eq b,Eq c) => ((a,b,c),(a,b,c)) -> Bool+isJiggly ((inx0,each0,sum0),(inx1,each1,sum1)) = (sum0 == sum1) && (each0 /= each1)++jigglize :: Integer -> Int -> ((Int,b,c),(Int,b,c)) -> Jiggle+jigglize pt n ((inx0,each0,sum0),(inx1,each1,sum1)) = (pt,n,inx0,inx1)++{-# NOINLINE clutch1 #-}+clutch1 :: Fractional a => Wring -> IO () -> (Integer,Int) -> ([a],[a],[Jiggle])+clutch1 wring upd (pt,n) = (totalRotStats,togetherRotStats,jiggle) where+ rotations = seq (unsafePerformIO upd) $ rotations256 wring pt n+ rotTogether = map (tail . inits) rotations+ totalRotStats = map (/(256*255)) $ map (fromIntegral . countPairs) $ transpose rotations+ togetherRotStats = map (/(256*255)) $ map (fromIntegral . countPairs) $ transpose rotTogether+ jiggle = map (jigglize pt n) $ filter isJiggly $ pairs $+ zip3 [0..255] (map (!! 1) rotTogether) (map (!! 1) rotations)++addClutch :: Num a => ([a],[a],[b]) -> ([a],[a],[b]) -> ([a],[a],[b])+addClutch (a,b,c) (m,n,o) = (zipWith (+) a m,zipWith (+) b n,c ++ o)++divClutch :: Fractional a => ([a],[a],[b]) -> ([a],[a],[b])+divClutch (a,b,c) = (map (/(fromIntegral clutchSamples)) a,+ map (/(fromIntegral clutchSamples)) b,+ c)++clutchStats :: Fractional a => Wring -> ProgressBar s -> ([a],[a],[Jiggle])+clutchStats wring pb = divClutch $ foldl' addClutch (repeat 0,repeat 0,[]) $+ map (clutch1 wring (incProgress pb 1)) $+ map (\x -> ((big3Power (8*clutchMsgLen))*(fromIntegral x),+ (x*(fromIntegral (findMaxOrder (fromIntegral clutchMsgLen))) `mod` clutchMsgLen)))+ ([1..clutchSamples] `using` parListDeal clutchParNum rdeepseq)++{-# NOINLINE jiggleMatch #-}+jiggleMatch :: Wring -> IO () -> Jiggle -> (Int,Int)+jiggleMatch wring upd (pt,byte,b0,b1) = (sideways,matches) where+ (ct0,r0) = encryptN wring 2 $ messageArray (pt .^. ((fromIntegral b0) .<<. (8*byte)))+ (ct1,r1) = encryptN wring 2 $ messageArray (pt .^. ((fromIntegral b1) .<<. (8*byte)))+ sideways' = ((head r1) - (head r0)) `mod` (8*clutchMsgLen)+ sideways = if (sideways' > 4*clutchMsgLen)+ then (sideways' - 8*clutchMsgLen)+ else sideways'+ matches = seq (unsafePerformIO upd) $ match (V.toList ct0) (V.toList ct1)++clutch :: IO ()+clutch = do+ pb <- newProgressBar defStyle 10 (Progress 0 clutchSamples ())+ let (statsTotal,statsTogether,jiggles) = clutchStats wring96_0 pb+ seq (length statsTotal) nopio+ putStrLn "Same total rotation after n rounds:"+ putStrLn $ show statsTotal+ putStrLn "Rotated together for n rounds:"+ putStrLn $ show statsTogether+ putStr "Number of 2-round jiggles: "+ putStrLn $ show $ length jiggles+ pbj <- newProgressBar defStyle 10 (Progress 0 (length jiggles) ())+ let jh = jiggleHisto $ map (jiggleMatch wring96_0 (incProgress pbj 1)) jiggles+ let jStats = jiggleStats jh+ seq (sum (map (\(a,(b,c,d,e,f)) -> c) jStats)) nopio+ putStrLn $ show jStats
app/Stats.hs view
@@ -11,12 +11,18 @@ , bitFold , sacStats , normμσ+ , jhCount+ , emptyJiggleHisto+ , jiggleHisto+ , listStats+ , jiggleStats ) where import Data.Bits import Data.Word import qualified Data.Sequence as Seq import Data.Sequence ((><), (<|), (|>), Seq((:<|)), Seq((:|>)))+import qualified Data.IntMap.Strict as Map import Data.Foldable (toList,foldl') import Control.Parallel import Control.Parallel.Strategies@@ -122,3 +128,28 @@ (n,total,total2) = pairwiseSumCount ndevsqs mean = total / (fromIntegral n) var = total2 / (fromIntegral n)++type JiggleHisto = Map.IntMap [Int]+-- The key is sideways; the value is the list of matches from jiggleMatch.++jhCount :: JiggleHisto -> (Int,Int) -> JiggleHisto+jhCount jh (sideways,matches) = Map.insertWith (++) sideways [matches] jh++emptyJiggleHisto :: JiggleHisto+emptyJiggleHisto = Map.empty++jiggleHisto :: [(Int,Int)] -> JiggleHisto+jiggleHisto sidematches = foldl' jhCount emptyJiggleHisto sidematches++listStats :: (Real a,Bounded a) => [a] -> (Int,Double,Double,a,a)+listStats xs = (n,mean,sqrt var,minxs,maxxs) where+ minxs = if null xs then maxBound else minimum xs+ maxxs = if null xs then minBound else maximum xs+ devs = map realToFrac xs+ devsqs = map (\x -> (1,x,x^2)) devs+ (n,total,total2) = pairwiseSumCount devsqs+ mean = total / (fromIntegral n)+ var = (total2 - total * mean) / (fromIntegral n-1)++jiggleStats :: JiggleHisto -> [(Int,(Int,Double,Double,Int,Int))]+jiggleStats jh = Map.toList $ Map.map listStats jh
src/Cryptography/Twistree.hs view
@@ -60,7 +60,14 @@ deal n = transpose . chunksOf n -- to be used as a parallel strategy -parListDeal :: Int -> Strategy a -> Strategy [a]+-- | Deals a list among threads; each thread evaluates the items of its part+-- of the list in sequence, and all the threads run in parallel. If n is 12,+-- the 0th, 12th, 24th, ... items are evaluated by one thread, the 1st, 13rd,+-- 25th, ... items are evaluated by another thread, and so on.+parListDeal+ :: Int -- ^ n, the number of threads+ -> Strategy a+ -> Strategy [a] parListDeal n strat xs | n <= 1 = evalList strat xs | otherwise = concat `fmap` parList (evalList strat) (deal n xs)
src/Cryptography/Wring.hs view
@@ -1,6 +1,7 @@ module Cryptography.Wring ( Wring , mix3Parts -- reexported for use in Cryptanalysis.hs+ , findMaxOrder -- reexported for use in Cryptanalysis.hs , permut8 -- reexported for testing , mul65537 -- " , wringName -- For events in traces when profiling@@ -10,6 +11,7 @@ , encrypt , decrypt , encryptFixed -- Only for cryptanalysis and testing+ , encryptN -- Only for cryptanalysis and testing ) where {- This cipher is intended to be used with short random keys (32 bytes or less,@@ -50,8 +52,8 @@ nRounds :: Integral a => a -> a nRounds len- | len < 3 = 3- | otherwise = (nRounds (len `div` 3)) +1+ | len < 2 = 3+ | otherwise = (nRounds (len `div` 2)) +1 -- | Exclusive-ors all bytes in a nonnegative number. The only reason this -- function is public is that it's used to generate a long `ByteString`@@ -102,6 +104,24 @@ i4 = V.fromListN len $ zipWith (+) (V.toList i3) (map (xor xornrond) (V.toList xornary)) +roundEncryptFunN ::+ Int ->+ SBox ->+ V.Vector Word8 ->+ (V.Vector Word8, [Int]) ->+ Int ->+ (V.Vector Word8, [Int])+roundEncryptFunN rprime sbox xornary (buf,ns) rond = (i4,ns') where+ len = V.length buf+ xornrond = xorn rond+ i1 = mix3Parts buf rprime+ i2 = V.fromListN len $ map (sbox V.!) $+ zipWith sboxInx (drop rond cycle3) (V.toList i1)+ (i3,n) = rotBitcountN i2 1+ i4 = V.fromListN len $ zipWith (+) (V.toList i3)+ (map (xor xornrond) (V.toList xornary))+ ns' = ns ++ [n]+ roundDecryptFun :: Int -> SBox ->@@ -128,6 +148,15 @@ rprime = fromIntegral $ findMaxOrder (fromIntegral $ len `div` 3) rounds = [0 .. (nRounds len) -1] +encryptFunN :: Wring -> Int -> V.Vector Word8 -> (V.Vector Word8, [Int])+encryptFunN wring n buf = foldl' (roundEncryptFunN rprime (sbox wring) xornary)+ (buf,[]) rounds+ where+ len = V.length buf+ xornary = xornArray len+ rprime = fromIntegral $ findMaxOrder (fromIntegral $ len `div` 3)+ rounds = [0 .. n-1]+ decryptFun :: Wring -> V.Vector Word8 -> V.Vector Word8 decryptFun wring buf = foldl' (roundDecryptFun rprime (invSbox wring) xornary) buf rounds@@ -260,6 +289,9 @@ -- | Used only for cryptanalysis encryptFixed = encryptFixedST++-- | Used only for cryptanalysis+encryptN = encryptFunN -- | Decrypts a message. decrypt
src/Cryptography/WringTwistree/Mix3.hs view
@@ -74,9 +74,11 @@ searchFrom :: (Integer,Int) -> [Integer] searchFrom (start,dir) = map (\x -> x*(fromIntegral dir)+start) searchSeq -findMaxOrder :: Integer -> Integer--- n must be positive.--- Returns the number of maximum multiplicative order mod n closest to n/φ.+findMaxOrder+ :: Integer -- ^ n must be positive.+ -> Integer -- ^ A number relatively prime to n+-- ^ Returns the number of maximum multiplicative order mod n closest to n/φ.+-- This is a primitive root, if there are any. -- n=1 is a special case, as (isMaxOrder 1 1 [] i) returns False for all i>=0. findMaxOrder 1 = 1 findMaxOrder n = head $ filter (isMaxOrder n car fac) $ searchFrom $ searchDir n
src/Cryptography/WringTwistree/RotBitcount.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE BangPatterns #-} module Cryptography.WringTwistree.RotBitcount ( rotBitcount+ , rotBitcountN -- Only for cryptanalysis and testing , rotBitcount' , rotFixed , rotFixed'@@ -37,17 +38,31 @@ multmod = if len>0 then mult `mod` (len * 8) else mult bitcount = sum $ map popCount $ V.toList src rotcount = if len>0 then (bitcount * multmod) `mod` (len * 8)- else bitcount * multmod+ else bitcount * multmod !byte = rotcount .>>. 3 !bit = rotcount .&. 7 +rotBitcountN :: V.Vector Word8 -> Int -> (V.Vector Word8, Int)+-- for cryptanalysis+rotBitcountN src mult = (V.fromListN len+ [ (src V.! ((i+len-byte) `mod` len) .<<. bit) .|.+ (src V.! ((i+len-byte-1) `mod` len) .>>. (8-bit)) | i <- [0..(len-1)]], bitcount)+ where+ len = V.length src+ multmod = if len>0 then mult `mod` (len * 8) else mult+ bitcount = sum $ map popCount $ V.toList src+ rotcount = if len>0 then (bitcount * multmod) `mod` (len * 8)+ else bitcount * multmod+ !byte = rotcount .>>. 3+ !bit = rotcount .&. 7+ rotBitcount' :: MV.MVector s Word8 -> Int -> MV.MVector s Word8 -> ST s () rotBitcount' src mult dst = do bitcount <- MV.foldl' (\acc x -> acc + popCount x) 0 src let len = MV.length src !multmod = if len>0 then mult `mod` (len * 8) else mult rotcount = if len>0 then (bitcount * multmod) `rem` (len * 8)- else bitcount * multmod+ else bitcount * multmod !byte = rotcount .>>. 3 !bit = rotcount .&. 7 forM_ [0..MV.length src - 1] $ \i -> do
src/Cryptography/WringTwistree/Sboxes.hs view
@@ -29,7 +29,7 @@ import Cryptography.WringTwistree.KeySchedule import qualified Data.Vector.Unboxed as V --- | Three 8×8 S-boxes used alternatively to substitute bytes+-- | Three 8×8 S-boxes used alternately to substitute bytes type SBox = V.Vector Word8 {-# SPECIALIZE sboxInx :: Word8 -> Word8 -> Int #-}
test/Spec.hs view
@@ -72,94 +72,114 @@ testVectorsWring = testGroup "Test vectors for Wring" [ testCase "lin8nulls" $ (encrypt linearWring (fromListN 8 [0,0,0,0,0,0,0,0]))- @?= fromListN 8 [0x04,0xd7,0x16,0x6a,0xca,0x70,0x57,0xbc]+ @?= fromListN 8 [0x5e,0x09,0xc0,0x54,0x3c,0x82,0xdc,0xe8] , testCase "lin8ff" $ (encrypt linearWring (fromListN 8 [255,255,255,255,255,255,255,255]))- @?= fromListN 8 [0x84,0x91,0x95,0xa0,0x9f,0x48,0x4b,0x59]+ @?= fromListN 8 [0x69,0x9d,0x9d,0xff,0xbc,0x22,0x8a,0x5d] , testCase "lin8Twistree" $ (encrypt linearWring (fromListN 8 $ stringToBytes "Twistree"))- @?= fromListN 8 [0xed,0x4b,0x2e,0xc6,0xc4,0x39,0x65,0x2b]+ @?= fromListN 8 [0xbb,0xc3,0x28,0x40,0x8c,0x94,0xae,0xf8] , testCase "lin9nulls" $ (encrypt linearWring (fromListN 9 [0,0,0,0,0,0,0,0,0]))- @?= fromListN 9 [0xad,0x93,0x5e,0x85,0xe1,0x49,0x45,0xca,0xe2]+ @?= fromListN 9 [0x4a,0xb1,0x99,0x07,0x1b,0x8c,0xd4,0x54,0xaf] , testCase "lin9ff" $ (encrypt linearWring (fromListN 9 [255,255,255,255,255,255,255,255,255]))- @?= fromListN 9 [0x36,0xdf,0x60,0xae,0xf5,0xbd,0x1a,0xaf,0x6e]+ @?= fromListN 9 [0x8b,0x6c,0x0a,0x7d,0x32,0x23,0xee,0xc5,0x7f] , testCase "lin9AllOrNone" $ (encrypt linearWring (fromListN 9 $ stringToBytes "AllOrNone"))- @?= fromListN 9 [0x53,0x28,0xe7,0xc7,0xe0,0x71,0xa5,0x2e,0x8c]+ @?= fromListN 9 [0xc1,0xd8,0x4e,0x09,0xea,0x44,0xf3,0x0d,0x19]+ , testCase "lin15Encipher" $+ (encrypt linearWring (fromListN 15 $ stringToBytes "EncipherMessage"))+ @?= fromListN 15 [ 0xc5, 0x0e, 0xc1, 0x88, 0xfd, 0x97, 0x89, 0x2d+ , 0xa3, 0xdf, 0x58, 0x15, 0xd3, 0xf4, 0x8c] , testCase "null8nulls" $ (encrypt wring0 (fromListN 8 [0,0,0,0,0,0,0,0]))- @?= fromListN 8 [0x77,0x3e,0x34,0x8f,0x48,0xa1,0x24,0x1a]+ @?= fromListN 8 [0x61,0x4b,0xf3,0x34,0x9e,0x21,0xd1,0x21] , testCase "null8ff" $ (encrypt wring0 (fromListN 8 [255,255,255,255,255,255,255,255]))- @?= fromListN 8 [0xc7,0xa7,0x58,0xed,0x5c,0x2b,0xb6,0xec]+ @?= fromListN 8 [0x22,0xc3,0x6c,0x9f,0xe4,0x27,0xbc,0xce] , testCase "null8Twistree" $ (encrypt wring0 (fromListN 8 $ stringToBytes "Twistree"))- @?= fromListN 8 [0xa3,0xcf,0xd4,0xa1,0x0d,0x7e,0xb7,0xb3]+ @?= fromListN 8 [0x9a,0xcd,0xf5,0xe0,0xb0,0x3c,0x0c,0x32] , testCase "null9nulls" $ (encrypt wring0 (fromListN 9 [0,0,0,0,0,0,0,0,0]))- @?= fromListN 9 [0x10,0x10,0x95,0x96,0x90,0xb5,0x97,0xeb,0x38]+ @?= fromListN 9 [0x72,0x4d,0x2a,0x49,0xc9,0xb3,0x00,0xdb,0x32] , testCase "null9ff" $ (encrypt wring0 (fromListN 9 [255,255,255,255,255,255,255,255,255]))- @?= fromListN 9 [0x09,0x0f,0xf3,0x66,0x36,0xa4,0xac,0x8d,0x5c]+ @?= fromListN 9 [0xbc,0x05,0xcf,0x4b,0x0f,0x31,0x81,0xba,0x5c] , testCase "null9AllOrNone" $ (encrypt wring0 (fromListN 9 $ stringToBytes "AllOrNone"))- @?= fromListN 9 [0xee,0x15,0x02,0x05,0xdd,0xa9,0x77,0xe4,0x23]+ @?= fromListN 9 [0x06,0x15,0xaf,0x81,0x8b,0x63,0x9c,0xa2,0x9e]+ , testCase "null15Encipher" $+ (encrypt wring0 (fromListN 15 $ stringToBytes "EncipherMessage"))+ @?= fromListN 15 [ 0x29, 0xb5, 0x8f, 0xd1, 0x67, 0xa7, 0xb3, 0xf6+ , 0x99, 0x33, 0xa8, 0x47, 0x3c, 0xb4, 0xcf] , testCase "aerate8nulls" $ (encrypt wring6 (fromListN 8 [0,0,0,0,0,0,0,0]))- @?= fromListN 8 [0x23,0x44,0x2e,0x6e,0xf3,0xd7,0xa0,0x7e]+ @?= fromListN 8 [0x7d,0x9b,0x65,0x67,0xe8,0xa3,0xd2,0x7d] , testCase "aerate8ff" $ (encrypt wring6 (fromListN 8 [255,255,255,255,255,255,255,255]))- @?= fromListN 8 [0x7e,0x05,0xae,0x5c,0x64,0xdd,0xf4,0xeb]+ @?= fromListN 8 [0x2d,0xdc,0xb9,0xb7,0xaa,0x88,0xa2,0xcf] , testCase "aerate8Twistree" $ (encrypt wring6 (fromListN 8 $ stringToBytes "Twistree"))- @?= fromListN 8 [0x36,0x39,0x14,0x22,0x40,0x7f,0xc3,0x79]+ @?= fromListN 8 [0xc8,0xbc,0x27,0x92,0x7a,0x8d,0x96,0x97] , testCase "aerate9nulls" $ (encrypt wring6 (fromListN 9 [0,0,0,0,0,0,0,0,0]))- @?= fromListN 9 [0x41,0xc1,0x44,0x0f,0x07,0x2d,0x92,0xbf,0x43]+ @?= fromListN 9 [0xbe,0x74,0x4a,0x6c,0x6b,0x5b,0x2a,0x6c,0x4e] , testCase "aerate9ff" $ (encrypt wring6 (fromListN 9 [255,255,255,255,255,255,255,255,255]))- @?= fromListN 9 [0x46,0xb0,0x57,0x43,0xfb,0xdb,0x9d,0x32,0x88]+ @?= fromListN 9 [0xb3,0xb5,0xf1,0x9d,0x44,0xa3,0x19,0xd0,0x10] , testCase "aerate9AllOrNone" $ (encrypt wring6 (fromListN 9 $ stringToBytes "AllOrNone"))- @?= fromListN 9 [0x5e,0x3b,0x49,0xd4,0xb8,0x70,0xdd,0x07,0xac]+ @?= fromListN 9 [0xe8,0x23,0xcd,0x4d,0x80,0x5f,0xdb,0xb4,0x0a]+ , testCase "aerate15Encipher" $+ (encrypt wring6 (fromListN 15 $ stringToBytes "EncipherMessage"))+ @?= fromListN 15 [ 0x17, 0x95, 0x06, 0x84, 0x85, 0x6c, 0x0e, 0xed+ , 0x68, 0x70, 0xe4, 0xdd, 0xeb, 0xd1, 0x92] , testCase "χαίρετε8nulls" $ (encrypt wring30 (fromListN 8 [0,0,0,0,0,0,0,0]))- @?= fromListN 8 [0x90,0x4d,0x00,0x3e,0x39,0x75,0x9e,0xe4]+ @?= fromListN 8 [0xa3,0x5d,0x2e,0x41,0xf3,0x20,0x9e,0x5c] , testCase "χαίρετε8ff" $ (encrypt wring30 (fromListN 8 [255,255,255,255,255,255,255,255]))- @?= fromListN 8 [0x85,0x56,0x3d,0x4e,0x84,0x5a,0x14,0xe3]+ @?= fromListN 8 [0xbf,0xab,0x12,0xd1,0xa4,0xa7,0x59,0x24] , testCase "χαίρετε8Twistree" $ (encrypt wring30 (fromListN 8 $ stringToBytes "Twistree"))- @?= fromListN 8 [0x96,0x89,0x48,0x50,0x98,0x26,0xeb,0x03]+ @?= fromListN 8 [0xf1,0x45,0xbe,0x34,0x3e,0x0e,0x50,0x44] , testCase "χαίρετε9nulls" $ (encrypt wring30 (fromListN 9 [0,0,0,0,0,0,0,0,0]))- @?= fromListN 9 [0x04,0xb0,0x1e,0xdf,0xd3,0xf0,0x39,0xa3,0x3c]+ @?= fromListN 9 [0x29,0x37,0x34,0x65,0xb4,0xe3,0xd6,0x8b,0x08] , testCase "χαίρετε9ff" $ (encrypt wring30 (fromListN 9 [255,255,255,255,255,255,255,255,255]))- @?= fromListN 9 [0x7c,0x67,0xeb,0xc8,0x40,0x97,0xc2,0x5f,0x82]+ @?= fromListN 9 [0xb9,0x31,0xfb,0xd0,0x08,0x42,0x24,0xe0,0x54] , testCase "χαίρετε9AllOrNone" $ (encrypt wring30 (fromListN 9 $ stringToBytes "AllOrNone"))- @?= fromListN 9 [0x4b,0xb2,0x68,0xe9,0xf1,0x64,0x0a,0x44,0xc4]+ @?= fromListN 9 [0xa3,0xab,0x75,0x4b,0xea,0x16,0x54,0xd6,0xa7]+ , testCase "χαίρετε15Encipher" $+ (encrypt wring30 (fromListN 15 $ stringToBytes "EncipherMessage"))+ @?= fromListN 15 [ 0x9b, 0x21, 0x52, 0x31, 0x35, 0x7c, 0xb4, 0x51+ , 0x85, 0x6d, 0xbd, 0x1b, 0xf0, 0xf6, 0x4d] , testCase "двор8nulls" $ (encrypt wring96 (fromListN 8 [0,0,0,0,0,0,0,0]))- @?= fromListN 8 [0x9c,0xc1,0x3c,0xe7,0x8a,0xc5,0x6f,0x18]+ @?= fromListN 8 [0xb3,0x8c,0xb4,0x4e,0x32,0xef,0xd7,0xc8] , testCase "двор8ff" $ (encrypt wring96 (fromListN 8 [255,255,255,255,255,255,255,255]))- @?= fromListN 8 [0x37,0x42,0x00,0x47,0xd5,0x2f,0x9d,0x7f]+ @?= fromListN 8 [0x8d,0x0d,0x3c,0x89,0x73,0x22,0xe6,0x6b] , testCase "двор8Twistree" $ (encrypt wring96 (fromListN 8 $ stringToBytes "Twistree"))- @?= fromListN 8 [0x15,0x06,0xc6,0xa6,0x3d,0xef,0x19,0xf1]+ @?= fromListN 8 [0xb7,0x00,0x83,0x84,0x41,0x4d,0x14,0x4b] , testCase "двор9nulls" $ (encrypt wring96 (fromListN 9 [0,0,0,0,0,0,0,0,0]))- @?= fromListN 9 [0xd6,0x7b,0x2c,0xcc,0x71,0x24,0x5d,0x06,0x07]+ @?= fromListN 9 [0x84,0xfb,0x3e,0x5b,0x4f,0xce,0x43,0x54,0x84] , testCase "двор9ff" $ (encrypt wring96 (fromListN 9 [255,255,255,255,255,255,255,255,255]))- @?= fromListN 9 [0xd3,0xa8,0x3f,0xb6,0x9a,0x5e,0x0f,0x07,0x11]+ @?= fromListN 9 [0xed,0xa7,0x7a,0x91,0x3e,0x47,0xa4,0x1e,0x50] , testCase "двор9AllOrNone" $ (encrypt wring96 (fromListN 9 $ stringToBytes "AllOrNone"))- @?= fromListN 9 [0x53,0x6c,0x7e,0x2d,0xcd,0xda,0xdc,0xf2,0x70]+ @?= fromListN 9 [0x21,0x15,0x76,0xbc,0x8b,0x29,0x18,0x92,0x21]+ , testCase "двор15Encipher" $+ (encrypt wring96 (fromListN 15 $ stringToBytes "EncipherMessage"))+ @?= fromListN 15 [ 0x5d, 0x6d, 0x0c, 0xa5, 0xfb, 0x0d, 0x38, 0x74+ , 0x6b, 0xd0, 0x41, 0x10, 0x80, 0xf3, 0x78] , testCase "searchDir" $ (encrypt wring96 (fromList $ stringToBytes key96)) -- This comes from a typo when translating to Julia. All the 8- and 9-byte@@ -167,18 +187,18 @@ -- wrong, because it was searching in the wrong direction. -- This typo would also cause all hashes of strings >31 bytes to be wrong. @?= fromList- [ 0xed, 0xbb, 0x75, 0xc4, 0xb2, 0xc9, 0xd2, 0x82- , 0xa5, 0xbe, 0x37, 0x62, 0x4f, 0x5e, 0x7c, 0x1e - , 0x22, 0x59, 0xca, 0x7a, 0x66, 0xb3, 0xa7, 0x91- , 0x34, 0xec, 0x50, 0x2e, 0x45, 0x3e, 0xc5, 0xc3- , 0xcc, 0xf3, 0xa3, 0x49, 0x72, 0x38, 0x3e, 0x5c- , 0xf6, 0x91, 0x44, 0x1c, 0x04, 0xf7, 0x80, 0x45 - , 0xdb, 0x9c, 0xcb, 0xe6, 0x08, 0xa2, 0x8d, 0x6a- , 0x5d, 0x28, 0x68, 0x93, 0x43, 0xa8, 0xb2, 0x65- , 0x2b, 0xc3, 0x1e, 0xa3, 0x70, 0xcc, 0x1e, 0x40- , 0xee, 0x7d, 0xa8, 0x19, 0x00, 0x72, 0x1e, 0x19 - , 0x46, 0xb1, 0x18, 0x6d, 0x9c, 0x7d, 0x88, 0x59- , 0x1b, 0x25, 0x01, 0x5d, 0x9d, 0xd7, 0x7c, 0x6d+ [ 0x45, 0x34, 0xac, 0xc5, 0xda, 0x60, 0xe1, 0x0f+ , 0xbf, 0x17, 0x5d, 0xc7, 0x4c, 0xfc, 0x96, 0xe2+ , 0xe9, 0x7a, 0x49, 0x58, 0x38, 0x74, 0x0f, 0x30+ , 0x42, 0x30, 0xcc, 0x39, 0x96, 0x2b, 0x1e, 0xd8+ , 0x1c, 0x82, 0x76, 0x82, 0x65, 0x09, 0xb3, 0xd8+ , 0x31, 0xa6, 0xd2, 0xec, 0x36, 0xdd, 0x80, 0x8a+ , 0x7b, 0x24, 0x56, 0x3e, 0x37, 0xe7, 0x20, 0x68+ , 0x45, 0x1f, 0x15, 0xa8, 0xf2, 0x17, 0xb3, 0xd5+ , 0x78, 0x48, 0xef, 0xc5, 0x73, 0xa6, 0x5c, 0x3c+ , 0x07, 0x11, 0xe2, 0x76, 0xc0, 0xeb, 0xc3, 0xf1+ , 0x96, 0x24, 0xb4, 0x8e, 0xdf, 0x94, 0xba, 0x4d+ , 0x67, 0x2f, 0xc5, 0x6f, 0x4a, 0x5d, 0x85, 0x7e ] ]