2010-09-09 21:47:19 +00:00
|
|
|
-- this is probably not a very good random interface, nor it has any good randomness capability.
|
|
|
|
-- the module is just here until a really good CPRNG implementation come up..
|
|
|
|
module Network.TLS.SRandom
|
|
|
|
( SRandomGen
|
|
|
|
, makeSRandomGen
|
|
|
|
, getRandomBytes
|
|
|
|
) where
|
|
|
|
|
|
|
|
import Data.Word
|
2010-11-04 19:05:36 +00:00
|
|
|
import Crypto.Random
|
|
|
|
import System.Crypto.Random (getEntropy)
|
|
|
|
import Data.ByteString (ByteString)
|
|
|
|
import qualified Data.ByteString as B
|
|
|
|
import qualified Codec.Crypto.AES as AES
|
2010-11-07 10:19:12 +00:00
|
|
|
import Data.Bits (xor)
|
2010-11-04 19:05:36 +00:00
|
|
|
import Data.Serialize
|
|
|
|
|
|
|
|
{-
|
|
|
|
- the following CPRNG is an AES cbc based counter system.
|
|
|
|
-
|
|
|
|
- 16 bytes IV, 16 bytes counter, 32 bytes key
|
|
|
|
- (IV `xor` counter) `aes` key -> 16 bytes output
|
|
|
|
-}
|
|
|
|
|
|
|
|
data Word128 = Word128 !Word64 !Word64
|
|
|
|
|
|
|
|
data SRandomGen = RNG !ByteString !Word128 !ByteString
|
|
|
|
|
|
|
|
instance Show SRandomGen where
|
|
|
|
show _ = "srandomgen[..]"
|
|
|
|
|
|
|
|
put128 :: Word128 -> ByteString
|
|
|
|
put128 (Word128 a b) = runPut (putWord64host a >> putWord64host b)
|
2010-09-09 21:47:19 +00:00
|
|
|
|
2010-11-04 19:05:36 +00:00
|
|
|
get128 :: ByteString -> Word128
|
|
|
|
get128 = either (\_ -> Word128 0 0) id . runGet (getWord64host >>= \a -> (getWord64host >>= \b -> return $ Word128 a b))
|
2010-09-09 21:47:19 +00:00
|
|
|
|
2010-11-04 19:05:36 +00:00
|
|
|
add1 :: Word128 -> Word128
|
|
|
|
add1 (Word128 a b) = if b == 0xffffffffffffffff then Word128 (a+1) 0 else Word128 a (b+1)
|
2010-09-09 21:47:19 +00:00
|
|
|
|
2010-11-07 10:19:12 +00:00
|
|
|
makeParams :: ByteString -> (ByteString, ByteString, ByteString)
|
|
|
|
makeParams b = (key, cnt, iv)
|
|
|
|
where
|
|
|
|
key = B.take 32 left2
|
|
|
|
(cnt, left2) = B.splitAt 16 left1
|
|
|
|
(iv, left1) = B.splitAt 16 b
|
2010-09-09 21:47:19 +00:00
|
|
|
|
2010-11-04 19:05:36 +00:00
|
|
|
make :: B.ByteString -> Either GenError SRandomGen
|
|
|
|
make b
|
|
|
|
| B.length b < 64 = Left NotEnoughEntropy
|
|
|
|
| otherwise = Right $ RNG iv (get128 cnt) key
|
|
|
|
where
|
2010-11-07 10:19:12 +00:00
|
|
|
(key, cnt, iv) = makeParams b
|
2010-11-04 19:05:36 +00:00
|
|
|
|
|
|
|
chunkSize :: Int
|
|
|
|
chunkSize = 16
|
|
|
|
|
2010-11-07 10:19:12 +00:00
|
|
|
bxor :: ByteString -> ByteString -> ByteString
|
|
|
|
bxor a b = B.pack $ B.zipWith xor a b
|
|
|
|
|
2010-11-04 19:05:36 +00:00
|
|
|
nextChunk :: SRandomGen -> (ByteString, SRandomGen)
|
|
|
|
nextChunk (RNG iv counter key) = (chunk, newrng)
|
|
|
|
where
|
|
|
|
newrng = RNG chunk (add1 counter) key
|
|
|
|
chunk = AES.crypt' AES.CBC key iv AES.Encrypt bytes
|
|
|
|
bytes = iv `bxor` (put128 counter)
|
|
|
|
|
|
|
|
makeSRandomGen :: IO (Either GenError SRandomGen)
|
|
|
|
makeSRandomGen = getEntropy 64 >>= return . make
|
|
|
|
|
|
|
|
getRandomBytes :: SRandomGen -> Int -> (ByteString, SRandomGen)
|
2010-09-09 21:47:19 +00:00
|
|
|
getRandomBytes rng n =
|
|
|
|
let list = helper rng n in
|
2010-11-04 19:05:36 +00:00
|
|
|
(B.concat $ map fst list, snd $ last list)
|
2010-09-09 21:47:19 +00:00
|
|
|
where
|
|
|
|
helper _ 0 = []
|
|
|
|
helper g i =
|
2010-11-04 19:05:36 +00:00
|
|
|
let (b, g') = nextChunk g in
|
|
|
|
if chunkSize >= i
|
|
|
|
then [ (B.take i b, g') ]
|
|
|
|
else (b, g') : helper g' (i-chunkSize)
|
|
|
|
|
|
|
|
instance CryptoRandomGen SRandomGen where
|
|
|
|
newGen = make
|
|
|
|
genSeedLength = 64
|
|
|
|
genBytes len rng = Right $ getRandomBytes rng len
|
2010-11-07 10:19:12 +00:00
|
|
|
reseed b rng@(RNG _ cnt1 _)
|
|
|
|
| B.length b < 64 = Left NotEnoughEntropy
|
|
|
|
| otherwise = Right $ RNG (r16 `bxor` iv2) (get128 (put128 cnt1 `bxor` cnt2)) key2
|
|
|
|
where
|
|
|
|
(r16, _) = nextChunk rng
|
|
|
|
(key2, cnt2, iv2) = makeParams b
|