Skip to content

Commit

Permalink
Merge pull request #218 from lolepezy/fix/use-cached-ta-cert
Browse files Browse the repository at this point in the history
Allow using cache TA certificate when it's impossible to fetch one for a TA
  • Loading branch information
lolepezy authored Sep 3, 2024
2 parents 6dfcca2 + 11fdbb1 commit ec6d0d7
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 78 deletions.
2 changes: 1 addition & 1 deletion package-template.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: rpki-prover
version: 0.9.5
version: 0.9.6
github: "lolepezy/rpki-prover"
license: BSD3
author: "Mikhail Puzanov"
Expand Down
6 changes: 3 additions & 3 deletions src/RPKI/Messages.hs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ toValidationMessage = \case

NoValidatedVersion -> "No tree validations has been run, cannot validate the object"

NoAKI -> "NO AKI found"
NoAKI -> "No AKI found"
ParentCertificateNotFound -> "Could not find parent ceertificate for the object"
ObjectNotOnManifest -> "Object is not listed on the parent manifest"

Expand All @@ -182,8 +182,8 @@ toValidationMessage = \case

TACertAKIIsNotEmpty u -> [i|TA certificate #{u} has an AKI.|]

TACertOlderThanPrevious {..} ->
[i|New TA certificate has validity period of #{before}-#{after}, previous one has #{prevBefore}-#{prevAfter}. |] <>
TACertPreferCachedCopy TACertValidities {..} ->
[i|New TA certificate has validity period of #{notBefore}-#{notAfter}, previous one has #{cachedNotBefore}-#{cachedNotAfter}. |] <>
[i|Will use previous TA certificate. NOTE: this means something really bad happened to the TA, consider stopping to use it at all.|]

CertNoPolicyExtension -> [i|Certificate has no policy extension.|]
Expand Down
1 change: 1 addition & 0 deletions src/RPKI/Orphans/Json.hs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ instance ToJSON InternalError
instance ToJSON SlurmError
instance ToJSON a => ToJSON (ParseError a)
instance ToJSON RpkiObjectType
instance ToJSON TACertValidities
instance ToJSON ValidationError
instance ToJSON ObjectIdentity
instance ToJSON StorageError
Expand Down
17 changes: 11 additions & 6 deletions src/RPKI/Reporting.hs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ newtype ParseError s = ParseError s
deriving stock (Show, Eq, Ord, Generic)
deriving anyclass (TheBinary, NFData)


data TACertValidities = TACertValidities {
notBefore :: Instant,
notAfter :: Instant,
cachedNotBefore :: Instant,
cachedNotAfter :: Instant
}
deriving stock (Show, Eq, Ord, Generic)
deriving anyclass (TheBinary, NFData)

data ValidationError = SPKIMismatch SPKI SPKI |
UnknownObjectAsTACert |
ObjectIsTooSmall Integer |
Expand All @@ -63,12 +73,7 @@ data ValidationError = SPKIMismatch SPKI SPKI |
NotFoundOnChecklist Hash Text |
ChecklistFileNameMismatch Hash Text Text |
TACertAKIIsNotEmpty URI |
TACertOlderThanPrevious {
before :: Instant,
after :: Instant,
prevBefore :: Instant,
prevAfter :: Instant
} |
TACertPreferCachedCopy TACertValidities |
CertNoPolicyExtension |
CertBrokenExtension OID BS.ByteString |
UnknownCriticalCertificateExtension OID BS.ByteString |
Expand Down
5 changes: 4 additions & 1 deletion src/RPKI/Store/Database.hs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ import RPKI.Time
-- It is brittle and inconvenient, but so far seems to be
-- the only realistic option.
currentDatabaseVersion :: Integer
currentDatabaseVersion = 32
currentDatabaseVersion = 33

-- Some constant keys
databaseVersionKey, lastValidMftKey, forAsyncFetchKey :: Text
Expand Down Expand Up @@ -559,6 +559,9 @@ getBySKI tx db@DB { objectStore = RpkiObjectStore {..} } ski = liftIO $ runMaybe
saveTA :: (MonadIO m, Storage s) => Tx s 'RW -> DB s -> StorableTA -> m ()
saveTA tx DB { taStore = TAStore s } ta = liftIO $ M.put tx s (getTaName $ tal ta) ta

deleteTA :: (MonadIO m, Storage s) => Tx s 'RW -> DB s -> TAL -> m ()
deleteTA tx DB { taStore = TAStore s } tal = liftIO $ M.delete tx s (getTaName tal)

getTA :: (MonadIO m, Storage s) => Tx s mode -> DB s -> TaName -> m (Maybe StorableTA)
getTA tx DB { taStore = TAStore s } name = liftIO $ M.get tx s name

Expand Down
2 changes: 1 addition & 1 deletion src/RPKI/Store/MakeLmdb.hs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ createDatabase env logger checkAction = do
dbVersion <- getDatabaseVersion tx db
case dbVersion of
Nothing -> do
logInfo logger [i|Cache version is not set, setting it to #{currentDatabaseVersion}, dropping the cache.|]
logInfo logger [i|Cache version is not set, setting it to #{currentDatabaseVersion}, cleaning up the cache.|]
(_, ms) <- timedMS $ emptyDBMaps tx db
logDebug logger [i|Erasing cache took #{ms}ms.|]
saveCurrentDatabaseVersion tx db
Expand Down
3 changes: 2 additions & 1 deletion src/RPKI/Store/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ data StorableTA = StorableTA {
tal :: TAL,
taCert :: CaCerObject,
fetchStatus :: FetchStatus,
initialRepositories :: PublicationPointAccess
initialRepositories :: PublicationPointAccess,
actualUrl :: RpkiURL
}
deriving (Show, Eq, Generic, TheBinary)

Expand Down
111 changes: 70 additions & 41 deletions src/RPKI/Validation/ObjectValidation.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedStrings #-}
Expand Down Expand Up @@ -145,21 +146,43 @@ validateTaCertAKI taCert u =
| SKI ki == getSKI taCert -> pure ()
| otherwise -> vPureError $ TACertAKIIsNotEmpty (getURL u)

-- | Compare new certificate and the previous one
-- If validaity period of the new certificate is somehow earlier than
-- the one of the previoius certificate, emit a warning and use
-- the previous certificate.
--
validateTACertWithPreviousCert :: CaCerObject -> CaCerObject -> PureValidatorT CaCerObject
validateTACertWithPreviousCert cert previousCert = do
-- Use the tiebreaker logic proposed by
-- https://datatracker.ietf.org/doc/draft-spaghetti-sidrops-rpki-ta-tiebreaker/
--
-- Emit a warning when deciding to use the cached certificate
-- instead of the fetched one.
--
chooseTaCert :: CaCerObject -> CaCerObject -> PureValidatorT CaCerObject
chooseTaCert cert cachedCert = do
let validities = bimap Instant Instant . certValidity . cwsX509certificate . getCertWithSignature
let (before, after) = validities cert
let (prevBefore, prevAfter) = validities previousCert
if before < prevBefore || after < prevAfter
then do
void $ vPureWarning $ TACertOlderThanPrevious{..}
pure previousCert
else pure cert
let (notBefore, notAfter) = validities cert
let (cachedNotBefore, cachedNotAfter) = validities cachedCert
let bothValidities = TACertValidities {..}

{-
Check whether the retrieved object has a more recent
notBefore than the locally cached copy of the retrieved TA.
If the notBefore of the retrieved object is less recent,
use the locally cached copy of the retrieved TA.
-}
if | notBefore < cachedNotBefore -> do
void $ vPureWarning $ TACertPreferCachedCopy bothValidities
pure cachedCert

{-
If the notBefore dates are equal, check whether the
retrieved object has a shorter validity period than the
locally cached copy of the retrieved TA. If the validity
period of the retrieved object is longer, use the locally
cached copy of the retrieved TA.
-}
| notBefore == cachedNotBefore && cachedNotAfter < notAfter -> do
void $ vPureWarning $ TACertPreferCachedCopy bothValidities
pure cachedCert

| otherwise -> pure cert


-- | In general, resource certifcate validation is:
--
Expand Down Expand Up @@ -205,53 +228,53 @@ validateResourceCert now cert parentCert vcrl = do

validateObjectValidityPeriod :: WithValidityPeriod c => c -> Now -> PureValidatorT ()
validateObjectValidityPeriod c (Now now) = do
let (before, after) = getValidityPeriod c
when (now < before) $
vPureError $ ObjectValidityIsInTheFuture before after
when (now > after) $
vPureError $ ObjectIsExpired before after
let (notBefore, notAfter) = getValidityPeriod c
when (now < notBefore) $
vPureError $ ObjectValidityIsInTheFuture notBefore notAfter
when (now > notAfter) $
vPureError $ ObjectIsExpired notBefore notAfter


validateResources ::
(WithRawResourceCertificate c,
(WithRawResourceCertificate child,
WithRawResourceCertificate parent,
WithRFC c,
WithRFC child,
OfCertType parent 'CACert) =>
Maybe (VerifiedRS PrefixesAndAsns) ->
c ->
child ->
parent ->
PureValidatorT (VerifiedRS PrefixesAndAsns)
validateResources verifiedResources cert parentCert =
validateResources verifiedResources childCert parentCert =
validateChildParentResources
(getRFC cert)
(getRawCert cert ^. typed)
(getRFC childCert)
(getRawCert childCert ^. typed)
(getRawCert parentCert ^. typed)
verifiedResources


validateBgpCert ::
forall c parent.
( WithRawResourceCertificate c
forall child parent.
( WithRawResourceCertificate child
, WithRawResourceCertificate parent
, WithSKI parent
, WithAKI c
, WithSKI c
, WithValidityPeriod c
, WithSerial c
, OfCertType c 'BGPCert
, OfCertType parent 'CACert
, WithAKI child
, WithSKI child
, WithValidityPeriod child
, WithSerial child
, child `OfCertType` BGPCert
, parent `OfCertType` CACert
) =>
Now ->
c ->
child ->
parent ->
Validated CrlObject ->
PureValidatorT (Validated c, BGPSecPayload)
PureValidatorT (Validated child, BGPSecPayload)
validateBgpCert now bgpCert parentCert validCrl = do
-- Validate BGP certificate according to
-- https://www.rfc-editor.org/rfc/rfc8209.html#section-3.3

-- Validate resource set
void $ validateResourceCert @_ @_ @'BGPCert now bgpCert parentCert validCrl
void $ validateResourceCert @_ @_ @BGPCert now bgpCert parentCert validCrl

let cwsX509 = cwsX509certificate $ getCertWithSignature bgpCert

Expand Down Expand Up @@ -304,10 +327,12 @@ validateCrl now crlObject@CrlObject {..} parentCert = do


validateMft ::
(WithRawResourceCertificate c, WithSKI c, OfCertType c 'CACert) =>
(WithRawResourceCertificate parent,
WithSKI parent,
parent `OfCertType` CACert) =>
Now ->
MftObject ->
c ->
parent ->
Validated CrlObject ->
Maybe (VerifiedRS PrefixesAndAsns) ->
PureValidatorT (Validated MftObject)
Expand All @@ -329,10 +354,12 @@ validateMft now mft parentCert crl verifiedResources = do


validateRoa ::
(WithRawResourceCertificate c, WithSKI c, OfCertType c 'CACert) =>
(WithRawResourceCertificate parent,
WithSKI parent,
OfCertType parent CACert) =>
Now ->
RoaObject ->
c ->
parent ->
Validated CrlObject ->
Maybe (VerifiedRS PrefixesAndAsns) ->
PureValidatorT (Validated RoaObject)
Expand Down Expand Up @@ -365,10 +392,12 @@ validateRoa now roa parentCert crl verifiedResources = do
vPureError $ errorReport vrs

validateSpl ::
(WithRawResourceCertificate c, WithSKI c, OfCertType c 'CACert) =>
(WithRawResourceCertificate parent,
WithSKI parent,
OfCertType parent CACert) =>
Now ->
SplObject ->
c ->
parent ->
Validated CrlObject ->
Maybe (VerifiedRS PrefixesAndAsns) ->
PureValidatorT (Validated SplObject)
Expand Down
Loading

0 comments on commit ec6d0d7

Please sign in to comment.