summaryrefslogtreecommitdiff
path: root/source/Felix/Checking/Authority.hs
blob: 58c123f55541101efb68290a921662db4dc7b371 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE NoImplicitPrelude #-}

-- | Compact public fact authority and exact contextual authorization.
module Felix.Checking.Authority
    ( EscapeKind(..)
    , EscapeKinds
    , emptyEscapeKinds
    , singletonEscapeKind
    , escapeKinds
    , escapeKindsToList
    , unionEscapeKinds
    , AuthoritySafety
    , cleanAuthoritySafety
    , authoritySafety
    , authoritySafetyEscapeKinds
    , unionAuthoritySafety
    , FactAuthority
    , factAuthority
    , factAuthorityTheorem
    , factAuthoritySafety
    , PreparedRequestDialect(..)
    , PreparedRequestMode(..)
    , PreparedRequestId
    , preparedRequestId
    , GuardedRuleSet
    , guardedRuleSet
    , guardedRuleTags
    , KernelConstructionDescriptor(..)
    , DatatypeCompilationDescriptor
    , datatypeCompilationDescriptor
    , CompilationDescriptor(..)
    , DirectAuthorization(..)
    , ValidationCertificate
    , validationCertificate
    , validationTarget
    , validationDirectAuthorization
    , ValidationCertificateError(..)
    , CandidateSafety
    , initialCandidateSafety
    , candidateSafetyAuthority
    , candidateFactAuthority
    , accumulateFactSafety
    , addCandidateEscape
    , FactSafetyError(..)
    , putEscapeKindsCache
    , getEscapeKindsCache
    , putAuthoritySafetyCache
    , getAuthoritySafetyCache
    , putFactAuthorityCache
    , getFactAuthorityCache
    , putPreparedRequestIdCache
    , getPreparedRequestIdCache
    , putDirectAuthorizationCache
    , getDirectAuthorizationCache
    , putValidationCertificateCache
    , getValidationCertificateCache
    ) where

import Base
import Felix.Checking.Foundation
import Felix.Checking.Identity
import Felix.Cache.Codec

import Control.DeepSeq (NFData)
import Control.Monad (unless, when)
import Data.Bits ((.&.), (.|.), bit, complement)
import Data.ByteString (ByteString)
import Data.ByteString qualified as ByteString
import Data.List (filter)
import Data.List.NonEmpty qualified as NonEmpty
import Data.Set qualified as Set
import Data.Word (Word8)


data EscapeKind
    = SourceAxiom
    | Omitted
    deriving stock (Show, Eq, Ord, Enum, Bounded, Generic)
    deriving anyclass (NFData)

-- | Canonical bounded set. The bit positions are stable cache tags.
newtype EscapeKinds =
    EscapeKinds Word8
    deriving stock (Show, Eq, Ord, Generic)
    deriving newtype (NFData)

emptyEscapeKinds :: EscapeKinds
emptyEscapeKinds =
    EscapeKinds 0

singletonEscapeKind :: EscapeKind -> EscapeKinds
singletonEscapeKind =
    EscapeKinds . escapeKindBit

escapeKinds :: Foldable collection => collection EscapeKind -> EscapeKinds
escapeKinds =
    foldl'
        (\acc kind ->
            unionEscapeKinds acc (singletonEscapeKind kind))
        emptyEscapeKinds

escapeKindsToList :: EscapeKinds -> [EscapeKind]
escapeKindsToList kinds =
    filter
        (\kind ->
            let EscapeKinds bits = kinds
            in bits .&. escapeKindBit kind /= 0)
        [SourceAxiom, Omitted]

unionEscapeKinds :: EscapeKinds -> EscapeKinds -> EscapeKinds
unionEscapeKinds (EscapeKinds left) (EscapeKinds right) =
    EscapeKinds (left .|. right)

escapeKindBit :: EscapeKind -> Word8
escapeKindBit = \case
    SourceAxiom -> bit 0
    Omitted -> bit 1

allEscapeBits :: Word8
allEscapeBits =
    escapeKindBit SourceAxiom .|. escapeKindBit Omitted


data AuthoritySafety
    = Clean
    | EscapeHatchBacked !EscapeKinds
    deriving stock (Show, Eq, Ord, Generic)
    deriving anyclass (NFData)

cleanAuthoritySafety :: AuthoritySafety
cleanAuthoritySafety =
    Clean

authoritySafety :: EscapeKinds -> AuthoritySafety
authoritySafety kinds
    | kinds == emptyEscapeKinds = Clean
    | otherwise = EscapeHatchBacked kinds

authoritySafetyEscapeKinds :: AuthoritySafety -> EscapeKinds
authoritySafetyEscapeKinds = \case
    Clean -> emptyEscapeKinds
    EscapeHatchBacked kinds -> kinds

unionAuthoritySafety
    :: AuthoritySafety
    -> AuthoritySafety
    -> AuthoritySafety
unionAuthoritySafety left right =
    authoritySafety
        (unionEscapeKinds
            (authoritySafetyEscapeKinds left)
            (authoritySafetyEscapeKinds right))


data FactAuthority = FactAuthority
    !TheoremRef
    !AuthoritySafety
    deriving stock (Show, Eq, Ord, Generic)
    deriving anyclass (NFData)

factAuthority :: TheoremRef -> AuthoritySafety -> FactAuthority
factAuthority =
    FactAuthority

factAuthorityTheorem :: FactAuthority -> TheoremRef
factAuthorityTheorem (FactAuthority reference _) =
    reference

factAuthoritySafety :: FactAuthority -> AuthoritySafety
factAuthoritySafety (FactAuthority _ safety) =
    safety


data PreparedRequestDialect
    = PreparedRequestFof
    | PreparedRequestTh0
    deriving stock (Show, Eq, Ord, Generic)

data PreparedRequestMode
    = PreparedRequestDirect
    | PreparedRequestIndirect
    deriving stock (Show, Eq, Ord, Generic)

newtype PreparedRequestId =
    PreparedRequestId CacheDigest
    deriving stock (Show, Eq, Ord, Generic)
    deriving newtype (Hashable, NFData)

preparedRequestId
    :: PreparedRequestDialect
    -> PreparedRequestMode
    -> ByteString
    -> PreparedRequestId
preparedRequestId dialect mode bytes =
    PreparedRequestId
        (hashCacheFields
            "felix-prepared-request-v2"
            [ ByteString.singleton (preparedRequestDialectTag dialect)
            , ByteString.singleton (preparedRequestModeTag mode)
            , bytes
            ])

preparedRequestDialectTag :: PreparedRequestDialect -> Word8
preparedRequestDialectTag = \case
    PreparedRequestFof -> 0x00
    PreparedRequestTh0 -> 0x01

preparedRequestModeTag :: PreparedRequestMode -> Word8
preparedRequestModeTag = \case
    PreparedRequestDirect -> 0x00
    PreparedRequestIndirect -> 0x01


-- | A nonempty canonical set of guarded kernel rules.
newtype GuardedRuleSet =
    GuardedRuleSet (Set KernelRuleTag)
    deriving stock (Show, Eq, Ord, Generic)

guardedRuleSet :: NonEmpty KernelRuleTag -> GuardedRuleSet
guardedRuleSet =
    GuardedRuleSet . Set.fromList . NonEmpty.toList

guardedRuleTags :: GuardedRuleSet -> Set KernelRuleTag
guardedRuleTags (GuardedRuleSet rules) =
    rules

data KernelConstructionDescriptor
    = FoundationLeaf !FoundationAxiomTag
    | GuardedFoundationRules !GuardedRuleSet
    | CheckedDefinitionEquation !ObjectId
    | CheckedSetConstructionExtensionality !ObjectId !CacheDigest
    deriving stock (Show, Eq, Ord, Generic)

-- | Exact semantic members of one trusted datatype compilation.
data DatatypeCompilationDescriptor =
    DatatypeCompilationDescriptor
        !ObjectId
        !(NonEmpty ObjectId)
        ![TheoremRef]
    deriving stock (Show, Eq, Ord, Generic)

datatypeCompilationDescriptor
    :: ObjectId
    -> NonEmpty ObjectId
    -> [TheoremRef]
    -> DatatypeCompilationDescriptor
datatypeCompilationDescriptor =
    DatatypeCompilationDescriptor

data CompilationDescriptor
    = DatatypeCompilation !DatatypeCompilationDescriptor
    deriving stock (Show, Eq, Ord, Generic)

data DirectAuthorization
    = CheckedKernelConstruction !KernelConstructionDescriptor
    | CheckedSourceProof ![PreparedRequestId]
    | TrustedCompilation !CompilationDescriptor
    | SourceAxiomAuthorization
    | OmittedAuthorization
    deriving stock (Show, Eq, Ord, Generic)


data ValidationCertificate = ValidationCertificate
    !FactAuthority
    !DirectAuthorization
    deriving stock (Show, Eq, Ord, Generic)

data ValidationCertificateError
    = SourceAxiomSafetyMismatch !AuthoritySafety
    | OmittedSafetyMissing !AuthoritySafety
    deriving stock (Show, Eq)

validationCertificate
    :: FactAuthority
    -> DirectAuthorization
    -> Either ValidationCertificateError ValidationCertificate
validationCertificate target direct = do
    case direct of
        SourceAxiomAuthorization ->
            unless
                (factAuthoritySafety target
                    == authoritySafety
                        (singletonEscapeKind SourceAxiom))
                (Left
                    (SourceAxiomSafetyMismatch
                        (factAuthoritySafety target)))
        OmittedAuthorization ->
            unless
                (Omitted
                    `elem` escapeKindsToList
                        (authoritySafetyEscapeKinds
                            (factAuthoritySafety target)))
                (Left
                    (OmittedSafetyMissing
                        (factAuthoritySafety target)))
        CheckedKernelConstruction{} -> pure ()
        CheckedSourceProof{} -> pure ()
        TrustedCompilation{} -> pure ()
    pure (ValidationCertificate target direct)

validationTarget :: ValidationCertificate -> FactAuthority
validationTarget (ValidationCertificate target _) =
    target

validationDirectAuthorization
    :: ValidationCertificate
    -> DirectAuthorization
validationDirectAuthorization (ValidationCertificate _ direct) =
    direct


newtype CandidateSafety =
    CandidateSafety AuthoritySafety
    deriving stock (Show, Eq, Ord, Generic)
    deriving newtype (NFData)

initialCandidateSafety :: CandidateSafety
initialCandidateSafety =
    CandidateSafety Clean

candidateSafetyAuthority :: CandidateSafety -> AuthoritySafety
candidateSafetyAuthority (CandidateSafety safety) =
    safety

-- | Freeze the final safety of one completed candidate into public authority.
--
-- The trusted completion boundary uses this projection for both the inert
-- validation certificate and its runtime pending authorization.
candidateFactAuthority
    :: TheoremRef
    -> CandidateSafety
    -> FactAuthority
candidateFactAuthority reference (CandidateSafety safety) =
    FactAuthority reference safety

data FactSafetyError
    = FactSafetyTheoremMismatch !TheoremRef !TheoremRef
    deriving stock (Show, Eq)

-- | Check theorem agreement and accumulate already-authorized public safety.
--
-- This operation does not authorize a freely constructed 'FactAuthority'.
-- Callers may use it only after checking opaque global, imported, or staged
-- builder authorization at the boundary that owns that authority.
accumulateFactSafety
    :: TheoremRef
    -> FactAuthority
    -> CandidateSafety
    -> Either FactSafetyError CandidateSafety
accumulateFactSafety expected supplied (CandidateSafety current) = do
    unless
        (factAuthorityTheorem supplied == expected)
        (Left
            (FactSafetyTheoremMismatch
                expected
                (factAuthorityTheorem supplied)))
    pure
        (CandidateSafety
            (unionAuthoritySafety
                current
                (factAuthoritySafety supplied)))

addCandidateEscape
    :: EscapeKind
    -> CandidateSafety
    -> CandidateSafety
addCandidateEscape kind (CandidateSafety current) =
    CandidateSafety
        (unionAuthoritySafety
            current
            (authoritySafety (singletonEscapeKind kind)))


putEscapeKindsCache :: EscapeKinds -> CachePut
putEscapeKindsCache (EscapeKinds bits) =
    putCacheTag bits

getEscapeKindsCache :: CacheGet EscapeKinds
getEscapeKindsCache = do
    bits <- getCacheTag
    unless
        (bits .&. complement allEscapeBits == 0)
        (fail "unknown cache escape-kind bit")
    pure (EscapeKinds bits)

putAuthoritySafetyCache :: AuthoritySafety -> CachePut
putAuthoritySafetyCache = \case
    Clean ->
        putCacheTag 0x00
    EscapeHatchBacked kinds -> do
        putCacheTag 0x01
        putEscapeKindsCache kinds

getAuthoritySafetyCache :: CacheGet AuthoritySafety
getAuthoritySafetyCache =
    getCacheTag >>= \case
        0x00 ->
            pure Clean
        0x01 -> do
            kinds <- getEscapeKindsCache
            when
                (kinds == emptyEscapeKinds)
                (fail "empty escape-hatch-backed safety")
            pure (EscapeHatchBacked kinds)
        tag ->
            fail ("unknown cache authority-safety tag " <> show tag)

putFactAuthorityCache :: FactAuthority -> CachePut
putFactAuthorityCache (FactAuthority reference safety) = do
    putTheoremRefCache reference
    putAuthoritySafetyCache safety

getFactAuthorityCache :: CacheGet FactAuthority
getFactAuthorityCache =
    FactAuthority
        <$> getTheoremRefCache
        <*> getAuthoritySafetyCache

putPreparedRequestIdCache :: PreparedRequestId -> CachePut
putPreparedRequestIdCache (PreparedRequestId digest) =
    putCacheDigest digest

getPreparedRequestIdCache :: CacheGet PreparedRequestId
getPreparedRequestIdCache =
    PreparedRequestId <$> getCacheDigest

putDirectAuthorizationCache :: DirectAuthorization -> CachePut
putDirectAuthorizationCache = \case
    CheckedKernelConstruction descriptor -> do
        putCacheTag 0x00
        putKernelDescriptor descriptor
    CheckedSourceProof requests -> do
        putCacheTag 0x01
        putCacheList putPreparedRequestIdCache requests
    TrustedCompilation descriptor -> do
        putCacheTag 0x02
        putCompilationDescriptor descriptor
    SourceAxiomAuthorization ->
        putCacheTag 0x03
    OmittedAuthorization ->
        putCacheTag 0x04

getDirectAuthorizationCache :: CacheGet DirectAuthorization
getDirectAuthorizationCache =
    getCacheTag >>= \case
        0x00 ->
            CheckedKernelConstruction <$> getKernelDescriptor
        0x01 ->
            CheckedSourceProof
                <$> getCacheList getPreparedRequestIdCache
        0x02 ->
            TrustedCompilation <$> getCompilationDescriptor
        0x03 ->
            pure SourceAxiomAuthorization
        0x04 ->
            pure OmittedAuthorization
        tag ->
            fail ("unknown cache direct-authorization tag " <> show tag)

putValidationCertificateCache :: ValidationCertificate -> CachePut
putValidationCertificateCache
        (ValidationCertificate target direct) = do
    putFactAuthorityCache target
    putDirectAuthorizationCache direct

getValidationCertificateCache :: CacheGet ValidationCertificate
getValidationCertificateCache = do
    target <- getFactAuthorityCache
    direct <- getDirectAuthorizationCache
    case validationCertificate target direct of
        Left err ->
            fail ("invalid cache validation certificate: " <> show err)
        Right certificate ->
            pure certificate


putKernelDescriptor :: KernelConstructionDescriptor -> CachePut
putKernelDescriptor = \case
    FoundationLeaf tag -> do
        putCacheTag 0x00
        putFoundationTag tag
    GuardedFoundationRules rules -> do
        putCacheTag 0x01
        putCacheList putRuleTag
            (Set.toAscList (guardedRuleTags rules))
    CheckedDefinitionEquation identity -> do
        putCacheTag 0x02
        putObjectIdCache identity
    CheckedSetConstructionExtensionality identity construction -> do
        putCacheTag 0x03
        putObjectIdCache identity
        putCacheDigest construction

getKernelDescriptor :: CacheGet KernelConstructionDescriptor
getKernelDescriptor =
    getCacheTag >>= \case
        0x00 -> FoundationLeaf <$> getFoundationTag
        0x01 -> do
            tags <- getCacheList getRuleTag
            rules <-
                maybe
                    (fail "cache guarded-rule set is empty")
                    pure
                    (NonEmpty.nonEmpty tags)
            unless (strictlyIncreasing tags)
                (fail "cache guarded-rule tags are not in canonical order")
            pure (GuardedFoundationRules (guardedRuleSet rules))
        0x02 -> CheckedDefinitionEquation <$> getObjectIdCache
        0x03 ->
            CheckedSetConstructionExtensionality
                <$> getObjectIdCache
                <*> getCacheDigest
        tag ->
            fail ("unknown cache kernel-construction tag " <> show tag)

putCompilationDescriptor :: CompilationDescriptor -> CachePut
putCompilationDescriptor
        (DatatypeCompilation
            (DatatypeCompilationDescriptor
                carrier constructors facts)) = do
    putCacheTag 0x00
    putObjectIdCache carrier
    putCacheList putObjectIdCache (NonEmpty.toList constructors)
    putCacheList putTheoremRefCache facts

getCompilationDescriptor :: CacheGet CompilationDescriptor
getCompilationDescriptor =
    getCacheTag >>= \case
        0x00 -> do
            carrier <- getObjectIdCache
            rawConstructors <-
                getCacheList getObjectIdCache
            constructors <-
                maybe
                    (fail "cache datatype compilation has no constructors")
                    pure
                    (NonEmpty.nonEmpty rawConstructors)
            facts <- getCacheList getTheoremRefCache
            pure
                (DatatypeCompilation
                    (DatatypeCompilationDescriptor
                        carrier constructors facts))
        tag ->
            fail ("unknown cache compilation tag " <> show tag)

putFoundationTag :: FoundationAxiomTag -> CachePut
putFoundationTag =
    putCacheTag . \case
        EmptyCharacteristic -> 0x00
        PairSetCharacteristic -> 0x01
        FamilyUnionCharacteristic -> 0x02
        PowerSetCharacteristic -> 0x03
        SeparationCharacteristic -> 0x04
        ReplacementCharacteristic -> 0x05
        SetChooseWitness -> 0x06
        SetExtensionality -> 0x07
        SetInduction -> 0x08
        PropositionalExtensionality -> 0x09
        DoubleNegationElim -> 0x0a
        UnivOfContains -> 0x0b
        UnivOfTransitive -> 0x0c
        UnivOfFamilyUnionClosed -> 0x0d
        UnivOfPowerSetClosed -> 0x0e
        UnivOfReplacementClosed -> 0x0f
        UnivOfMinimal -> 0x10

getFoundationTag :: CacheGet FoundationAxiomTag
getFoundationTag =
    getCacheTag >>= \case
        0x00 -> pure EmptyCharacteristic
        0x01 -> pure PairSetCharacteristic
        0x02 -> pure FamilyUnionCharacteristic
        0x03 -> pure PowerSetCharacteristic
        0x04 -> pure SeparationCharacteristic
        0x05 -> pure ReplacementCharacteristic
        0x06 -> pure SetChooseWitness
        0x07 -> pure SetExtensionality
        0x08 -> pure SetInduction
        0x09 -> pure PropositionalExtensionality
        0x0a -> pure DoubleNegationElim
        0x0b -> pure UnivOfContains
        0x0c -> pure UnivOfTransitive
        0x0d -> pure UnivOfFamilyUnionClosed
        0x0e -> pure UnivOfPowerSetClosed
        0x0f -> pure UnivOfReplacementClosed
        0x10 -> pure UnivOfMinimal
        tag ->
            fail ("unknown cache foundation-axiom tag " <> show tag)

putRuleTag :: KernelRuleTag -> CachePut
putRuleTag =
    putCacheTag . \case
        SetLfpBound -> 0x00
        SetLfpLeast -> 0x01
        SetLfpFixed -> 0x02
        SetLfpInduct -> 0x03

getRuleTag :: CacheGet KernelRuleTag
getRuleTag =
    getCacheTag >>= \case
        0x00 -> pure SetLfpBound
        0x01 -> pure SetLfpLeast
        0x02 -> pure SetLfpFixed
        0x03 -> pure SetLfpInduct
        tag ->
            fail ("unknown cache kernel-rule tag " <> show tag)

strictlyIncreasing :: Ord value => [value] -> Bool
strictlyIncreasing values =
    and (zipWith (<) values (drop 1 values))