cipher-rc5 0.1.0.0 → 0.1.0.1
raw patch · 2 files changed
+95/−237 lines, 2 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- Crypto/Cipher/RC5.hs +94/−236
- cipher-rc5.cabal +1/−1
Crypto/Cipher/RC5.hs view
@@ -9,10 +9,10 @@ -- <http://en.wikipedia.org/wiki/RC5> -- -- You need to select a block size and number of rounds. --- If you are unsure, the most common settings are 64bit blocks with 12 rounds. +-- If you are unsure, the most common settings are 32bit blocks with 12 rounds. -- -- This implementation supports all the standard block lengths of 32, 64 & 128 bits. --- It even includes support for non-standard (not recommended) 16bit blocks. +-- It also includes support for non-standard (not recommended) 16bit blocks. -- -- In addition to being useful when required for e.g. legacy integration, -- this cipher's option of short block lengths makes it useful for encrypting @@ -29,81 +29,90 @@ ---------------------------------------------------------------------------- --- | RC5 encryption +-- | RC5 Cipher -- -- Using the given blocksize, number of rounds and key, encrypts the plaintext. -- --- * Valid blocksizes are 16, 32, 64, 128 +-- * Valid blocksizes are 16 (not standard), 32, 64, 128 -- -- * Valid rounds are 0 - 256 -- --- If in doubt, 32bit blocks and 12 rounds is the most common combination +-- If in doubt, 64bit blocks and 12 rounds is the most common combination. +-- This is called RC5-32/12 (32 is the word size, which is half the block size). +-- 128bit blocks and 18 rounds is also quite common. This is called RC5-64/18 -- --- >encrypt 32 12 [1,2,3,4] [0xFE,0x13,0x37,0x00] +-- >encrypt 64 12 [1,2,3,4] [0xFE,0x13,0x37,0x00] -- --- Encrypts the plaintext (last) with a blocksize of 32 bits, 12 rounds and key @[1,2,3,4]@ +-- Encrypts the plaintext @[0xFE,0x13,0x37,0x00]@ with a blocksize of 64 bits, 12 rounds and key @[1,2,3,4]@ -- -- Maximum key length is 256. A common (and sufficient) length is 16 bytes. -- The length of the result is divisible by the block size (i.e. 2, 4, 8, 16) -- On invalid input, the empty list is returned. -encrypt :: Int -> Int -> [Word8] -> [Word8] -> [Word8] +encrypt :: Int -- ^ Blocksize in bits (16, 32, 64 or 128) + -> Int -- ^ Number of rounds (0 - 256) + -> [Word8] -- ^ Key (max length 256) + -> [Word8] -- ^ Plaintext + -> [Word8] -- ^ Ciphertext encrypt blocksize rounds key plain - | length key > 256 || null key || null plain || rounds > 256 || rounds < 0 = [] - | blocksize == 16 = crypt8 encryptblock8 key rounds plain - | blocksize == 32 = crypt16 encryptblock16 key rounds plain - | blocksize == 64 = crypt32 encryptblock32 key rounds plain - | blocksize == 128 = crypt64 encryptblock64 key rounds plain + | null key || null plain || length (take 257 key) == 257 || rounds > 256 || rounds < 0 = [] + | blocksize == 16 = crypt ws p8 q8 encryptblock rounds key plain + | blocksize == 32 = crypt ws p16 q16 encryptblock rounds key plain + | blocksize == 64 = crypt ws p32 q32 encryptblock rounds key plain + | blocksize == 128 = crypt ws p64 q64 encryptblock rounds key plain | otherwise = [] + where ws = shiftR blocksize 4 -- | RC5 decryption -- -- All parameters must match those used for encryption -- The length of the result is equal to the length of the input -decrypt :: Int -> Int -> [Word8] -> [Word8] -> [Word8] +decrypt :: Int -- ^ Blocksize in bits + -> Int -- ^ Number of rounds + -> [Word8] -- ^ Key + -> [Word8] -- ^ Ciphertext + -> [Word8] -- ^ Recovered plaintext decrypt blocksize rounds key cipher | length key > 256 || null key || null cipher || rounds > 256 || rounds < 0 = [] - | blocksize == 16 = crypt8 decryptblock8 key rounds cipher - | blocksize == 32 = crypt16 decryptblock16 key rounds cipher - | blocksize == 64 = crypt32 decryptblock32 key rounds cipher - | blocksize == 128 = crypt64 decryptblock64 key rounds cipher + | blocksize == 16 = crypt ws p8 q8 decryptblock rounds key cipher + | blocksize == 32 = crypt ws p16 q16 decryptblock rounds key cipher + | blocksize == 64 = crypt ws p32 q32 decryptblock rounds key cipher + | blocksize == 128 = crypt ws p64 q64 decryptblock rounds key cipher | otherwise = [] - ---Blocksize 16bit/2B, wordsize Word8 -ws8_w = 8 :: Int -- W -ws8_ww = 1 :: Int -- w/8 - ---Blocksize 32bit/4B, wordsize Word16 -ws16_w = 16 :: Int -- W -ws16_ww = 2 :: Int -- w/8 - ---Blocksize 64bit/8B, wordsize Word32 -ws32_w = 32 :: Int -- W -ws32_ww = 4 :: Int -- w/8 - ---Blocksize 128bit/16B, wordsize Word64 -ws64_w = 64 :: Int -- W -ws64_ww = 8 :: Int -- w/8 + where ws = shiftR blocksize 4 -- Magic constants --- (they are easily calculated from euler's number and the golden ratio) -p8 = 0xb7 :: Word8 -q8 = 0x9f :: Word8 -p16 = 0xb7e1 :: Word16 -q16 = 0x9e37 :: Word16 -p32 = 0xb7e15163 :: Word32 -q32 = 0x9e3779b9 :: Word32 -p64 = 0xb7e151628aed2a6b :: Word64 -q64 = 0x9e3779b97f4a7c15 :: Word64 +p8 = 0xb7 :: Word8 -- Two constants, Pw and Qw, are defined for +q8 = 0x9f :: Word8 -- any word size W by the expressions: +p16 = 0xb7e1 :: Word16 -- Pw = odd $ ((exp 1) - 2) * (2 ** W) +q16 = 0x9e37 :: Word16 -- Qw = odd $ ((1+sqrt 5)/2-1)* (2**W) +p32 = 0xb7e15163 :: Word32 -- odd(x) adds one if x is even. +q32 = 0x9e3779b9 :: Word32 -- Note that the magic is all dependent on +p64 = 0xb7e151628aed2a6b :: Word64 -- euler's number and the golden ratio. +q64 = 0x9e3779b97f4a7c15 :: Word64 -- No real magic going on here. --- Examples for RC5/32/12/16 +-- Example & selftest for RC5/32/12/16. From the appendix of the Rivest reference paper key1 = take 16 $ repeat 0 :: [Word8] key2 = [0x91,0x5F,0x46,0x19,0xBE,0x41,0xB2,0x51,0x63,0x55,0xA5,0x01,0x10,0xA9,0xCE,0x91] :: [Word8] plain1 = take 8 $ repeat 0 :: [Word8] -plain2 = [0x21,0xA5,0xDB,0xEE,0x15,0x4B,0x8F,0x6D] :: [Word8] -plain2' = (0xEEDBA521,0x6D8F4B15) :: (Word32,Word32) +cipher1 = [0x21,0xA5,0xDB,0xEE,0x15,0x4B,0x8F,0x6D] :: [Word8] +cipher1' = (0xEEDBA521,0x6D8F4B15) :: (Word32,Word32) +selftestresults = [[33,165,219,238,21,75,143,109] + ,[247,192,19,172,91,43,137,82] + ,[47,66,179,183,3,105,252,146] + ,[101,193,120,178,132,209,151,204] + ,[235,68,228,21,218,49,152,36]] + +selftest = selftest' key1 plain1 selftestresults + +selftest' key plain fasit + | null fasit = [] + | otherwise = [((decrypt 64 12 key cipher) == plain) && (cipher == (head fasit))] ++ selftest' (nextkey cipher) cipher (tail fasit) + where cipher = encrypt 64 12 key plain + nextkey cipher = map (\j -> fromIntegral (((bytes2word (take 4 cipher))::Word32) `mod` (255-j))) [0..15] + -- Left rotate for encryption rotl :: Bits a => a -> Int -> Int -> a rotl x s w = (shiftL x (s .&. (w-1))) .|. (shiftR x (w-(s .&. (w-1)))) @@ -112,194 +121,59 @@ rotr :: Bits a => a -> Int -> Int -> a rotr x s w = (shiftR x (s .&. (w-1))) .|. (shiftL x (w-(s .&. (w-1)))) -crypt8 :: ([Word8] -> Int -> (Word8,Word8) -> [Word8]) -> [Word8] -> Int -> [Word8] -> [Word8] -crypt8 operation key rounds plain = - concatMap (operation s rounds) ab - where ab = splitAB8 plain - s = keyexpand8 key rounds - -crypt16 :: ([Word16] -> Int -> (Word16,Word16) -> [Word8]) -> [Word8] -> Int -> [Word8] -> [Word8] -crypt16 operation key rounds content = - concatMap (operation s rounds) ab - where ab = splitAB16 content - s = keyexpand16 key rounds - -crypt32 :: ([Word32] -> Int -> (Word32,Word32) -> [Word8]) -> [Word8] -> Int -> [Word8] -> [Word8] -crypt32 operation key rounds content = - concatMap (operation s rounds) ab - where ab = splitAB32 content - s = keyexpand32 key rounds - -crypt64 :: ([Word64] -> Int -> (Word64,Word64) -> [Word8]) -> [Word8] -> Int -> [Word8] -> [Word8] -crypt64 operation key rounds content = - concatMap (operation s rounds) ab - where ab = splitAB64 content - s = keyexpand64 key rounds - - -encryptblock8 :: [Word8] -> Int -> (Word8,Word8) -> [Word8] -encryptblock8 s rounds (a,b) = - [a',b'] - where (a',b') = enc8 rounds 1 (a + (s!!0)) (b + (s!!1)) s - -encryptblock16 :: [Word16] -> Int -> (Word16,Word16) -> [Word8] -encryptblock16 s rounds (a,b) = - word2bytes 2 a' ++ word2bytes 2 b' - where (a',b') = enc16 rounds 1 (a + (s!!0)) (b + (s!!1)) s - -encryptblock32 :: [Word32] -> Int -> (Word32,Word32) -> [Word8] -encryptblock32 s rounds (a,b) = - word2bytes 4 a' ++ word2bytes 4 b' - where (a',b') = enc32 rounds 1 (a + (s!!0)) (b + (s!!1)) s - -encryptblock32' :: [Word32] -> Int -> (Word32,Word32) -> (Word32,Word32) -encryptblock32' s rounds (a,b) = (a',b') - where (a',b') = enc32 rounds 1 (a + (s!!0)) (b + (s!!1)) s - -encryptblock64 :: [Word64] -> Int -> (Word64,Word64) -> [Word8] -encryptblock64 s rounds (a,b) = - word2bytes 8 a' ++ word2bytes 8 b' - where (a',b') = enc64 rounds 1 (a + (s!!0)) (b + (s!!1)) s - -decryptblock8 :: [Word8] -> Int -> (Word8,Word8) -> [Word8] -decryptblock8 s rounds (a,b) = - (a' - s!!0) : [(b' - s!!1)] - where (a',b') = dec8 rounds a b s - -decryptblock16 :: [Word16] -> Int -> (Word16,Word16) -> [Word8] -decryptblock16 s rounds (a,b) = - word2bytes 2 (a' - s!!0) ++ word2bytes 2 (b' - s!!1) - where (a',b') = dec16 rounds a b s - -decryptblock32 :: [Word32] -> Int -> (Word32,Word32) -> [Word8] -decryptblock32 s rounds (a,b) = - word2bytes 4 (a' - s!!0) ++ word2bytes 4 (b' - s!!1) - where (a',b') = dec32 rounds a b s - -decryptblock32' :: [Word32] -> Int -> (Word32,Word32) -> (Word32,Word32) -decryptblock32' s rounds (a,b) = ((a' - s!!0) , (b' - s!!1)) - where (a',b') = dec32 rounds a b s +crypt :: (Bits a, Integral a) => Int -> a -> a -> (Int -> [a] -> Int -> (a,a) -> [Word8]) -> Int -> [Word8] -> [Word8] -> [Word8] +crypt ws p q operation rounds key content = + concatMap (operation ws s rounds) ab + where ab = splitAB ws content + s = keyexpand ws p q key rounds -decryptblock64 :: [Word64] -> Int -> (Word64,Word64) -> [Word8] -decryptblock64 s rounds (a,b) = - word2bytes 8 (a' - s!!0) ++ word2bytes 8 (b' - s!!1) - where (a',b') = dec64 rounds a b s - -enc8 :: Int -> Int -> Word8 -> Word8 -> [Word8] -> (Word8,Word8) -enc8 rounds i a b s - | i > rounds = (a,b) - | otherwise = enc8 rounds (i+1) a' b' s - where a' = (rotl (a `xor` b) (fromIntegral b) ws8_w) + (s !! (2*i)) - b' = (rotl (b `xor` a') (fromIntegral a') ws8_w) + (s !! (2*i+1)) - -enc16 :: Int -> Int -> Word16 -> Word16 -> [Word16] -> (Word16,Word16) -enc16 rounds i a b s - | i > rounds = (a,b) - | otherwise = enc16 rounds (i+1) a' b' s - where a' = (rotl (a `xor` b) (fromIntegral b) ws16_w) + (s !! (2*i)) - b' = (rotl (b `xor` a') (fromIntegral a') ws16_w) + (s !! (2*i+1)) - -enc32 :: Int -> Int -> Word32 -> Word32 -> [Word32] -> (Word32,Word32) -enc32 rounds i a b s - | i > rounds = (a,b) - | otherwise = enc32 rounds (i+1) a' b' s - where a' = (rotl (a `xor` b) (fromIntegral b) ws32_w ) + (s !! (2*i)) - b' = (rotl (b `xor` a') (fromIntegral a') ws32_w ) + (s !! (2*i+1)) +encryptblock :: (Bits a, Integral a) => Int -> [a] -> Int -> (a,a) -> [Word8] +encryptblock ws s rounds (a,b) = + word2bytes ws a' ++ word2bytes ws b' + where (a',b') = enc (ws*8) rounds 1 (a + (s!!0)) (b + (s!!1)) s + +decryptblock :: (Bits a, Integral a) => Int -> [a] -> Int -> (a,a) -> [Word8] +decryptblock ws s rounds (a,b) = + word2bytes ws (a' - s!!0) ++ word2bytes ws (b' - s!!1) + where (a',b') = dec (ws*8) rounds a b s -enc64 :: Int -> Int -> Word64 -> Word64 -> [Word64] -> (Word64,Word64) -enc64 rounds i a b s +enc :: (Bits a, Integral a) => Int -> Int -> Int -> a -> a -> [a] -> (a,a) +enc mask rounds i a b s | i > rounds = (a,b) - | otherwise = enc64 rounds (i+1) a' b' s - where a' = (rotl (a `xor` b) (fromIntegral b) ws64_w ) + (s !! (2*i)) - b' = (rotl (b `xor` a') (fromIntegral a') ws64_w ) + (s !! (2*i+1)) - -dec8 :: Int -> Word8 -> Word8 -> [Word8] -> (Word8,Word8) -dec8 i a b s - | i == 0 = (a,b) - | otherwise = dec8 (i-1) a' b' s - where b' = (rotr (b - (s !! (2*i+1))) (fromIntegral a) ws8_w) `xor` a - a' = (rotr (a - (s !! (2*i))) (fromIntegral b') ws8_w) `xor` b' - -dec16 :: Int -> Word16 -> Word16 -> [Word16] -> (Word16,Word16) -dec16 i a b s - | i == 0 = (a,b) - | otherwise = dec16 (i-1) a' b' s - where b' = (rotr (b - (s !! (2*i+1))) (fromIntegral a) ws16_w) `xor` a - a' = (rotr (a - (s !! (2*i))) (fromIntegral b') ws16_w) `xor` b' - -dec32 :: Int -> Word32 -> Word32 -> [Word32] -> (Word32,Word32) -dec32 i a b s - | i == 0 = (a,b) - | otherwise = dec32 (i-1) a' b' s - where b' = (rotr (b - (s !! (2*i+1))) (fromIntegral a) ws32_w ) `xor` a - a' = (rotr (a - (s !! (2*i))) (fromIntegral b') ws32_w ) `xor` b' + | otherwise = enc mask rounds (i+1) a' b' s + where a' = (rotl (a `xor` b) (fromIntegral b) mask) + (s !! (2*i)) + b' = (rotl (b `xor` a') (fromIntegral a') mask) + (s !! (2*i+1)) -dec64 :: Int -> Word64 -> Word64 -> [Word64] -> (Word64,Word64) -dec64 i a b s +dec :: (Bits a, Integral a) => Int -> Int -> a -> a -> [a] -> (a,a) +dec mask i a b s | i == 0 = (a,b) - | otherwise = dec64 (i-1) a' b' s - where b' = (rotr (b - (s !! (2*i+1))) (fromIntegral a) ws64_w ) `xor` a - a' = (rotr (a - (s !! (2*i))) (fromIntegral b') ws64_w ) `xor` b' - -splitAB8 :: [Word8] -> [(Word8,Word8)] -splitAB8 plain = map pair ab8' - where ab8' = chunksOf 2 plain - -splitAB16 :: [Word8] -> [(Word16,Word16)] -splitAB16 plain = map pair ab16' - where chunks = chunksOf ws16_ww plain - ab16 = map bytes2word chunks - ab16' = chunksOf 2 ab16 + | otherwise = dec mask (i-1) a' b' s + where b' = (rotr (b - (s !! (2*i+1))) (fromIntegral a) mask) `xor` a + a' = (rotr (a - (s !! (2*i))) (fromIntegral b') mask) `xor` b' -splitAB32 :: [Word8] -> [(Word32,Word32)] -splitAB32 plain = map pair ab32' - where chunks = chunksOf ws32_ww plain - ab32 = map bytes2word chunks - ab32' = chunksOf 2 ab32 +splitAB :: (Bits a, Integral a) => Int -> [Word8] -> [(a,a)] +splitAB ws bs = map pair ab + where chunks = chunksOf ws bs + ab = chunksOf 2 (map bytes2word chunks) -splitAB64 :: [Word8] -> [(Word64,Word64)] -splitAB64 plain = map pair ab64' - where chunks = chunksOf ws64_ww plain - ab64 = map bytes2word chunks - ab64' = chunksOf 2 ab64 - pair :: Integral a => [a] -> (a,a) pair (a:b:_) = (a,b) pair (a:[]) = (a,0) - -- KEY INIT & EXPANSION -keyexpand8 :: [Word8] -> Int -> [Word8] -keyexpand8 key rounds = - mixsecretkey ws8_w s l -- mix in secret key - where l = key -- convert key to words - s = makeS (2*rounds+2) p8 q8 -- init S table - -keyexpand16 :: [Word8] -> Int -> [Word16] -keyexpand16 key rounds = - mixsecretkey ws16_w s l -- mix in secret key - where l = makewordkey16 key -- convert key to words - s = makeS (2*rounds+2) p16 q16 -- init S table - -keyexpand32 :: [Word8] -> Int -> [Word32] -keyexpand32 key rounds = - mixsecretkey ws32_w s l -- mix in secret key - where l = makewordkey32 key -- convert key to words - s = makeS (2*rounds+2) p32 q32 -- init S table - -keyexpand64 :: [Word8] -> Int -> [Word64] -keyexpand64 key rounds = - mixsecretkey ws64_w s l -- mix in secret key - where l = makewordkey64 key -- convert key to words - s = makeS (2*rounds+2) p64 q64 -- init S table +keyexpand :: (Bits a, Integral a) => Int -> a -> a -> [Word8] -> Int -> [a] +keyexpand ws p q key rounds = mixsecretkey ws s l -- mix in secret key + where l = makewordkey ws key -- convert key to words + s = makeS (2*rounds+2) p q -- init S table mixsecretkey :: (Bits a, Integral a) => Int -> [a] -> [a] -> [a] mixsecretkey bs s l = s' where k = if ll > t then 3 * ll else 3 * t ll = length l t = length s - (s',l') = mixS bs k 0 0 0 0 s l t ll + (s',l') = mixS (bs*8) k 0 0 0 0 s l t ll +-- Mixes S box with key. Paramter names may look cryptic, but matches those in standard mixS :: (Bits a, Integral a) => Int -> Int -> a -> a -> Int -> Int -> [a] -> [a] -> Int -> Int -> ([a],[a]) mixS bs k a b i j s l t ll | k == 0 = (s,l) @@ -311,32 +185,16 @@ s' = (take i s) ++ [a'] ++ (drop (i+1) s) l' = (take j l) ++ [b'] ++ (drop (j+1) l) +-- Creates S box. Could be precomputed for the most common variants. makeS :: Integral a => Int -> a -> a -> [a] makeS t seed const | t == 0 = [] | otherwise = seed : makeS (t-1) (seed + const) const -{-- -makewordkey8 :: [Word8] -> [Word8] -makewordkey8 key = map sum chunks - where expokey = map (\(k,m) -> shiftL (fromIntegral k) m) (zip key (repeat 0)) - chunks = chunksOf ws8_ww expokey ---} - -makewordkey16 :: [Word8] -> [Word16] -makewordkey16 key = map sum chunks - where expokey = map (\(k,m) -> shiftL (fromIntegral k) m) (zip key (cycle [0,8])) - chunks = chunksOf ws16_ww expokey - -makewordkey32 :: [Word8] -> [Word32] -makewordkey32 key = map sum chunks - where expokey = map (\(k,m) -> shiftL (fromIntegral k) m) (zip key (cycle [0,8,16,24])) - chunks = chunksOf ws32_ww expokey - -makewordkey64 :: [Word8] -> [Word64] -makewordkey64 key = map sum chunks - where expokey = map (\(k,m) -> shiftL (fromIntegral k) m) (zip key (cycle [0,8,16,24,32,40,48,56])) - chunks = chunksOf ws64_ww expokey +makewordkey :: (Bits a, Integral a) => Int -> [Word8] -> [a] +makewordkey ws key = map sum chunks + where expokey = map (\(k,m) -> shiftL (fromIntegral k) m) (zip key (cycle (take ws [0,8..]))) + chunks = chunksOf ws expokey bytes2word :: (Bits a, Integral a) => [Word8] -> a bytes2word bs = bytes2word' 0 (fromIntegral 0) bs
cipher-rc5.cabal view
@@ -1,5 +1,5 @@ name: cipher-rc5 -version: 0.1.0.0 +version: 0.1.0.1 synopsis: Pure RC5 implementation description: Pure RC5 implementation homepage: http://github.com/fegu/cipher-rc5