{-# LANGUAGE DataKinds, DeriveDataTypeable, DeriveGeneric, GeneralizedNewtypeDeriving, TemplateHaskell #-} ----------------------------------------------------------------------------- -- | -- Module : Crypto.SecretSharing.Internal -- Copyright : Peter Robinson 2014 -- License : LGPL -- -- Stability : experimental -- Portability : portable -- ----------------------------------------------------------------------------- module Crypto.SecretSharing.Internal where import Data.ByteString.Lazy( ByteString ) import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BLC import qualified Data.List as L import Data.Char import Data.Vector( Vector ) import qualified Data.Vector as V import Data.Typeable import Control.Exception import Control.Monad import Data.Binary( Binary ) import GHC.Generics import Data.FiniteField.PrimeField as PF import Data.FiniteField.Base(FiniteField,order) import System.Random.Dice -- | Evaluate a Lagrange interpolation polynomial -- passing through the specified set of points. polyInterp :: Fractional a => [(a, a)] -> a -> a polyInterp xys x = sum $ map evalBasisPoly $ slidingFocus xys where evalBasisPoly (left, (xj, yj), right) = yj * product (map (\(xm, _) -> (x - xm) / (xj - xm)) (left ++ right)) -- [1,2,3] -> [([], 1, [2,3]), ([1], 2, [3]), ([2,1], 3, [])] slidingFocus :: [a] -> [([a], a, [a])] slidingFocus [] = [] slidingFocus (x : xs) = go [] x xs where go left focus right = (left, focus, right) : case right of [] -> [] focus' : right' -> go (focus : left) focus' right' -- | A share of an encoded byte. data ByteShare = ByteShare { shareId :: !Int -- ^ the index of this share , reconstructionThreshold :: !Int -- ^ number of shares required for -- reconstruction , shareValue :: !Int -- ^ the value of p(shareId) where p(x) is the -- generated (secret) polynomial } deriving(Typeable,Eq,Generic) instance Show ByteShare where show = show . shareValue -- | A share of the encoded secret. data Share = Share { theShare :: ![ByteShare] } deriving(Typeable,Eq,Generic) instance Show Share where show s = show (shareId $ head $ theShare s,BLC.pack $ map (chr . shareValue) $ theShare s) instance Binary ByteShare instance Binary Share -- | Encodes a 'ByteString' as a list of n shares, m of which are required for -- reconstruction. -- Lives in the 'IO' to access a random source. encode :: Int -- ^ m -> Int -- ^ n -> ByteString -- ^ the secret that we want to share -> IO [Share] -- a list of n-shares (per byte) encode m n bstr | n >= prime || m > n = throw $ AssertionFailed $ "encode: require n < " ++ show prime ++ " and m<=n." | BL.null bstr = return [] | otherwise = do let len = max 1 ((fromIntegral $ BL.length bstr) * (m-1)) coeffs <- (groupInto (m-1) . map fromIntegral . take len ) `liftM` (getDiceRolls prime len) let byteVecs = zipWith (encodeByte m n) coeffs $ map fromIntegral $ BL.unpack bstr return [ Share $ map (V.! (i-1)) byteVecs | i <- [1..n] ] -- | Reconstructs a (secret) bytestring from a list of (at least @m@) shares. -- Throws 'AssertionFailed' if the number of shares is too small. decode :: [Share] -- ^ list of at least @m@ shares -> ByteString -- ^ reconstructed secret decode [] = BL.pack [] decode shares@((Share s):_) | length shares < reconstructionThreshold (head s) = throw $ AssertionFailed "decode: not enough shares for reconstruction." | otherwise = let origLength = length s in let byteVecs = map (V.fromList . theShare) shares in let byteShares = [ map ((V.! (i-1))) byteVecs | i <- [1..origLength] ] in BL.pack . map (fromInteger . PF.toInteger . number) . map decodeByte $ byteShares encodeByte :: Int -> Int -> Polyn -> FField -> Vector ByteShare encodeByte m n coeffs secret = V.fromList[ ByteShare i m $ fromInteger . PF.toInteger . number $ evalPolynomial (secret:coeffs) (fromIntegral i::FField) | i <- [1..n] ] decodeByte :: [ByteShare] -> FField decodeByte ss = let m = reconstructionThreshold $ head ss in if length ss < m then throw $ AssertionFailed "decodeByte: insufficient number of shares for reconstruction!" else let shares = take m ss pts = map (\s -> (fromIntegral $ shareId s,fromIntegral $ shareValue s)) shares in polyInterp pts 0 -- | Groups a list into blocks of certain size. Running time: /O(n)/ groupInto :: Int -> [a] -> [[a]] groupInto num as | num < 0 = throw $ AssertionFailed "groupInto: Need positive number as argument." | otherwise = let (fs,ss) = L.splitAt num as in if L.null ss then [fs] else fs : groupInto num ss -- | A finite prime field. All computations are performed in this field. newtype FField = FField { number :: $(primeField $ fromIntegral 1021) } deriving(Show,Read,Ord,Eq,Num,Fractional,Generic,Typeable,FiniteField) -- | The size of the finite field prime :: Int prime = fromInteger $ order (0 :: FField) -- | A polynomial over the finite field given as a list of coefficients. type Polyn = [FField] -- | Evaluates the polynomial at a given point. evalPolynomial :: Polyn -> FField -> FField evalPolynomial coeffs x = foldr (\c res -> c + (x * res)) 0 coeffs -- let clist = zipWith (\pow c -> (\x -> c * (x^pow))) [0..] coeffs -- in L.foldl' (+) 0 [ c x | c <- clist ]