glirc-2.20.2: src/Client/Authentication/Ecdsa.hs
{-# Language OverloadedStrings #-}
{-|
Module : Client.Authentication.Ecdsa
Description : Binding to ecdsatool
Copyright : (c) Eric Mertens, 2016
License : ISC
Maintainer : emertens@gmail.com
Implementation of ECDSA-NIST256P-CHALLENGE SASL authentication mode
as implemented at <https://github.com/kaniini/ecdsatool> and used
on Freenode.
Using this mode requires that the @ecdsa@ utility program is available
in your search path.
-}
module Client.Authentication.Ecdsa
( authenticationMode
, encodeUsername
, computeResponse
) where
import Client.Configuration (resolveConfigurationPath)
import Control.Exception (displayException, try)
import Data.ByteString.Base64 as Enc
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import System.IO.Error (IOError)
import System.Process (readProcess)
-- | Identifier for SASL ECDSA challenge response authentication
-- using curve NIST256P.
--
-- @ECDSA-NIST256P-CHALLENGE@
authenticationMode :: Text
authenticationMode = "ECDSA-NIST256P-CHALLENGE"
-- | Encode a username as specified in this authentication mode.
encodeUsername ::
Text {- ^ username -} ->
Text {- ^ base-64 encoded username -}
encodeUsername = Text.decodeUtf8 . Enc.encode . Text.encodeUtf8
-- | Compute the response for a given challenge using the @ecdsatool@
-- executable which must be available in @PATH@.
computeResponse ::
FilePath {- ^ private key file -} ->
Text {- ^ challenge string -} ->
IO (Either String Text) {- ^ error message or response string -}
computeResponse privateKeyFile challenge =
do path <- resolveConfigurationPath privateKeyFile
res <- try $ readProcess
"ecdsatool"
["sign", path, Text.unpack challenge]
"" -- stdin
return $! case words <$> res of
Right [resp] -> Right $! Text.pack resp
Right _ -> Left "bad sasl ecdsa response message"
Left e -> Left (displayException (e :: IOError))