{-# LANGUAGE OverloadedStrings #-}
module Crypto.Gpgme.Key.Gen (
genKey
, GenKeyParams (..)
, BitSize
, Crypto.Gpgme.Key.Gen.bitSize
, UsageList (..)
, Encrypt (..)
, Sign (..)
, Auth (..)
, ExpireDate (..)
, CreationDate (..)
, Positive (unPositive)
, toPositive
, toParamsString
) where
import Crypto.Gpgme.Types
import Crypto.Gpgme.Internal
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC8
import Text.Email.Validate
import Data.Monoid ((<>))
import Foreign as F
import Foreign.C.String as FCS
import Bindings.Gpgme
import Data.Time.Clock
import Data.Time.Format
import Data.Default
data GenKeyParams = GenKeyParams {
keyType :: Maybe PubKeyAlgo
, keyLength :: Maybe BitSize
, keyGrip :: BS.ByteString
, keyUsage :: Maybe UsageList
, subkeyType :: Maybe PubKeyAlgo
, subkeyLength :: Maybe BitSize
, passphrase :: BS.ByteString
, nameReal :: BS.ByteString
, nameComment :: BS.ByteString
, nameEmail :: Maybe EmailAddress
, expireDate :: Maybe ExpireDate
, creationDate :: Maybe CreationDate
, preferences :: BS.ByteString
, revoker :: BS.ByteString
, keyserver :: BS.ByteString
, handle :: BS.ByteString
, rawParams :: BS.ByteString
}
instance Default GenKeyParams where
def = GenKeyParams Nothing Nothing "" Nothing Nothing Nothing "" "" ""
Nothing Nothing Nothing "" "" "" "" ""
data BitSize = BitSize Int
bitSize :: Int -> Either String BitSize
bitSize x
| x < 1024 = Left "BitSize must be greater than 1024"
| x > 4096 = Left "BitSize must be less than 4096"
| otherwise = Right $ BitSize x
data Encrypt = Encrypt
data Sign = Sign
data Auth = Auth
data UsageList = UsageList {
encrypt :: Maybe Encrypt
, sign :: Maybe Sign
, auth :: Maybe Auth
}
instance Default UsageList where
def = UsageList Nothing Nothing Nothing
data ExpireDate =
ExpireT UTCTime | ExpireD Positive | ExpireW Positive |
ExpireM Positive | ExpireY Positive | ExpireS Positive
data CreationDate = CreationT UTCTime
| CreationS Positive
data Positive = Positive { unPositive :: Int }
toPositive :: Int -> Maybe Positive
toPositive n = if (n < 0) then Nothing else Just (Positive n)
genKey :: Ctx
-> GenKeyParams
-> IO (Either GpgmeError Fpr)
genKey (Ctx {_ctx=ctxPtr}) params = do
ctx <- F.peek ctxPtr
ret <- BS.useAsCString (toParamsString params) $ \p -> do
let nullGpgmeData = 0
c'gpgme_op_genkey ctx p nullGpgmeData nullGpgmeData
if ret == noError
then do
rPtr <- c'gpgme_op_genkey_result ctx
r <- F.peek rPtr
let fprPtr = c'_gpgme_op_genkey_result'fpr r
fpr <- FCS.peekCString fprPtr
return . Right $ BSC8.pack fpr
else return . Left $ GpgmeError ret
toParamsString :: GenKeyParams -> BS.ByteString
toParamsString params = (BSC8.unlines . filter ((/=)""))
[ "<GnupgKeyParms format=\"internal\">"
, "Key-Type: " <> (maybe "default" keyTypeToString $ keyType params)
, maybeLine "Key-Length: " keyLengthToString $ keyLength params
, addLabel "Key-Grip: " $ keyGrip params
, maybeLine "Key-Usage: " keyUsageListToString $ keyUsage params
, maybeLine "Subkey-Type: " keyTypeToString $ subkeyType params
, maybeLine "Subkey-Length: " keyLengthToString $ subkeyLength params
, addLabel "Passphrase: " $ passphrase params
, addLabel "Name-Real: " $ nameReal params
, addLabel "Name-Comment: " $ nameComment params
, maybeLine "Name-Email: " toByteString $ nameEmail params
, maybeLine "Expire-Date: " expireDateToString $ expireDate params
, maybeLine "Creation-Date: " creationDateToString $ creationDate params
, addLabel "Preferences: " $ preferences params
, addLabel "Revoker: " $ revoker params
, addLabel "Keyserver: " $ keyserver params
, addLabel "Handle: " $ handle params
, rawParams params
, "</GnupgKeyParms>"
]
where
maybeLine :: BS.ByteString -> (a -> BS.ByteString) -> Maybe a -> BS.ByteString
maybeLine h f p = addLabel h $ maybe "" f p
addLabel :: BS.ByteString -> BS.ByteString -> BS.ByteString
addLabel _ "" = ""
addLabel h s = h <> s
keyTypeToString :: PubKeyAlgo -> BS.ByteString
keyTypeToString Rsa = "RSA"
keyTypeToString RsaE = "RSA-E"
keyTypeToString RsaS = "RSA-S"
keyTypeToString ElgE = "ELG-E"
keyTypeToString Dsa = "DSA"
keyTypeToString Elg = "ELG"
keyLengthToString :: BitSize -> BS.ByteString
keyLengthToString (BitSize i) = BSC8.pack $ show i
keyUsageListToString :: UsageList -> BS.ByteString
keyUsageListToString (UsageList e s a) =
let eStr = maybe (""::BS.ByteString) (const "encrypt") e
sStr = maybe (""::BS.ByteString) (const "sign") s
aStr = maybe (""::BS.ByteString) (const "auth") a
in (BSC8.intercalate "," . filter ((/=) "" )) [eStr, sStr, aStr]
expireDateToString :: ExpireDate -> BS.ByteString
expireDateToString (ExpireD p) = BSC8.pack $ ((show $ unPositive p) ++ "d")
expireDateToString (ExpireW p) = BSC8.pack $ ((show $ unPositive p) ++ "w")
expireDateToString (ExpireM p) = BSC8.pack $ ((show $ unPositive p) ++ "m")
expireDateToString (ExpireY p) = BSC8.pack $ ((show $ unPositive p) ++ "y")
expireDateToString (ExpireS p) =
BSC8.pack $ ("seconds=" ++ (show $ unPositive p))
expireDateToString (ExpireT t) =
BSC8.pack $ formatTime defaultTimeLocale "%Y%m%dT%H%M%S" t
creationDateToString :: CreationDate -> BS.ByteString
creationDateToString (CreationS p) =
BSC8.pack $ ("seconds=" ++ (show $ unPositive p))
creationDateToString (CreationT t) =
BSC8.pack $ formatTime defaultTimeLocale "%Y%m%dT%H%M%S" t