{-# LANGUAGE OverloadedStrings #-}
module Convex.ThreatModel.InvalidDatumIndex (
invalidDatumIndexAttack,
invalidDatumIndexAttackWith,
replaceConstrIndex,
) where
import Convex.ThreatModel
invalidDatumIndexAttack :: ThreatModel ()
invalidDatumIndexAttack :: ThreatModel ()
invalidDatumIndexAttack = Integer -> ThreatModel ()
invalidDatumIndexAttackWith Integer
5
invalidDatumIndexAttackWith :: Integer -> ThreatModel ()
invalidDatumIndexAttackWith :: Integer -> ThreatModel ()
invalidDatumIndexAttackWith Integer
invalidIdx = [Char] -> ThreatModel () -> ThreatModel ()
forall a. [Char] -> ThreatModel a -> ThreatModel a
Named ([Char]
"Invalid Datum Index Attack (index " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Integer -> [Char]
forall a. Show a => a -> [Char]
show Integer
invalidIdx [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
")") (ThreatModel () -> ThreatModel ())
-> ThreatModel () -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$ do
Input
_ <- (Input -> Bool) -> ThreatModel Input
anyInputSuchThat (Bool -> Bool
not (Bool -> Bool) -> (Input -> Bool) -> Input -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AddressAny -> Bool
isKeyAddressAny (AddressAny -> Bool) -> (Input -> AddressAny) -> Input -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Input -> AddressAny
forall t. IsInputOrOutput t => t -> AddressAny
addressOf)
[Output]
outputs <- ThreatModel [Output]
getTxOutputs
let scriptOutputsWithConstr :: [Output]
scriptOutputsWithConstr = (Output -> Bool) -> [Output] -> [Output]
forall a. (a -> Bool) -> [a] -> [a]
filter Output -> Bool
isScriptOutputWithConstrInlineDatum [Output]
outputs
ThreatModel () -> ThreatModel ()
forall a. ThreatModel a -> ThreatModel a
threatPrecondition (ThreatModel () -> ThreatModel ())
-> ThreatModel () -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$ Bool -> ThreatModel ()
ensure (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [Output] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Output]
scriptOutputsWithConstr)
Output
target <- [Output] -> ThreatModel Output
forall a. Show a => [a] -> ThreatModel a
pickAny [Output]
scriptOutputsWithConstr
ScriptData
originalDatum <- case Output -> Maybe ScriptData
getInlineDatum Output
target of
Maybe ScriptData
Nothing -> [Char] -> ThreatModel ScriptData
forall a. [Char] -> ThreatModel a
failPrecondition [Char]
"Script output missing inline datum"
Just ScriptData
d -> ScriptData -> ThreatModel ScriptData
forall a. a -> ThreatModel a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ScriptData
d
let mutatedDatum :: ScriptData
mutatedDatum = Integer -> ScriptData -> ScriptData
replaceConstrIndex Integer
invalidIdx ScriptData
originalDatum
[Char] -> ThreatModel ()
counterexampleTM ([Char] -> ThreatModel ()) -> [Char] -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$
[[Char]] -> [Char]
paragraph
[ [Char]
"The transaction contains a script output at index"
, TxIx -> [Char]
forall a. Show a => a -> [Char]
show (Output -> TxIx
outputIx Output
target)
, [Char]
"with an inline Constr datum."
]
[Char] -> ThreatModel ()
counterexampleTM ([Char] -> ThreatModel ()) -> [Char] -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$
[[Char]] -> [Char]
paragraph
[ [Char]
"Testing if the datum's constructor index can be replaced with"
, Integer -> [Char]
forall a. Show a => a -> [Char]
show Integer
invalidIdx
, [Char]
"while the fields are left unchanged, and the transaction still validates."
]
[Char] -> ThreatModel ()
counterexampleTM ([Char] -> ThreatModel ()) -> [Char] -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$
[[Char]] -> [Char]
paragraph
[ [Char]
"If this validates, the script's FromData parser accepts out-of-range"
, [Char]
"constructor indices (e.g., via a catch-all branch). An attacker could"
, [Char]
"exploit this to:"
, [Char]
"1) Confuse the validator about which state the datum represents"
, [Char]
"2) Bypass state-transition guards that depend on the constructor index"
, [Char]
"3) Lock funds permanently with an unspendable corrupted datum"
]
TxModifier -> ThreatModel ()
shouldNotValidate (TxModifier -> ThreatModel ()) -> TxModifier -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$ Output -> Datum -> TxModifier
forall t. IsInputOrOutput t => t -> Datum -> TxModifier
changeDatumOf Output
target (ScriptData -> Datum
toInlineDatum ScriptData
mutatedDatum)
replaceConstrIndex :: Integer -> ScriptData -> ScriptData
replaceConstrIndex :: Integer -> ScriptData -> ScriptData
replaceConstrIndex Integer
newIdx ScriptData
sd = case ScriptData
sd of
ScriptDataConstructor Integer
_idx [ScriptData]
fields -> Integer -> [ScriptData] -> ScriptData
ScriptDataConstructor Integer
newIdx [ScriptData]
fields
ScriptData
_ -> ScriptData
sd
isScriptOutputWithConstrInlineDatum :: Output -> Bool
isScriptOutputWithConstrInlineDatum :: Output -> Bool
isScriptOutputWithConstrInlineDatum Output
output =
Bool -> Bool
not (AddressAny -> Bool
isKeyAddressAny (Output -> AddressAny
forall t. IsInputOrOutput t => t -> AddressAny
addressOf Output
output))
Bool -> Bool -> Bool
&& case Output -> Maybe ScriptData
getInlineDatum Output
output of
Just (ScriptDataConstructor{}) -> Bool
True
Maybe ScriptData
_ -> Bool
False
getInlineDatum :: Output -> Maybe ScriptData
getInlineDatum :: Output -> Maybe ScriptData
getInlineDatum Output
output =
case TxOut CtxTx Era -> Datum
forall ctx. TxOut ctx Era -> TxOutDatum ctx Era
datumOfTxOut (Output -> TxOut CtxTx Era
outputTxOut Output
output) of
TxOutDatumInline BabbageEraOnwards Era
_ HashableScriptData
hashableData -> ScriptData -> Maybe ScriptData
forall a. a -> Maybe a
Just (HashableScriptData -> ScriptData
getScriptData HashableScriptData
hashableData)
Datum
_ -> Maybe ScriptData
forall a. Maybe a
Nothing
toInlineDatum :: ScriptData -> Datum
toInlineDatum :: ScriptData -> Datum
toInlineDatum ScriptData
sd =
BabbageEraOnwards Era -> HashableScriptData -> Datum
forall era ctx.
BabbageEraOnwards era -> HashableScriptData -> TxOutDatum ctx era
TxOutDatumInline BabbageEraOnwards Era
BabbageEraOnwardsConway (ScriptData -> HashableScriptData
unsafeHashableScriptData ScriptData
sd)