Define HMAC based access token and switch CapSignKey from Ed25519 to HMAC
This commit is contained in:
parent
fdbe46741b
commit
61d1aa6720
3 changed files with 92 additions and 1 deletions
|
@ -64,6 +64,7 @@ import Yesod.Mail.Send
|
||||||
import qualified Network.HTTP.Signature as S (Algorithm (..))
|
import qualified Network.HTTP.Signature as S (Algorithm (..))
|
||||||
|
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
|
import Web.ActivityAccess
|
||||||
import Web.ActivityPub hiding (PublicKey)
|
import Web.ActivityPub hiding (PublicKey)
|
||||||
|
|
||||||
import Text.Email.Local
|
import Text.Email.Local
|
||||||
|
@ -92,7 +93,7 @@ data App = App
|
||||||
, appSvgFont :: PreparedFont Double
|
, appSvgFont :: PreparedFont Double
|
||||||
, appActorKeys :: TVar (ActorKey, ActorKey, Bool)
|
, appActorKeys :: TVar (ActorKey, ActorKey, Bool)
|
||||||
, appInstanceMutex :: InstanceMutex
|
, appInstanceMutex :: InstanceMutex
|
||||||
, appCapSignKey :: ActorKey
|
, appCapSignKey :: AccessTokenSecretKey
|
||||||
, appHashidEncode :: Int64 -> Text
|
, appHashidEncode :: Int64 -> Text
|
||||||
, appHashidDecode :: Text -> Maybe Int64
|
, appHashidDecode :: Text -> Maybe Int64
|
||||||
|
|
||||||
|
|
89
src/Web/ActivityAccess.hs
Normal file
89
src/Web/ActivityAccess.hs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2019 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
|
-
|
||||||
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
|
-
|
||||||
|
- The author(s) have dedicated all copyright and related and neighboring
|
||||||
|
- rights to this software to the public domain worldwide. This software is
|
||||||
|
- distributed without any warranty.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the CC0 Public Domain Dedication along
|
||||||
|
- with this software. If not, see
|
||||||
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
-}
|
||||||
|
|
||||||
|
module Web.ActivityAccess
|
||||||
|
( SignedAccessToken ()
|
||||||
|
, AccessTokenSecretKey ()
|
||||||
|
, encodeSignedAccessToken
|
||||||
|
, decodeSignedAccessToken
|
||||||
|
, signAccessToken
|
||||||
|
, verifyAccessToken
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Prelude
|
||||||
|
|
||||||
|
import Crypto.Hash
|
||||||
|
import Crypto.MAC.HMAC
|
||||||
|
import Crypto.Random
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
|
import Data.KeyFile
|
||||||
|
|
||||||
|
import qualified Data.ByteArray as BA
|
||||||
|
import qualified Data.ByteString as B
|
||||||
|
import qualified Data.ByteString.Base64 as B64
|
||||||
|
import qualified Data.ByteString.Char8 as BC
|
||||||
|
|
||||||
|
data SignedAccessToken = SignedAccessToken
|
||||||
|
{ _accessTokenMsg :: ByteString
|
||||||
|
, _accessTokenHMAC :: HMAC SHA256
|
||||||
|
}
|
||||||
|
|
||||||
|
newtype AccessTokenSecretKey = AccessTokenSecretKey ByteString
|
||||||
|
|
||||||
|
instance KeyFile AccessTokenSecretKey where
|
||||||
|
generateKey = AccessTokenSecretKey <$> getRandomBytes 32
|
||||||
|
parseKey b =
|
||||||
|
if B.length b == 32
|
||||||
|
then return $ AccessTokenSecretKey b
|
||||||
|
else fail "AccessTokenSigningKey parseKey invalid length"
|
||||||
|
renderKey (AccessTokenSecretKey b) = b
|
||||||
|
|
||||||
|
encodeSignedAccessToken :: SignedAccessToken -> ByteString
|
||||||
|
encodeSignedAccessToken (SignedAccessToken msg sig) = B.concat
|
||||||
|
[ msg
|
||||||
|
, "-"
|
||||||
|
, B64.encode $ BA.convert sig
|
||||||
|
]
|
||||||
|
|
||||||
|
decodeSignedAccessToken :: ByteString -> Either String SignedAccessToken
|
||||||
|
decodeSignedAccessToken token = do
|
||||||
|
let (msg, rest) = BC.break (== '-') token
|
||||||
|
sigB64 <-
|
||||||
|
case B.stripPrefix "-" rest of
|
||||||
|
Nothing -> err "Invalid format, separator not found"
|
||||||
|
Just rest' -> return rest'
|
||||||
|
sigBin <-
|
||||||
|
case B64.decode sigB64 of
|
||||||
|
Left s -> err $ "Base64 decoding sig failed: " ++ s
|
||||||
|
Right b -> return b
|
||||||
|
digest <-
|
||||||
|
case digestFromByteString sigBin of
|
||||||
|
Nothing -> err "Decoding sig hash failed, invalid length"
|
||||||
|
Just d -> return d
|
||||||
|
return $ SignedAccessToken msg $ HMAC digest
|
||||||
|
where
|
||||||
|
err s = Left $ "decodeSignedAccessToken: " ++ s
|
||||||
|
|
||||||
|
signAccessToken :: AccessTokenSecretKey -> ByteString -> SignedAccessToken
|
||||||
|
signAccessToken (AccessTokenSecretKey key) msg =
|
||||||
|
SignedAccessToken msg $ hmac key msg
|
||||||
|
|
||||||
|
verifyAccessToken
|
||||||
|
:: AccessTokenSecretKey -> SignedAccessToken -> Maybe ByteString
|
||||||
|
verifyAccessToken (AccessTokenSecretKey key) (SignedAccessToken msg sig) =
|
||||||
|
if hmac key msg == sig
|
||||||
|
then Just msg
|
||||||
|
else Nothing
|
|
@ -86,6 +86,7 @@ library
|
||||||
Text.Email.Local
|
Text.Email.Local
|
||||||
Text.FilePath.Local
|
Text.FilePath.Local
|
||||||
Text.Jasmine.Local
|
Text.Jasmine.Local
|
||||||
|
Web.ActivityAccess
|
||||||
Web.ActivityPub
|
Web.ActivityPub
|
||||||
Web.Hashids.Local
|
Web.Hashids.Local
|
||||||
Web.PathPieces.Local
|
Web.PathPieces.Local
|
||||||
|
|
Loading…
Reference in a new issue