module Crypto.Gpgme.Crypto (
encrypt
, encryptSign
, encrypt'
, encryptSign'
, decrypt
, decrypt'
, decryptVerify
, decryptVerify'
, verifyDetached
, verifyDetached'
, verifyPlain
, verifyPlain'
) where
import Bindings.Gpgme
import qualified Data.ByteString as BS
import Control.Monad (liftM)
import Control.Monad.Trans.Either
import Foreign
import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
import GHC.Ptr
import Crypto.Gpgme.Ctx
import Crypto.Gpgme.Internal
import Crypto.Gpgme.Key
import Crypto.Gpgme.Types
locale :: String
locale = "C"
encrypt' :: String -> Fpr -> Plain -> IO (Either String Encrypted)
encrypt' = encryptIntern' encrypt
encryptSign' :: String -> Fpr -> Plain -> IO (Either String Encrypted)
encryptSign' = encryptIntern' encryptSign
orElse :: Monad m => m (Maybe a) -> e -> EitherT e m a
orElse action err = EitherT $ maybe (Left err) return `liftM` action
encryptIntern' :: (Ctx -> [Key] -> Flag -> Plain
-> IO (Either [InvalidKey] Encrypted)
) -> String -> Fpr -> Plain -> IO (Either String Encrypted)
encryptIntern' encrFun gpgDir recFpr plain =
withCtx gpgDir locale OpenPGP $ \ctx -> runEitherT $
do pubKey <- getKey ctx recFpr NoSecret `orElse` ("no such key: " ++ show recFpr)
bimapEitherT show id $ EitherT $ encrFun ctx [pubKey] NoFlag plain
encrypt :: Ctx -> [Key] -> Flag -> Plain -> IO (Either [InvalidKey] Encrypted)
encrypt = encryptIntern c'gpgme_op_encrypt
encryptSign :: Ctx -> [Key] -> Flag -> Plain -> IO (Either [InvalidKey] Encrypted)
encryptSign = encryptIntern c'gpgme_op_encrypt_sign
encryptIntern :: (C'gpgme_ctx_t
-> GHC.Ptr.Ptr C'gpgme_key_t
-> C'gpgme_encrypt_flags_t
-> C'gpgme_data_t
-> C'gpgme_data_t
-> IO C'gpgme_error_t
)
-> Ctx
-> [Key]
-> Flag
-> Plain
-> IO (Either [InvalidKey] Encrypted)
encryptIntern enc_op (Ctx {_ctx=ctxPtr}) recPtrs flag plain = do
plainBufPtr <- malloc
BS.useAsCString plain $ \bs -> do
let copyData = 1
let plainlen = fromIntegral (BS.length plain)
ret <- c'gpgme_data_new_from_mem plainBufPtr bs plainlen copyData
checkError "data_new_from_mem" ret
plainBuf <- peek plainBufPtr
resultBufPtr <- newDataBuffer
resultBuf <- peek resultBufPtr
ctx <- peek ctxPtr
withKeyPtrArray recPtrs $ \recArray ->
checkError "op_encrypt" =<< enc_op ctx recArray (fromFlag flag)
plainBuf resultBuf
free plainBufPtr
encResPtr <- c'gpgme_op_encrypt_result ctx
encRes <- peek encResPtr
let recPtr = c'_gpgme_op_encrypt_result'invalid_recipients encRes
let res = if recPtr /= nullPtr
then Left (collectFprs recPtr)
else Right (collectResult resultBuf)
free resultBufPtr
return res
withKeyPtrArray :: [Key] -> (Ptr C'gpgme_key_t -> IO a) -> IO a
withKeyPtrArray [] f = f nullPtr
withKeyPtrArray keys f = do
arr <- newArray0 nullPtr =<< mapM (peek . unsafeForeignPtrToPtr . unKey) keys
f arr
decrypt' :: String -> Encrypted -> IO (Either DecryptError Plain)
decrypt' = decryptInternal' decrypt
decryptVerify' :: String -> Encrypted -> IO (Either DecryptError Plain)
decryptVerify' = decryptInternal' decryptVerify
decryptInternal' :: (Ctx -> Encrypted -> IO (Either DecryptError Plain))
-> String
-> Encrypted
-> IO (Either DecryptError Plain)
decryptInternal' decrFun gpgDir cipher =
withCtx gpgDir locale OpenPGP $ \ctx ->
decrFun ctx cipher
decrypt :: Ctx -> Encrypted -> IO (Either DecryptError Plain)
decrypt = decryptIntern c'gpgme_op_decrypt
decryptVerify :: Ctx -> Encrypted -> IO (Either DecryptError Plain)
decryptVerify = decryptIntern c'gpgme_op_decrypt_verify
decryptIntern :: (C'gpgme_ctx_t
-> C'gpgme_data_t
-> C'gpgme_data_t
-> IO C'gpgme_error_t
)
-> Ctx
-> Encrypted
-> IO (Either DecryptError Plain)
decryptIntern dec_op (Ctx {_ctx=ctxPtr}) cipher = do
cipherBufPtr <- malloc
BS.useAsCString cipher $ \bs -> do
let copyData = 1
let cipherlen = fromIntegral (BS.length cipher)
ret <- c'gpgme_data_new_from_mem cipherBufPtr bs cipherlen copyData
checkError "data_new_from_mem" ret
cipherBuf <- peek cipherBufPtr
resultBufPtr <- newDataBuffer
resultBuf <- peek resultBufPtr
ctx <- peek ctxPtr
errcode <- dec_op ctx cipherBuf resultBuf
let res = if errcode /= noError
then Left (toDecryptError errcode)
else Right (collectResult resultBuf)
free cipherBufPtr
free resultBufPtr
return res
verifyDetached :: Ctx -> Signature -> BS.ByteString -> IO (Either GpgmeError VerificationResult)
verifyDetached ctx sig dat = do
res <- verifyInternal go ctx sig dat
return $ fmap fst res
where
go ctx' sig' dat' = do
errcode <- c'gpgme_op_verify ctx' sig' dat' 0
return (errcode, ())
verifyDetached' :: String -> Signature -> BS.ByteString -> IO (Either GpgmeError VerificationResult)
verifyDetached' gpgDir sig dat =
withCtx gpgDir locale OpenPGP $ \ctx ->
verifyDetached ctx sig dat
verifyPlain :: Ctx -> Signature -> BS.ByteString -> IO (Either GpgmeError (VerificationResult, BS.ByteString))
verifyPlain = verifyInternal go
where
go ctx sig dat = do
resultBufPtr <- newDataBuffer
resultBuf <- peek resultBufPtr
errcode <- c'gpgme_op_verify ctx sig dat resultBuf
let res = if errcode /= noError
then mempty
else collectResult resultBuf
free resultBufPtr
return (errcode, res)
verifyPlain' :: String -> Signature -> BS.ByteString -> IO (Either GpgmeError (VerificationResult, BS.ByteString))
verifyPlain' gpgDir sig dat =
withCtx gpgDir locale OpenPGP $ \ctx ->
verifyPlain ctx sig dat
verifyInternal :: (C'gpgme_ctx_t -> C'gpgme_data_t -> C'gpgme_data_t -> IO (C'gpgme_error_t, a))-> Ctx -> Signature -> BS.ByteString -> IO (Either GpgmeError (VerificationResult, a))
verifyInternal ver_op (Ctx {_ctx=ctxPtr}) sig dat = do
sigBufPtr <- malloc
BS.useAsCString sig $ \bs -> do
let copyData = 1
let siglen = fromIntegral (BS.length sig)
ret <- c'gpgme_data_new_from_mem sigBufPtr bs siglen copyData
checkError "data_new_from_mem" ret
sigBuf <- peek sigBufPtr
datBufPtr <- malloc
BS.useAsCString dat $ \bs -> do
let copyData = 1
let datlen = fromIntegral (BS.length dat)
ret <- c'gpgme_data_new_from_mem datBufPtr bs datlen copyData
checkError "data_new_from_mem" ret
datBuf <- peek datBufPtr
ctx <- peek ctxPtr
(errcode, res) <- ver_op ctx sigBuf datBuf
let res' = if errcode /= noError
then Left (GpgmeError errcode)
else Right (collectSignatures ctx, res)
free sigBufPtr
free datBufPtr
return res'
newDataBuffer :: IO (Ptr C'gpgme_data_t)
newDataBuffer = do
resultBufPtr <- malloc
checkError "data_new" =<< c'gpgme_data_new resultBufPtr
return resultBufPtr