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

View file

@ -47,7 +47,9 @@ module Web.ActivityPub
, Fetched (..)
, fetchAPID
, keyListedByActor
, fetchKey
, fetchUnknownKey
, fetchKnownPersonalKey
, fetchKnownSharedKey
)
where
@ -485,12 +487,14 @@ data Fetched = Fetched
fetchAP :: (MonadIO m, FromJSON a) => Manager -> FedURI -> ExceptT String m a
fetchAP m u = ExceptT $ bimap displayException responseBody <$> httpGetAP m u
{-
fetchAPH :: (MonadIO m, ActivityPub a) => Manager -> Text -> LocalURI -> ExceptT String m a
fetchAPH m h lu = do
Doc h' v <- fetchAP m $ l2f h lu
if h == h'
then return v
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 m getId h lu = runExceptT $ do
@ -535,15 +539,53 @@ keyListedByActor manager host luKey luActor = runExceptT $ do
match (Right _) = False
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
=> Manager
-- ^ Manager for making HTTP requests
-> Bool
-- ^ Whether the Ed25519 algorithm is specified explicitly in the HTTP
-- signature header
-> Text
-- ^ Instance host
-> Maybe LocalURI
-- ^ Actor URI possibly provided in the HTTP request's actor header
-> LocalURI
-> m (Either String Fetched)
fetchKey manager sigAlgo host mluActor luKey = runExceptT $ do
-- ^ Key URI provided in HTTP signature header
-> ExceptT String m Fetched
fetchUnknownKey manager sigAlgo host mluActor luKey = do
obj <- fetchAPIDOrH manager publicKeyId host luKey
(pem, mkFetched, malgo) <-
case obj of
@ -603,32 +645,81 @@ fetchKey manager sigAlgo host mluActor luKey = runExceptT $ do
ExceptT . pure $ do
verifyAlgo sigAlgo malgo
mkFetched <$> parseKey pem
where
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"
keyDetail pk = (publicKeyPem pk, publicKeyExpires pk, publicKeyAlgo pk)
-- | Fetch a personal key we already have cached locally, but we'd like to
-- refresh the local copy by fetching the key again from the server.
fetchKnownPersonalKey
:: MonadIO m
=> Manager
-- ^ Manager for making HTTP requests
-> Bool
-- ^ Whether the Ed25519 algorithm is specified explicitly in the HTTP
-- signature header
-> Text
-- ^ Instance host
-> LocalURI
-- ^ Key owner actor ID URI
-> LocalURI
-- ^ Key URI
-> ExceptT String m (E.PublicKey, Maybe UTCTime)
fetchKnownPersonalKey manager sigAlgo host luOwner luKey = do
obj <- fetchAPIDOrH manager publicKeyId host luKey
(pem, mexpires, malgo) <-
case obj of
Left pkey -> do
case publicKeyOwner pkey of
OwnerInstance -> throwE "Personal key became shared"
OwnerActor owner ->
when (luOwner /= owner) $ throwE "Key owner changed"
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