1
0
Fork 0
mirror of https://code.sup39.dev/repos/Wqawg synced 2024-12-29 03:04:53 +09:00

Use the DB for checking key usage by actors

* When we refetch a personal key, we never need to separately fetch the actor
* When we refetch a shared key, check usage in DB and insert usage to DB
This commit is contained in:
fr33domlover 2019-02-23 17:17:52 +00:00
parent b53a7b4f48
commit 0912b8e291
2 changed files with 140 additions and 54 deletions

View file

@ -623,10 +623,10 @@ keyListedByActorShared
-> Text -> Text
-> LocalURI -> LocalURI
-> LocalURI -> LocalURI
-> Handler (Either String ()) -> ExceptT String Handler ()
keyListedByActorShared manager iid vkid host luKey luActor = do keyListedByActorShared manager iid vkid host luKey luActor = do
mresult <- do mresult <- do
ments <- runDB $ do ments <- lift $ runDB $ do
mrs <- getBy $ UniqueRemoteSharer iid luActor mrs <- getBy $ UniqueRemoteSharer iid luActor
for mrs $ \ (Entity rsid _) -> for mrs $ \ (Entity rsid _) ->
(rsid,) . isJust <$> (rsid,) . isJust <$>
@ -638,7 +638,7 @@ keyListedByActorShared manager iid vkid host luKey luActor = do
if used if used
then Nothing then Nothing
else Just $ Just rsid else Just $ Just rsid
runExceptT $ for_ mresult $ \ mrsid -> do for_ mresult $ \ mrsid -> do
luInbox <- actorInbox <$> ExceptT (keyListedByActor manager host luKey luActor) luInbox <- actorInbox <$> ExceptT (keyListedByActor manager host luKey luActor)
ExceptT $ runDB $ case mrsid of ExceptT $ runDB $ case mrsid of
Nothing -> do Nothing -> do
@ -655,7 +655,6 @@ keyListedByActorShared manager iid vkid host luKey luActor = do
data VerifKeyDetail = VerifKeyDetail data VerifKeyDetail = VerifKeyDetail
{ vkdKeyId :: LocalURI { vkdKeyId :: LocalURI
-- , vkdInboxOrId :: Either LocalURI VerifKeyId
, vkdKey :: PublicKey , vkdKey :: PublicKey
, vkdExpires :: Maybe UTCTime , vkdExpires :: Maybe UTCTime
, vkdActorId :: LocalURI , vkdActorId :: LocalURI
@ -675,6 +674,8 @@ instance YesodHttpSig App where
(host, luKey) <- f2l <$> parseKeyId keyid (host, luKey) <- f2l <$> parseKeyId keyid
signature <- parseSig sig signature <- parseSig sig
mluActorHeader <- getActorHeader host mluActorHeader <- getActorHeader host
let sigAlgo = isJust malgo
manager <- getsYesod appHttpManager
(inboxOrVkid, vkd) <- do (inboxOrVkid, vkd) <- do
ments <- lift $ runDB $ do ments <- lift $ runDB $ do
mvk <- runMaybeT $ do mvk <- runMaybeT $ do
@ -700,10 +701,10 @@ instance YesodHttpSig App where
Just u -> return u Just u -> return u
manager <- getsYesod appHttpManager manager <- getsYesod appHttpManager
let iid = verifKeyInstance vk let iid = verifKeyInstance vk
ExceptT $ keyListedByActorShared manager iid vkid host luKey ua keyListedByActorShared manager iid vkid host luKey ua
return (ua, True) return (ua, True)
return return
( Right vkid ( Right (verifKeyInstance vk, vkid)
, VerifKeyDetail , VerifKeyDetail
{ vkdKeyId = luKey { vkdKeyId = luKey
, vkdKey = verifKeyPublic vk , vkdKey = verifKeyPublic vk
@ -712,11 +713,10 @@ instance YesodHttpSig App where
, vkdShared = s , vkdShared = s
} }
) )
Nothing -> fetched2vkd luKey <$> fetchKey' host mluActorHeader luKey Nothing -> fetched2vkd luKey <$> fetchUnknownKey manager sigAlgo host mluActorHeader luKey
let verify' k = verify k input signature let verify' k = verify k input signature
errSig = throwE "Ed25519 sig verification says not valid" errSig = throwE "Ed25519 sig verification says not valid"
errTime = throwE "Key expired" errTime = throwE "Key expired"
-- existsInDB = isRight $ vkdInboxOrId vkd
now <- liftIO getCurrentTime now <- liftIO getCurrentTime
let stillValid Nothing = True let stillValid Nothing = True
stillValid (Just expires) = expires > now stillValid (Just expires) = expires > now
@ -724,21 +724,19 @@ instance YesodHttpSig App where
if verify' (vkdKey vkd) && stillValid (vkdExpires vkd) if verify' (vkdKey vkd) && stillValid (vkdExpires vkd)
then case inboxOrVkid of then case inboxOrVkid of
Left uinb -> ExceptT $ runDB $ addVerifKey host uinb vkd Left uinb -> ExceptT $ runDB $ addVerifKey host uinb vkd
Right _vkid -> return () Right _ids -> return ()
else case inboxOrVkid of else case inboxOrVkid of
Left _uinb -> Left _uinb ->
if stillValid $ vkdExpires vkd if stillValid $ vkdExpires vkd
then errSig then errSig
else errTime else errTime
Right vkid -> do Right (iid, vkid) -> do
Fetched newKey newExp newActor _newInbox s <- fetchKey' host mluActorHeader luKey let ua = vkdActorId vkd
if vkdShared vkd == s listed = keyListedByActorShared manager iid vkid host luKey ua
then return () (newKey, newExp) <-
else throwE "Key scope changed, we reject that" if vkdShared vkd
unless (vkdShared vkd) $ then fetchKnownSharedKey manager listed sigAlgo host ua luKey
if newActor == vkdActorId vkd else fetchKnownPersonalKey manager sigAlgo host ua luKey
then return ()
else throwE "Key owner changed, we reject that"
if stillValid newExp if stillValid newExp
then return () then return ()
else errTime else errTime
@ -775,9 +773,6 @@ instance YesodHttpSig App where
else Left "Key and actor have different hosts" else Left "Key and actor have different hosts"
Right lu Right lu
_ -> throwE "Multiple ActivityPub-Actor headers" _ -> throwE "Multiple ActivityPub-Actor headers"
fetchKey' h mua uk = do
manager <- getsYesod appHttpManager
ExceptT $ fetchKey manager (isJust malgo) h mua uk
fetched2vkd uk (Fetched k mexp ua uinb s) = fetched2vkd uk (Fetched k mexp ua uinb s) =
( Left uinb ( Left uinb
, VerifKeyDetail , VerifKeyDetail

View file

@ -47,7 +47,9 @@ module Web.ActivityPub
, Fetched (..) , Fetched (..)
, fetchAPID , fetchAPID
, keyListedByActor , keyListedByActor
, fetchKey , fetchUnknownKey
, fetchKnownPersonalKey
, fetchKnownSharedKey
) )
where where
@ -485,12 +487,14 @@ data Fetched = Fetched
fetchAP :: (MonadIO m, FromJSON a) => Manager -> FedURI -> ExceptT String m a fetchAP :: (MonadIO m, FromJSON a) => Manager -> FedURI -> ExceptT String m a
fetchAP m u = ExceptT $ bimap displayException responseBody <$> httpGetAP m u fetchAP m u = ExceptT $ bimap displayException responseBody <$> httpGetAP m u
{-
fetchAPH :: (MonadIO m, ActivityPub a) => Manager -> Text -> LocalURI -> ExceptT String m a fetchAPH :: (MonadIO m, ActivityPub a) => Manager -> Text -> LocalURI -> ExceptT String m a
fetchAPH m h lu = do fetchAPH m h lu = do
Doc h' v <- fetchAP m $ l2f h lu Doc h' v <- fetchAP m $ l2f h lu
if h == h' if h == h'
then return v then return v
else throwE "Object @id URI's host doesn't match the URI we fetched" else throwE "Object @id URI's host doesn't match the URI we fetched"
-}
fetchAPID :: (MonadIO m, ActivityPub a) => Manager -> (a -> LocalURI) -> Text -> LocalURI -> m (Either String a) fetchAPID :: (MonadIO m, ActivityPub a) => Manager -> (a -> LocalURI) -> Text -> LocalURI -> m (Either String a)
fetchAPID m getId h lu = runExceptT $ do fetchAPID m getId h lu = runExceptT $ do
@ -535,15 +539,53 @@ keyListedByActor manager host luKey luActor = runExceptT $ do
match (Right _) = False match (Right _) = False
in match k1 || maybe False match mk2 in match k1 || maybe False match mk2
fetchKey matchKeyObj luKey (PublicKeySet k1 mk2) =
let match' = match luKey
in case match' k1 <|> (match' =<< mk2) of
Nothing -> throwE "keyId resolved to actor which doesn't have a key object with that ID"
Just pk -> return pk
where
match _ (Left _) = Nothing
match luk (Right pk) =
if publicKeyId pk == luk
then Just pk
else Nothing
verifyAlgo sigAlgo Nothing =
Left $
if sigAlgo
then "Algo mismatch, Ed25519 in Sig but none in actor"
else "Algo not given in Sig nor actor"
verifyAlgo sigAlgo (Just algo) =
case algo of
AlgorithmEd25519 -> Right ()
AlgorithmOther _ ->
Left $
if sigAlgo
then "Algo mismatch, Ed25519 in Sig but unsupported algo in actor"
else "No algo in Sig, unsupported algo in actor"
parseKey pem =
case E.publicKey $ pemContent pem of
CryptoPassed k -> Right k
CryptoFailed _ -> Left "Parsing Ed25519 public key failed"
-- | Fetch a key we don't have cached locally.
fetchUnknownKey
:: MonadIO m :: MonadIO m
=> Manager => Manager
-- ^ Manager for making HTTP requests
-> Bool -> Bool
-- ^ Whether the Ed25519 algorithm is specified explicitly in the HTTP
-- signature header
-> Text -> Text
-- ^ Instance host
-> Maybe LocalURI -> Maybe LocalURI
-- ^ Actor URI possibly provided in the HTTP request's actor header
-> LocalURI -> LocalURI
-> m (Either String Fetched) -- ^ Key URI provided in HTTP signature header
fetchKey manager sigAlgo host mluActor luKey = runExceptT $ do -> ExceptT String m Fetched
fetchUnknownKey manager sigAlgo host mluActor luKey = do
obj <- fetchAPIDOrH manager publicKeyId host luKey obj <- fetchAPIDOrH manager publicKeyId host luKey
(pem, mkFetched, malgo) <- (pem, mkFetched, malgo) <-
case obj of case obj of
@ -603,32 +645,81 @@ fetchKey manager sigAlgo host mluActor luKey = runExceptT $ do
ExceptT . pure $ do ExceptT . pure $ do
verifyAlgo sigAlgo malgo verifyAlgo sigAlgo malgo
mkFetched <$> parseKey pem mkFetched <$> parseKey pem
where
matchKeyObj luKey (PublicKeySet k1 mk2) = keyDetail pk = (publicKeyPem pk, publicKeyExpires pk, publicKeyAlgo pk)
let match' = match luKey
in case match' k1 <|> (match' =<< mk2) of -- | Fetch a personal key we already have cached locally, but we'd like to
Nothing -> throwE "keyId resolved to actor which doesn't have a key object with that ID" -- refresh the local copy by fetching the key again from the server.
Just pk -> return pk fetchKnownPersonalKey
where :: MonadIO m
match _ (Left _) = Nothing => Manager
match luk (Right pk) = -- ^ Manager for making HTTP requests
if publicKeyId pk == luk -> Bool
then Just pk -- ^ Whether the Ed25519 algorithm is specified explicitly in the HTTP
else Nothing -- signature header
verifyAlgo sigAlgo Nothing = -> Text
Left $ -- ^ Instance host
if sigAlgo -> LocalURI
then "Algo mismatch, Ed25519 in Sig but none in actor" -- ^ Key owner actor ID URI
else "Algo not given in Sig nor actor" -> LocalURI
verifyAlgo sigAlgo (Just algo) = -- ^ Key URI
case algo of -> ExceptT String m (E.PublicKey, Maybe UTCTime)
AlgorithmEd25519 -> Right () fetchKnownPersonalKey manager sigAlgo host luOwner luKey = do
AlgorithmOther _ -> obj <- fetchAPIDOrH manager publicKeyId host luKey
Left $ (pem, mexpires, malgo) <-
if sigAlgo case obj of
then "Algo mismatch, Ed25519 in Sig but unsupported algo in actor" Left pkey -> do
else "No algo in Sig, unsupported algo in actor" case publicKeyOwner pkey of
parseKey pem = OwnerInstance -> throwE "Personal key became shared"
case E.publicKey $ pemContent pem of OwnerActor owner ->
CryptoPassed k -> Right k when (luOwner /= owner) $ throwE "Key owner changed"
CryptoFailed _ -> Left "Parsing Ed25519 public key failed" return $ keyDetail pkey
Right actor -> do
when (actorId actor /= luKey { luriFragment = "" }) $
throwE "Actor ID doesn't match the keyid URI we fetched"
when (actorId actor /= luOwner) $
throwE "Key owner changed"
pk <- matchKeyObj luKey $ actorPublicKeys actor
case publicKeyOwner pk of
OwnerInstance -> throwE "Personal key became shared"
OwnerActor owner ->
when (owner /= luOwner) $
throwE "Actor's publicKey's owner doesn't match the actor's ID"
return $ keyDetail pk
ExceptT . pure $ do
verifyAlgo sigAlgo malgo
(, mexpires) <$> parseKey pem
-- | Fetch a shared key we already have cached locally, but we'd like to
-- refresh the local copy by fetching the key again from the server.
fetchKnownSharedKey
:: MonadIO m
=> Manager
-- ^ Manager for making HTTP requests
-> ExceptT String m ()
-- ^ Action which checks whether the actor from HTTP actor header lists the
-- key, potentually updating our local cache if needed.
-> Bool
-- ^ Whether the Ed25519 algorithm is specified explicitly in the HTTP
-- signature header
-> Text
-- ^ Instance host
-> LocalURI
-- ^ Actor ID from HTTP actor header
-> LocalURI
-- ^ Key URI
-> ExceptT String m (E.PublicKey, Maybe UTCTime)
fetchKnownSharedKey manager listed sigAlgo host luActor luKey = do
obj <- fetchAPIDOrH manager publicKeyId host luKey
pkey <-
case obj :: Either PublicKey Actor of
Left pk -> return pk
Right _actor -> throwE "Expected stand-alone key, got embedded key"
case publicKeyOwner pkey of
OwnerInstance -> return ()
OwnerActor _owner -> throwE "Shared key became personal"
listed
let (pem, mexpires, malgo) = keyDetail pkey
ExceptT . pure $ do
verifyAlgo sigAlgo malgo
(, mexpires) <$> parseKey pem