{-# language RecordWildCards #-} module Fedi.Crypto where import Crypto.Hash qualified as Crypto import Data.ByteArray qualified as BA import Crypto.PubKey.RSA.PKCS15 qualified as Crypto import Crypto.Store.X509 qualified as Crypto import Crypto.Store.PKCS8 qualified as Crypto import Data.X509 qualified as Crypto import Fedi.Helpers import Data.ByteString.Base64 qualified as Base64 import Data.Base64.Types qualified as Base64 import Data.Text qualified as T verifyPub :: MonadThrow m => ByteString -> ByteString -> ByteString -> m Bool verifyPub pubkeypem sig message = do pubkey <- case Crypto.readPubKeyFileFromMemory pubkeypem of [Crypto.PubKeyRSA pubkey'] -> pure pubkey' _ -> throw "failed to read pubkey pem" pure $ Crypto.verify (Just Crypto.SHA256) pubkey message sig sign :: FilePath -> ByteString -> IO Signed sign privatePemFile message = do -- get private key privkeypem <- Crypto.readKeyFile privatePemFile privateKey <- case privkeypem of [Crypto.Unprotected (Crypto.PrivKeyRSA privkey)] -> pure privkey _ -> throw $ "error reading local private key from '" <> privatePemFile <> "'." -- sign message signedMessage <- Crypto.sign Nothing (Just Crypto.SHA256) privateKey message & either (throw . show) pure -- return pure Signed{..} newtype Signed = Signed { signedMessage :: ByteString } deriving Show ppSigned :: Signed -> String ppSigned signed = unlines [ "Signature" , "{ signedMessage = " <> encodeBase64String signed.signedMessage , "}" ] encodeBase64 :: ByteString -> ByteString encodeBase64 = Base64.extractBase64 . Base64.encodeBase64' encodeBase64String :: ByteString -> String encodeBase64String = T.unpack . Base64.extractBase64 . Base64.encodeBase64 decodeBase64 :: ByteString -> ByteString decodeBase64 = Base64.decodeBase64Lenient makeDigest :: ByteString -> ByteString makeDigest message = BA.convert (Crypto.hash message :: Crypto.Digest Crypto.SHA256)