ginsu-0.8.0: Gale/KeyCache.hs
{-# LANGUAGE PatternGuards #-}
module Gale.KeyCache(
dumpKey,
KeyCache,
newKeyCache,
getKey,
getPKey,
parseKey,
keyIsPubKey,
keyIsPrivKey,
keyIsPublic,
putKey,
noKey,
numberKeys,
keyRequestPuff
) where
import Atom
import Bits
import Char
import Control.Concurrent
import Directory
import EIO
import ErrorLog
import Gale.Proto(decodeTime, decodeFrags, xdrReadUInt, getGaleDir, decodeFragments)
import GenUtil
import List
import Monad
import PackedString
import Gale.Puff
import RSA
import System.Mem.Weak
import Word
import qualified Data.Map as Map
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import Text.ParserCombinators.ReadP.ByteString
import Data.Binary.Get(runGet,Get())
stons :: [Char] -> [Word8]
stons = map (fromIntegral . ord)
private_magic1, private_magic2, private_magic3 :: BS.ByteString
pubkey_magic1, pubkey_magic2 :: BS.ByteString
private_magic1 = BS.pack $ stons "h\DC3\000\001"
private_magic2 = BS.pack $ stons "h\DC3\000\003"
private_magic3 = BS.pack $ stons "GALE\000\002"
pubkey_magic1 = BS.pack $ stons "h\DC3\000\000"
pubkey_magic2 = BS.pack $ stons "h\DC3\000\002"
pubkey_magic3 = BS.pack $ stons "GALE\000\001"
galeRSAModulusBits = 1024
galeRSAModulusLen = (galeRSAModulusBits + 7) `div` 8
galeRSAPrimeBits = (galeRSAModulusBits + 1) `div` 2
galeRSAPrimeLen = (galeRSAPrimeBits + 7) `div` 8
data KeyCache = KeyCache {
pkeyCache :: !(MVar (Map.Map String (Weak (Key,EvpPkey)))),
kkeyCache :: !(MVar (Map.Map String (Weak Key))),
galeDir :: String
}
numberKeys kc = fmap Map.size $ readMVar (kkeyCache kc)
keyIsPubKey k = (hasFragment k f_rsaExponent)
keyIsPrivKey k = (hasFragment k f_rsaPrivateExponent)
keyIsPublic k = any nullPS $ getFragmentStrings k f_keyMember
newKeyCache galeDir = do
pk <- newMVar Map.empty
kk <- newMVar Map.empty
return KeyCache { {- keyCache = kc, publicKeyCache = pkc,-} galeDir = galeDir, pkeyCache = pk, kkeyCache = kk }
keyToRSAElems :: Monad m => Key -> m (RSAElems BS.ByteString)
keyToRSAElems fl = do
if not (keyIsPubKey fl) then fail "key does not have bits" else do
n <- getFragmentData fl f_rsaModulus
e <- getFragmentData fl f_rsaExponent
if not (keyIsPrivKey fl) then
return RSAElemsPublic { rsaN = n, rsaE = e } else do
d <- getFragmentData fl f_rsaPrivateExponent
iqmp <- getFragmentData fl f_rsaPrivateCoefficient
pq <- getFragmentData fl f_rsaPrivatePrime
dmpq1 <- getFragmentData fl f_rsaPrivatePrimeExponent
let (p,q) = BS.splitAt galeRSAPrimeLen pq -- should be "rsa.bits"?
(dmp1,dmq1) = BS.splitAt galeRSAPrimeLen dmpq1
return RSAElemsPrivate {
rsaN = n,
rsaE = e,
rsaD = d ,
rsaIQMP = iqmp,
rsaP = p,
rsaQ = q,
rsaDMP1 = dmp1,
rsaDMQ1 = dmq1
}
{-
keyToRSAElems :: Monad m => Key -> m (RSAElems [Word8])
keyToRSAElems fl = do
if not (keyIsPubKey fl) then fail "key does not have bits" else do
n <- getFragmentData fl f_rsaModulus
e <- getFragmentData fl f_rsaExponent
if not (keyIsPrivKey fl) then
return RSAElemsPublic { rsaN = BS.unpack n, rsaE = BS.unpack e } else do
d <- getFragmentData fl f_rsaPrivateExponent
iqmp <- getFragmentData fl f_rsaPrivateCoefficient
pq <- getFragmentData fl f_rsaPrivatePrime
dmpq1 <- getFragmentData fl f_rsaPrivatePrimeExponent
let (p,q) = splitAt galeRSAPrimeLen (BS.unpack pq) -- should be "rsa.bits"?
(dmp1,dmq1) = splitAt galeRSAPrimeLen (BS.unpack dmpq1)
return RSAElemsPrivate {
rsaN = BS.unpack n,
rsaE = BS.unpack e,
rsaD = BS.unpack d ,
rsaIQMP = BS.unpack iqmp,
rsaP = p,
rsaQ = q,
rsaDMP1 = dmp1,
rsaDMQ1 = dmq1
}
-}
keyToPkey :: Key -> IO EvpPkey
keyToPkey key = do
re <- keyToRSAElems key
putLog LogDebug $ show re
createPkey re
getPKey :: KeyCache -> String -> IO (Maybe (Key,EvpPkey))
getPKey kc kn = modifyMVar (pkeyCache kc) f where
f pkeyCache | Just v <- Map.lookup kn pkeyCache = do
v <- deRefWeak v
case v of
Just _ -> return (pkeyCache, v)
Nothing -> g pkeyCache
f pkeyCache = g pkeyCache
g pkeyCache = do
k <- getKey kc kn
case k of
Nothing -> return (pkeyCache, Nothing)
Just v | not (hasFragment v f_rsaModulus) -> return (pkeyCache, Nothing)
Just key -> do
--rsa <- keyToRSA v
--pkey <- pkeyNewRSA rsa
pkey <- keyToPkey key
let kp = (key,pkey)
ptr <- mkWeakPtr kp Nothing
return (Map.insert kn ptr pkeyCache, Just kp)
blankKey kn = (Key kn [])
noKey :: KeyCache -> String -> IO ()
noKey kc kn = modifyMVar (kkeyCache kc) f where
f kkeyCache | Just _ <- Map.lookup kn kkeyCache = return (kkeyCache, ())
f kkeyCache = do
n <- mkWeakPtr (blankKey kn) Nothing
return (Map.insert kn n kkeyCache, ())
putKey :: KeyCache -> BS.ByteString -> IO ()
putKey kc xs = do
mk <- first [fmap Just (parseKey $ BS.unpack xs),return Nothing]
case mk of
Nothing -> return ()
Just v'@(Key kn _) -> do
modifyMVar (kkeyCache kc) f where
f kkeyCache | Just _ <- Map.lookup kn kkeyCache = return (kkeyCache, ())
f kkeyCache = do
first [createDirectory $ galeDir kc ++ "/auth/", return ()]
first [createDirectory $ galeDir kc ++ "/auth/cache/", return ()]
--xs <- unsafeThaw xs
--bnds <- getBounds xs
atomicWrite (galeDir kc ++ "/auth/cache/" ++ kn ++ ".gpub") $
\h -> BS.hPut h xs -- hPutArray h xs (rangeSize bnds)
v' <- mkWeakPtr v' Nothing
return (Map.insert kn v' kkeyCache, ())
getKey :: KeyCache -> String -> IO (Maybe Key)
getKey kc kn = modifyMVar (kkeyCache kc) f where
f kkeyCache | Just v <- Map.lookup kn kkeyCache = do
v <- deRefWeak v
case v of
Just _ -> return (kkeyCache, v)
Nothing -> g kkeyCache
f kkeyCache = g kkeyCache
g kkeyCache = do
v <- ioM getFromDisk
case v of
Nothing -> return (kkeyCache, Nothing)
Just v' -> do
v'' <- mkWeakPtr v' Nothing
return (Map.insert kn v'' kkeyCache, Just v')
getFromDisk :: IO Key
getFromDisk = do
let pc = galeDir kc ++ "/auth/cache/"
pp = galeDir kc ++ "/auth/private/"
knames = [ pp ++ kn ++ ".gpri", pp ++ kn ++ ".gpub", pp ++ kn , pc ++ kn ++ ".gpub" ]
gn = (map (\fn -> (readRawFile fn >>= parseKey >>= \c -> return (c,fn))) knames)
--putLog LogDebug $ "Looking for: " ++ show knames
xs <- mapM tryMost gn
let ks = [ k | Right (k@(Key n _),_) <- xs, kn == n]
let nk = foldr (\(Key _ fl) (Key n fl') -> Key n (fl `mergeFrags` fl')) (Key kn []) ks
--(key_c, fn) <- (first gn)
--key <- parseKey key_c
--rsa <- pubKeyToRSA key
--pkey <- pkeyNewRSA rsa
if null (getFragmentList nk) then ioError $ userError "bad key" else return nk
flipLocalPart :: String -> String
flipLocalPart s | '@' `notElem` s = s
flipLocalPart s = nbp ++ ep where
nbp = concat $ reverse (groupBy f bp)
(bp,ep) = span (/= '@') s
f '.' '.' = True
f x y | x /= '.' && y /= '.' = True
f _ _ = False
keyRequestPuff :: String -> Puff
keyRequestPuff s = emptyPuff { cats = [Category ("_gale.query." ++ n, d)], fragments = [(f_questionKey, FragmentText $ packString s), (f_questionKey',FragmentText $ packString s')]} where
s' = flipLocalPart s
Category (n,d) = catParseNew s
--la xs = listArray (0, length xs - 1) xs
--la xs = BS.pack xs
parseWord32 :: ReadP Word32
parseWord32 = do
b1 <- get
b2 <- get
b3 <- get
b4 <- get
return $ (fromIntegral b4) .|. (fromIntegral b3 `shiftL` 8) .|.
(fromIntegral b2 `shiftL` 16) .|. (fromIntegral b1 `shiftL` 24)
parseWord16 :: ReadP Word16
parseWord16 = do
b1 <- get
b2 <- get
return $ (fromIntegral b2) .|. (fromIntegral b1 `shiftL` 8)
parseIntegral32 = fmap fromIntegral parseWord32
keyDecode12 = do
bits <- parseIntegral32
modulusD <- splitRLE galeRSAModulusLen
exponentD <- splitRLE galeRSAModulusLen
privateExponentD <- splitRLE (galeRSAPrimeLen * 2)
privatePrimeD <- splitRLE galeRSAModulusLen
privatePrimeExponentD <- splitRLE (galeRSAPrimeLen * 2)
privateCoefficientD <- splitRLE galeRSAPrimeLen
let fl = [("rsa.modulus",FragmentData modulusD),
("rsa.exponent",FragmentData exponentD),
("rsa.private.exponent",FragmentData privateExponentD),
("rsa.private.prime",FragmentData privatePrimeD),
("rsa.private.prime.exponent",FragmentData privatePrimeExponentD),
("rsa.private.coefficient",FragmentData privateCoefficientD),
("rsa.bits", FragmentInt (fromIntegral bits))
]
return [ (fromString x,y) | (x,y) <- fl]
{-
(bits,ys) = xdrReadUInt xs
(modulusD,xs') = splitRLE galeRSAModulusLen ys
(exponentD,xs'') = splitRLE galeRSAModulusLen xs'
(privateExponentD,xs''') = splitRLE (galeRSAPrimeLen * 2) xs''
(privatePrimeD,xs'''') = splitRLE galeRSAModulusLen xs'''
(privatePrimeExponentD,xs''''') = splitRLE (galeRSAPrimeLen * 2) xs''''
(privateCoefficientD,_) = splitRLE galeRSAPrimeLen xs'''''
-}
eof = do
l <- look
guard $ BS.null l
parseNullString :: ReadP String
parseNullString = map (chr . fromIntegral) `fmap` manyTill get (char 0)
parseLenString :: ReadP String
parseLenString = do
len <- parseIntegral32
ws <- replicateM len parseWord16
return $ map (chr . fromIntegral) ws
keyParse :: ReadP Key
keyParse = choice [ppk1,ppk2,ppk3,pk1,pk2,pk3] where
ppk1 = do
string private_magic1
kn <- fmap flipLocalPart parseNullString
fl <- keyDecode12
return $ Key kn fl
ppk2 = do
string private_magic2
kn <- fmap flipLocalPart parseLenString
fl <- keyDecode12
return $ Key kn fl
ppk3 = do
string private_magic3
kn <- fmap flipLocalPart parseLenString
fl <- toParser decodeFrags
return $ Key kn fl
pk1 = do
string pubkey_magic1
kn <- fmap flipLocalPart parseNullString
(eof >> return (Key kn [])) +++ do
comment <- parseNullString
fl <- parsePublic12
skipMany get -- the signature
return $ Key kn ([(f_keyOwner, FragmentText (packString comment))] ++ fl)
pk2 = do
string pubkey_magic2
kn <- fmap flipLocalPart parseLenString
(eof >> return (Key kn [])) +++ do
comment <- parseLenString
fl' <- parsePublic12
let fl = ((f_keyOwner, FragmentText (packString comment)):fl')
(eof >> return (Key kn fl)) +++ do
ts <- replicateM 16 get
te <- replicateM 16 get
skipMany get
--_signature <- parseRest
return $ Key kn $ fl ++ [(f_keySigned,FragmentTime (decodeTime ts)), (f_keyExpires,FragmentTime (decodeTime te))]
pk3 = do
string pubkey_magic3
kn <- fmap flipLocalPart parseLenString
fl <- toParser decodeFrags
return $ Key kn fl
parsePublic12 = do
bits <- parseIntegral32
--modulus <- (MkP (\x -> Just (splitRLE galeRSAModulusLen x)))
--exponent <- (MkP (\x -> Just (splitRLE galeRSAModulusLen x)))
modulus <- splitRLE galeRSAModulusLen
exponent <- splitRLE galeRSAModulusLen
return [(f_rsaModulus, FragmentData modulus),
(f_rsaExponent, FragmentData exponent),
(f_rsaBits, FragmentInt $ fromIntegral bits)]
parser :: Monad m => ReadP a -> BS.ByteString -> m a
parser p bs = case readP_to_S p bs of
[(a,bs)] | BS.null bs -> return a
_ -> fail "parser failed"
parseKey :: [Word8] -> IO Key
parseKey xs = do
(Key kn fl) <- parser keyParse (BS.pack xs)
fl <- unsignFragments fl
return $ Key kn fl
splitRLE :: Int -> ReadP BS.ByteString
splitRLE n = f n [] where
f n _ | n < 0 = fail "invalid RLE encoding"
f 0 xs = return $ BS.concat (reverse xs)
f n rs = plain +++ rep where
plain = do
c <- get
guard $ c .&. 0x80 /= 0
let count = fromIntegral $ (c .&. 0x7f) + 1
(x,_) <- gather (skip count)
f (n - count) (x:rs)
rep = do
c <- get
guard $ c .&. 0x80 == 0
x <- get
let count = fromIntegral $ (c .&. 0x7f) + 1
f (n - count) (BS.replicate count x:rs)
{-
splitRLE :: Int -> [Word8] -> ([Word8], [Word8])
splitRLE n _ | n < 0 = error "invalid RLE encoding"
splitRLE 0 xs = ([],xs)
splitRLE n (c:xs) | c .&. 0x80 /= 0 = (take count xs ++ ys,rest) where
count = fromIntegral $ (c .&. 0x7f) + 1
(ys,rest) = splitRLE (n - count) (drop count xs)
splitRLE n (c:x:xs) | c .&. 0x80 == 0 = (replicate count x ++ ys,rest) where
count = fromIntegral $ (c .&. 0x7f) + 1
(ys,rest) = splitRLE (n - count) xs
splitRLE _ _ = error "invalid RLE encoding"
-}
rest = do
s <- look
skip (BS.length s)
return s
toParser :: Get a -> ReadP a
toParser g = (runGet g . LBS.fromChunks . (:[])) `fmap` rest
{-
unsignData :: Monad m => BS.ByteString -> m (FragmentList,Key)
unsignData xs = flip parser xs $ do
l <- parseIntegral32
skip 4
sb <- parseIntegral32
-}
unsignData :: [Word8] -> IO (FragmentList,Key)
unsignData xs = do
key <- parseKey $ take (fromIntegral (l - (8 + sb))) (drop (fromIntegral sb) xs'')
return (fl,key) where
(l,xs') = xdrReadUInt xs
(sb,xs'') = xdrReadUInt (drop 4 xs')
fl = (decodeFragments $ drop (fromIntegral l + 4) xs')
unsignFragments :: FragmentList -> IO FragmentList
unsignFragments tfl | (xs:_) <- [xs | (n,FragmentData xs) <- tfl, n == f_securitySignature] = do
(fl,_) <- unsignData (BS.unpack xs)
unsignFragments fl
unsignFragments x = return x
----------------
-- test code
----------------
{-# NOTINLINE dumpKey #-}
dumpKey :: String -> IO ()
dumpKey arg = do
gd <- getGaleDir
kc <- newKeyCache gd
nk <- getKey kc arg
key <- case nk of
Just key -> return key
Nothing -> do
c <- readRawFile arg
parseKey c
putStrLn $ showKey key
{-
getPrivateKey :: KeyCache -> String -> IO (Maybe (Key,EvpPkey))
getPrivateKey gc kn = modifyMVar (keyCache gc) f where
f kc = case [x|x@(Key kn' _,_) <- kc, kn == kn'] of
(v:_) -> return (kc,Just v)
[] -> do
v <- ioMp getFromDisk
return ((maybeToList v ++ kc),v)
getFromDisk = do
let gd = galeDir gc
let p = (gd ++ "/auth/private/")
knames = [p ++ kn, p ++ kn ++ ".gpri"]
gn = (map (\fn -> (readRawFile fn >>= \c -> return (c,fn))) knames)
(key_c, fn) <- (first gn)
key <- parseKey key_c
rsa <- privkeyToRSA key
pkey <- pkeyNewRSA rsa
putLog LogNotice $ "Retrieved private key from disk: " ++ fn
return (key,pkey)
getPublicKey :: KeyCache -> String -> IO (Maybe (Key,EvpPkey))
getPublicKey gc kn = modifyMVar (publicKeyCache gc) f where
f keyCache | Just v <- lookupFM keyCache kn = return (keyCache, Just v)
f keyCache = do
v <- ioM getFromDisk
return $ case v of
Nothing -> (keyCache, Nothing)
Just v' -> (addToFM keyCache kn v', Just v')
getFromDisk :: IO (Key,EvpPkey)
getFromDisk = do
let gd = galeDir gc
let p = (gd ++ "/auth/cache/")
knames = [ p ++ kn ++ ".gpub"]
gn = (map (\fn -> (readRawFile fn >>= \c -> return (c,fn))) knames)
putLog LogDebug $ "Looking for: " ++ show knames
(key_c, fn) <- (first gn)
key <- parseKey key_c
rsa <- pubKeyToRSA key
pkey <- pkeyNewRSA rsa
return (key,pkey)
-}
{-
fragmentData' x y = fragmentData (packString x) y
privkeyToRSA :: Key -> IO RSA
privkeyToRSA (Key _ fl) = do
rsa <- rsaNew
fragmentData' "rsa.modulus" fl >>= bn_bin2bn >>= rsaSetN rsa
fragmentData' "rsa.exponent" fl >>= bn_bin2bn >>= rsaSetE rsa
fragmentData' "rsa.private.exponent" fl >>= bn_bin2bn >>= rsaSetD rsa
fragmentData' "rsa.private.coefficient" fl >>= bn_bin2bn >>= rsaSetIQMP rsa
xs <- fragmentData' "rsa.private.prime" fl
let (p,q) = splitAt galeRSAPrimeLen xs
bn_bin2bn p >>= rsaSetP rsa
bn_bin2bn q >>= rsaSetQ rsa
xs <- fragmentData' "rsa.private.prime.exponent" fl
let (dmp1,dmq1) = splitAt galeRSAPrimeLen xs
bn_bin2bn dmp1 >>= rsaSetDMP1 rsa
bn_bin2bn dmq1 >>= rsaSetDMQ1 rsa
rsaCheckKey rsa
return rsa
pubKeyToRSA :: Key -> IO RSA
pubKeyToRSA (Key _ fl) = do
rsa <- rsaNew
fragmentData' "rsa.modulus" fl >>= bn_bin2bn >>= rsaSetN rsa
fragmentData' "rsa.exponent" fl >>= bn_bin2bn >>= rsaSetE rsa
--fragmentData' "rsa.private.exponent" fl >>= bn_bin2bn >>= rsaSetD rsa
--fragmentData' "rsa.private.coefficient" fl >>= bn_bin2bn >>= rsaSetIQMP rsa
--xs <- fragmentData' "rsa.private.prime" fl
--let (p,q) = splitAt galeRSAPrimeLen xs
--bn_bin2bn p >>= rsaSetP rsa
--bn_bin2bn q >>= rsaSetQ rsa
--xs <- fragmentData' "rsa.private.prime.exponent" fl
--let (dmp1,dmq1) = splitAt galeRSAPrimeLen xs
--bn_bin2bn dmp1 >>= rsaSetDMP1 rsa
--bn_bin2bn dmq1 >>= rsaSetDMQ1 rsa
--rsaCheckKey rsa
return rsa
-}
{-
keyToRSA :: Key -> IO RSA
keyToRSA (Key _ fl) = do
rsa <- rsaNew
fragmentData' "rsa.modulus" fl >>= bn_bin2bn >>= rsaSetN rsa
fragmentData' "rsa.exponent" fl >>= bn_bin2bn >>= rsaSetE rsa
if (hasFragment fl (packString "rsa.private.exponent")) then do
fragmentData' "rsa.private.exponent" fl >>= bn_bin2bn >>= rsaSetD rsa
fragmentData' "rsa.private.coefficient" fl >>= bn_bin2bn >>= rsaSetIQMP rsa
xs <- fragmentData' "rsa.private.prime" fl
let (p,q) = splitAt galeRSAPrimeLen xs -- should be "rsa.bits"?
bn_bin2bn p >>= rsaSetP rsa
bn_bin2bn q >>= rsaSetQ rsa
xs <- fragmentData' "rsa.private.prime.exponent" fl
let (dmp1,dmq1) = splitAt galeRSAPrimeLen xs
bn_bin2bn dmp1 >>= rsaSetDMP1 rsa
bn_bin2bn dmq1 >>= rsaSetDMQ1 rsa
rsaCheckKey rsa
else do
return ()
-- rsaSetD rsa nullPtr
-- rsaSetIQMP rsa nullPtr
-- rsaSetP rsa nullPtr
-- rsaSetQ rsa nullPtr
-- rsaSetDMP1 rsa nullPtr
-- rsaSetDMQ1 rsa nullPtr
return rsa
-}