1
0
Fork 0
mirror of https://code.sup39.dev/repos/Wqawg synced 2024-12-28 11:44:51 +09:00

Refactor HTTP sig verification DB update code

This commit is contained in:
fr33domlover 2019-02-22 07:20:19 +00:00
parent d3e14b3edf
commit 1affe269bb
2 changed files with 168 additions and 174 deletions

View file

@ -603,7 +603,20 @@ actorRoom rsid =
(count [VerifKeySharedUsageUser ==. rsid])
(count [VerifKeySharer ==. Just rsid])
keyListedByActor'
-- | Given a shared key we have in our DB, verify that the given actor lists
-- this key, and update the DB accordingly.
--
-- * If we know the actor and we have a record that it lists the key, return
-- success, otherwise no action
-- * If we know the actor but we don't have a record of usage, fetch the
-- actor and verify usage. If the actor already has 2 known keys, return
-- error, otherwise store usage in DB.
-- * If we don't know the actor, fetch actor, verify usage, store actor and
-- usage in DB.
--
-- If we get success, that means the actor lists the key, and both the actor
-- and the usage exist in our DB now.
keyListedByActorShared
:: Manager
-> InstanceId
-> VerifKeyId
@ -611,7 +624,7 @@ keyListedByActor'
-> LocalURI
-> LocalURI
-> Handler (Either String ())
keyListedByActor' manager iid vkid host luKey luActor = do
keyListedByActorShared manager iid vkid host luKey luActor = do
mresult <- do
ments <- runDB $ do
mrs <- getBy $ UniqueRemoteSharer iid luActor
@ -640,92 +653,15 @@ keyListedByActor' manager iid vkid host luKey luActor = do
return $ Right ()
else return $ Left "Actor already has at least 2 keys"
data AddVerifKey = AddVerifKey
{ addvkHost :: Text
, addvkKeyId :: LocalURI
, addvkExpires :: Maybe UTCTime
, addvkKey :: PublicKey
, addvkActorId :: LocalURI
, addvkActorInbox :: LocalURI
}
addSharedKey :: AddVerifKey -> AppDB (Maybe String)
addSharedKey (AddVerifKey host luKey mexpires key luActor luInbox) = do
(iid, rsid, inew) <- instanceAndActor host luActor luInbox
room <-
case inew of
Nothing -> pure True
Just rsnew -> do
iRoom <- instanceRoom iid
if iRoom
then if rsnew
then pure True
else actorRoom rsid
else return False
if room
then do
vkid <- insert $ VerifKey luKey iid mexpires key Nothing
insert_ $ VerifKeySharedUsage vkid rsid
return Nothing
else return $ Just "We already store 2 keys"
where
instanceRoom iid =
(< 2) <$> count [VerifKeyInstance ==. iid, VerifKeySharer ==. Nothing]
addPersonalKey :: AddVerifKey -> AppDB (Maybe String)
addPersonalKey (AddVerifKey host luKey mexpires key luActor luInbox) = do
(iid, rsid, inew) <- instanceAndActor host luActor luInbox
room <-
if inew == Just False
then actorRoom rsid
else pure True
if room
then do
insert_ $ VerifKey luKey iid mexpires key (Just rsid)
return Nothing
else return $ Just "We already store 2 keys"
data UpdateVerifKey = UpdateVerifKey
{ updvkId :: VerifKeyId
, updvkExpires :: Maybe UTCTime
, updvkKey :: PublicKey
}
updateVerifKey :: UpdateVerifKey -> AppDB (Maybe String)
updateVerifKey (UpdateVerifKey vkid mexpires key) = do
update vkid [VerifKeyExpires =. mexpires, VerifKeyPublic =. key]
return Nothing
data VerifKeyUpdate
= VKUAddSharedKey AddVerifKey
| VKUAddPersonalKey AddVerifKey
| VKUUpdateKey UpdateVerifKey
updateVerifKeyInDB :: VerifKeyUpdate -> AppDB (Maybe String)
updateVerifKeyInDB (VKUAddSharedKey avk) = addSharedKey avk
updateVerifKeyInDB (VKUAddPersonalKey avk) = addPersonalKey avk
updateVerifKeyInDB (VKUUpdateKey uvk) = updateVerifKey uvk
data VerifKeyDetail = VerifKeyDetail
{ vkdKeyId :: LocalURI
, vkdInboxOrId :: Either LocalURI VerifKeyId
-- , vkdInboxOrId :: Either LocalURI VerifKeyId
, vkdKey :: PublicKey
, vkdExpires :: Maybe UTCTime
, vkdActorId :: LocalURI
, vkdShared :: Bool
}
makeVerifKeyUpdate :: Text -> VerifKeyDetail -> VerifKeyUpdate
makeVerifKeyUpdate
host (VerifKeyDetail luKey iori key mexpires luActor shared) =
case iori of
Left luInbox ->
let avk = AddVerifKey host luKey mexpires key luActor luInbox
in if shared
then VKUAddSharedKey avk
else VKUAddPersonalKey avk
Right vkid -> VKUUpdateKey $ UpdateVerifKey vkid mexpires key
instance YesodHttpSig App where
data HttpSigVerResult App = HttpSigVerResult (Either String FedURI)
httpSigVerHeaders = const [hRequestTarget, hHost, hActivityPubActor]
@ -735,32 +671,11 @@ instance YesodHttpSig App where
toSeconds :: TimeInterval -> Second
toSeconds = toTimeUnit
httpVerifySig (Verification malgo (KeyId keyid) input (Signature sig)) = fmap HttpSigVerResult $ runExceptT $ do
ExceptT . pure $ case malgo of
Nothing -> Right ()
Just algo ->
case algo of
S.AlgorithmEd25519 -> Right ()
S.AlgorithmOther _ -> Left "Unsupported algo in Sig header"
(host, luKey) <- ExceptT . pure $ case parseFedURI =<< (first displayException . decodeUtf8') keyid of
Left e -> Left $ "keyId in Sig header isn't a valid FedURI: " ++ e
Right uri -> Right $ f2l uri
signature <- ExceptT . pure $ do
case signature sig of
CryptoPassed s -> Right s
CryptoFailed e -> Left "Parsing Ed25519 signature failed"
mluActorHeader <- do
bs <- lookupHeaders hActivityPubActor
case bs of
[] -> return Nothing
[b] -> fmap Just . ExceptT . pure $ do
t <- first displayException $ decodeUtf8' b
(h, lu) <- f2l <$> parseFedURI t
if h == host
then Right ()
else Left "Key and actor have different hosts"
Right lu
_ -> throwE "Multiple ActivityPub-Actor headers"
vkd <- do
verifySigAlgo malgo
(host, luKey) <- f2l <$> parseKeyId keyid
signature <- parseSig sig
mluActorHeader <- getActorHeader host
(inboxOrVkid, vkd) <- do
ments <- lift $ runDB $ do
mvk <- runMaybeT $ do
Entity iid _ <- MaybeT $ getBy $ UniqueInstance host
@ -785,68 +700,133 @@ instance YesodHttpSig App where
Just u -> return u
manager <- getsYesod appHttpManager
let iid = verifKeyInstance vk
ExceptT $
keyListedByActor' manager iid vkid host luKey ua
ExceptT $ keyListedByActorShared manager iid vkid host luKey ua
return (ua, True)
return VerifKeyDetail
{ vkdKeyId = luKey
, vkdInboxOrId = Right vkid
, vkdKey = verifKeyPublic vk
, vkdExpires = verifKeyExpires vk
, vkdActorId = ua
, vkdShared = s
}
return
( Right vkid
, VerifKeyDetail
{ vkdKeyId = luKey
, vkdKey = verifKeyPublic vk
, vkdExpires = verifKeyExpires vk
, vkdActorId = ua
, vkdShared = s
}
)
Nothing -> fetched2vkd luKey <$> fetchKey' 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
-- existsInDB = isRight $ vkdInboxOrId vkd
now <- liftIO getCurrentTime
let stillValid Nothing = True
stillValid (Just expires) = expires > now
mvkd <-
if verify' (vkdKey vkd) && stillValid (vkdExpires vkd)
then return $ if existsInDB
then Nothing
else Just vkd
else if existsInDB
then do
Fetched newKey newExp newActor _newInbox s <- fetchKey' host mluActorHeader luKey
if vkdShared vkd == s
if verify' (vkdKey vkd) && stillValid (vkdExpires vkd)
then case inboxOrVkid of
Left uinb -> ExceptT $ runDB $ addVerifKey host uinb vkd
Right _vkid -> 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 scope changed, we reject that"
unless (vkdShared vkd) $
if newActor == vkdActorId vkd
then return ()
else throwE "Key owner changed, we reject that"
if stillValid newExp
then return ()
else errTime
if verify' newKey
then return $ Just vkd
{ vkdKey = newKey
, vkdExpires = newExp
}
else errSig
else if stillValid $ vkdExpires vkd
then errSig
else errTime
else throwE "Key owner changed, we reject that"
if stillValid newExp
then return ()
else errTime
if verify' newKey
then lift $ runDB $ updateVerifKey vkid vkd
{ vkdKey = newKey
, vkdExpires = newExp
}
else errSig
for_ mvkd $ ExceptT . fmap (maybe (Right ()) Left) . runDB . updateVerifKeyInDB . makeVerifKeyUpdate host
return $ l2f host $ vkdActorId vkd
where
verifySigAlgo = traverse_ $ \ algo ->
case algo of
S.AlgorithmEd25519 -> return ()
S.AlgorithmOther _ -> throwE "Unsupported algo in Sig header"
parseKeyId k =
case parseFedURI =<< (first displayException . decodeUtf8') k of
Left e -> throwE $ "keyId in Sig header isn't a valid FedURI: " ++ e
Right u -> return u
parseSig b =
case signature b of
CryptoPassed s -> return s
CryptoFailed e -> throwE "Parsing Ed25519 signature failed"
getActorHeader host = do
bs <- lookupHeaders hActivityPubActor
case bs of
[] -> return Nothing
[b] -> fmap Just . ExceptT . pure $ do
t <- first displayException $ decodeUtf8' b
(h, lu) <- f2l <$> parseFedURI t
if h == host
then Right ()
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) = VerifKeyDetail
{ vkdKeyId = uk
, vkdInboxOrId = Left uinb
, vkdKey = k
, vkdExpires = mexp
, vkdActorId = ua
, vkdShared = s
}
fetched2vkd uk (Fetched k mexp ua uinb s) =
( Left uinb
, VerifKeyDetail
{ vkdKeyId = uk
, vkdKey = k
, vkdExpires = mexp
, vkdActorId = ua
, vkdShared = s
}
)
addVerifKey h uinb vkd =
if vkdShared vkd
then addSharedKey h uinb vkd
else addPersonalKey h uinb vkd
where
addSharedKey host luInbox (VerifKeyDetail luKey key mexpires luActor _) = do
(iid, rsid, inew) <- instanceAndActor host luActor luInbox
room <-
case inew of
Nothing -> pure True
Just rsnew -> do
iRoom <- instanceRoom iid
if iRoom
then if rsnew
then pure True
else actorRoom rsid
else return False
if room
then do
vkid <- insert $ VerifKey luKey iid mexpires key Nothing
insert_ $ VerifKeySharedUsage vkid rsid
return $ Right ()
else return $ Left "We already store 2 keys"
where
instanceRoom iid =
(< 2) <$> count [VerifKeyInstance ==. iid, VerifKeySharer ==. Nothing]
addPersonalKey host luInbox (VerifKeyDetail luKey key mexpires luActor _) = do
(iid, rsid, inew) <- instanceAndActor host luActor luInbox
room <-
if inew == Just False
then actorRoom rsid
else pure True
if room
then do
insert_ $ VerifKey luKey iid mexpires key (Just rsid)
return $ Right ()
else return $ Left "We already store 2 keys"
updateVerifKey vkid vkd =
update vkid [VerifKeyExpires =. vkdExpires vkd, VerifKeyPublic =. vkdKey vkd]
instance YesodBreadcrumbs App where
breadcrumb route = return $ case route of

View file

@ -545,23 +545,34 @@ fetchKey
-> m (Either String Fetched)
fetchKey manager sigAlgo host mluActor luKey = runExceptT $ do
obj <- fetchAPIDOrH manager publicKeyId host luKey
(actor, pkey) <-
(pem, mkFetched, malgo) <-
case obj of
Left pkey -> do
luActor <-
(oi, luActor) <-
case publicKeyOwner pkey of
OwnerInstance ->
case mluActor of
Nothing -> throwE "Key is shared but actor header not specified!"
Just u -> return u
Just u -> return (True, u)
OwnerActor owner -> do
for_ mluActor $ \ lu ->
if owner == lu
then return ()
else throwE "Key's owner doesn't match actor header"
return owner
actor <- ExceptT $ keyListedByActor manager host luKey luActor
return (actor, pkey)
return (False, owner)
inbox <- actorInbox <$> ExceptT (keyListedByActor manager host luKey luActor)
return
( publicKeyPem pkey
, \ k ->
Fetched
{ fetchedPublicKey = k
, fetchedKeyExpires = publicKeyExpires pkey
, fetchedActorId = luActor
, fetchedActorInbox = inbox
, fetchedKeyShared = oi
}
, publicKeyAlgo pkey
)
Right actor -> do
if actorId actor == luKey { luriFragment = "" }
then return ()
@ -581,15 +592,24 @@ fetchKey manager sigAlgo host mluActor luKey = runExceptT $ do
Just pk ->
case publicKeyOwner pk of
OwnerInstance -> throwE "Actor's publicKey is shared, but embedded in actor document! We allow shared keys only if they're in a separate document"
OwnerActor _ -> return (actor, pk)
OwnerActor owner -> do
if owner == actorId actor
then return ()
else throwE "Actor's publicKey's owner doesn't match the actor's ID"
return
( publicKeyPem pk
, \ k ->
Fetched
{ fetchedPublicKey = k
, fetchedKeyExpires = publicKeyExpires pk
, fetchedActorId = owner
, fetchedActorInbox = actorInbox actor
, fetchedKeyShared = False
}
, publicKeyAlgo pk
)
ExceptT . pure $ do
case publicKeyOwner pkey of
OwnerInstance -> Right ()
OwnerActor owner ->
if owner == actorId actor
then Right ()
else Left "Actor's publicKey's owner doesn't match the actor's ID"
case publicKeyAlgo pkey of
case malgo of
Nothing ->
Left $
if sigAlgo
@ -603,12 +623,6 @@ fetchKey manager sigAlgo host mluActor luKey = runExceptT $ do
if sigAlgo
then "Algo mismatch, Ed25519 in Sig but unsupported algo in actor"
else "No algo in Sig, unsupported algo in actor"
case E.publicKey $ pemContent $ publicKeyPem pkey of
CryptoPassed k -> Right Fetched
{ fetchedPublicKey = k
, fetchedKeyExpires = publicKeyExpires pkey
, fetchedActorId = actorId actor
, fetchedActorInbox = actorInbox actor
, fetchedKeyShared = ownerShared $ publicKeyOwner pkey
}
case E.publicKey $ pemContent pem of
CryptoPassed k -> Right $ mkFetched k
CryptoFailed _ -> Left "Parsing Ed25519 public key failed"