From c9bd198565ae279822b4ee82e552ce4a11bd0c0b Mon Sep 17 00:00:00 2001 From: Paul Kehrer <161495+reaperhulk@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:54:55 +0000 Subject: [PATCH 1/8] Implement verified Argon2 derivation on x86-64 --- .../Impl/Argon2/X86_64/AddressCache.lean | 32 +++ .../Impl/Argon2/X86_64/AddressCalls.lean | 37 ++++ .../Impl/Argon2/X86_64/AddressHeader.lean | 31 +++ .../Impl/Argon2/X86_64/AddressMode.lean | 27 +++ .../Impl/Argon2/X86_64/BlockAddress.lean | 20 ++ .../Impl/Argon2/X86_64/ClearBlock.lean | 18 ++ .../Impl/Argon2/X86_64/DependentWord.lean | 21 ++ .../Impl/Argon2/X86_64/FillBlock.lean | 12 ++ .../Impl/Argon2/X86_64/FillColumn.lean | 26 +++ .../Impl/Argon2/X86_64/FillCompress.lean | 33 ++++ .../Impl/Argon2/X86_64/FillFinish.lean | 13 ++ .../Impl/Argon2/X86_64/FillIteration.lean | 13 ++ .../Impl/Argon2/X86_64/FillIterations.lean | 20 ++ .../Impl/Argon2/X86_64/FillKernel.lean | 27 +++ .../Impl/Argon2/X86_64/FillLanes.lean | 16 ++ .../Impl/Argon2/X86_64/FillPointers.lean | 35 ++++ .../Impl/Argon2/X86_64/FillSegment.lean | 15 ++ .../Impl/Argon2/X86_64/FillSetup.lean | 18 ++ .../Impl/Argon2/X86_64/FillSlice.lean | 13 ++ .../Impl/Argon2/X86_64/FillSlices.lean | 15 ++ .../Impl/Argon2/X86_64/FillWrite.lean | 24 +++ .../Impl/Argon2/X86_64/FinalOutput.lean | 19 ++ .../Impl/Argon2/X86_64/FinalReduction.lean | 12 ++ .../Impl/Argon2/X86_64/Finish.lean | 13 ++ .../Impl/Argon2/X86_64/FirstLane.lean | 20 ++ .../Impl/Argon2/X86_64/InitFill.lean | 14 ++ .../Impl/Argon2/X86_64/Parameters.lean | 19 ++ .../Impl/Argon2/X86_64/RandomSource.lean | 17 ++ .../Impl/Argon2/X86_64/ReduceBlock.lean | 11 ++ .../Impl/Argon2/X86_64/ReduceLane.lean | 12 ++ .../Impl/Argon2/X86_64/ReduceLanes.lean | 16 ++ .../Impl/Argon2/X86_64/ReducePointers.lean | 19 ++ .../Impl/Argon2/X86_64/ReductionInit.lean | 15 ++ .../Impl/Argon2/X86_64/ReferenceMap.lean | 44 +++++ .../Impl/Argon2/X86_64/SegmentSetup.lean | 25 +++ .../Proof/Argon2/AddressInput.lean | 19 ++ .../Proof/Argon2/FillPositions.lean | 55 ++++++ .../Proof/Argon2/FillStep.lean | 41 ++++ .../Proof/Argon2/FinalReduction.lean | 45 +++++ .../Proof/Argon2/Iterations.lean | 24 +++ .../Proof/Argon2/IterationsIndices.lean | 41 ++++ lean/VerifiedGarbage/Proof/Argon2/Lanes.lean | 21 ++ .../Proof/Argon2/LanesIndices.lean | 61 ++++++ lean/VerifiedGarbage/Proof/Argon2/Matrix.lean | 39 ++++ .../VerifiedGarbage/Proof/Argon2/Segment.lean | 27 +++ .../Proof/Argon2/SegmentIndices.lean | 38 ++++ .../Proof/Argon2/SegmentStart.lean | 26 +++ .../Proof/Argon2/Serialization.lean | 29 +++ lean/VerifiedGarbage/Proof/Argon2/Slices.lean | 23 +++ .../Proof/Argon2/SlicesIndices.lean | 38 ++++ .../Proof/Argon2/X86_64/AddressCache.lean | 49 +++++ .../Argon2/X86_64/AddressCacheInvariant.lean | 87 ++++++++ .../Argon2/X86_64/AddressCacheMatrix.lean | 59 ++++++ .../Proof/Argon2/X86_64/AddressCacheMeta.lean | 77 ++++++++ .../Proof/Argon2/X86_64/AddressCacheSave.lean | 65 ++++++ .../Argon2/X86_64/AddressCacheSelect.lean | 114 +++++++++++ .../Argon2/X86_64/AddressCacheSelectCT.lean | 118 +++++++++++ .../Argon2/X86_64/AddressCacheState.lean | 53 +++++ .../Proof/Argon2/X86_64/AddressCacheWord.lean | 61 ++++++ .../Argon2/X86_64/AddressCacheWordCT.lean | 45 +++++ .../Proof/Argon2/X86_64/AddressCalls.lean | 64 ++++++ .../Proof/Argon2/X86_64/AddressCallsArgs.lean | 49 +++++ .../Proof/Argon2/X86_64/AddressCallsCT.lean | 94 +++++++++ .../Argon2/X86_64/AddressCallsClear.lean | 64 ++++++ .../Argon2/X86_64/AddressCallsLayout.lean | 79 ++++++++ .../Proof/Argon2/X86_64/AddressCallsMx.lean | 32 +++ .../Argon2/X86_64/AddressCallsPrepare.lean | 159 +++++++++++++++ .../Argon2/X86_64/AddressCallsStage.lean | 76 +++++++ .../Argon2/X86_64/AddressGeneration.lean | 39 ++++ .../Argon2/X86_64/AddressGenerationCT.lean | 95 +++++++++ .../Proof/Argon2/X86_64/AddressHeader.lean | 100 ++++++++++ .../Argon2/X86_64/AddressHeaderCorrect.lean | 64 ++++++ .../Proof/Argon2/X86_64/AddressHeaderLit.lean | 10 + .../Argon2/X86_64/AddressHeaderWords.lean | 38 ++++ .../Proof/Argon2/X86_64/AddressInputCT.lean | 26 +++ .../Proof/Argon2/X86_64/AddressMode.lean | 73 +++++++ .../Proof/Argon2/X86_64/AddressModeCT.lean | 18 ++ .../Proof/Argon2/X86_64/AddressModeLit.lean | 10 + .../Proof/Argon2/X86_64/AddressModeSteps.lean | 76 +++++++ .../Proof/Argon2/X86_64/BlockAddress.lean | 81 ++++++++ .../Proof/Argon2/X86_64/BlockAddressLit.lean | 10 + .../Proof/Argon2/X86_64/BlockStore.lean | 28 +++ .../Proof/Argon2/X86_64/ClearBlock.lean | 78 ++++++++ .../Proof/Argon2/X86_64/ClearBlockLit.lean | 10 + .../Proof/Argon2/X86_64/DependentWord.lean | 68 +++++++ .../Proof/Argon2/X86_64/DependentWordCT.lean | 53 +++++ .../Proof/Argon2/X86_64/DependentWordLit.lean | 10 + .../Argon2/X86_64/DependentWordPointer.lean | 52 +++++ .../Argon2/X86_64/DependentWordState.lean | 25 +++ .../Proof/Argon2/X86_64/FillAllocation.lean | 25 +++ .../Proof/Argon2/X86_64/FillBlock.lean | 47 +++++ .../Proof/Argon2/X86_64/FillBlockCT.lean | 72 +++++++ .../Proof/Argon2/X86_64/FillBlockCounter.lean | 26 +++ .../Proof/Argon2/X86_64/FillBlockFrame.lean | 37 ++++ .../Argon2/X86_64/FillCacheInvariant.lean | 57 ++++++ .../Proof/Argon2/X86_64/FillColumn.lean | 164 ++++++++++++++++ .../Proof/Argon2/X86_64/FillColumnCT.lean | 16 ++ .../Proof/Argon2/X86_64/FillColumnLit.lean | 10 + .../Proof/Argon2/X86_64/FillCompress.lean | 52 +++++ .../Proof/Argon2/X86_64/FillCompressArgs.lean | 64 ++++++ .../Proof/Argon2/X86_64/FillCompressCT.lean | 54 +++++ .../Proof/Argon2/X86_64/FillCompressCall.lean | 101 ++++++++++ .../Argon2/X86_64/FillCompressCallCT.lean | 30 +++ .../Proof/Argon2/X86_64/FillCompressLit.lean | 11 ++ .../Argon2/X86_64/FillCompressOperation.lean | 101 ++++++++++ .../X86_64/FillCompressOperationCT.lean | 87 ++++++++ .../X86_64/FillCompressOperationMx.lean | 14 ++ .../Argon2/X86_64/FillCompressSetup.lean | 162 +++++++++++++++ .../Proof/Argon2/X86_64/FillContext.lean | 61 ++++++ .../Proof/Argon2/X86_64/FillFinish.lean | 54 +++++ .../Proof/Argon2/X86_64/FillFinishCT.lean | 60 ++++++ .../Proof/Argon2/X86_64/FillFinishReady.lean | 39 ++++ .../Proof/Argon2/X86_64/FillHeader.lean | 71 +++++++ .../Proof/Argon2/X86_64/FillIndex.lean | 81 ++++++++ .../Proof/Argon2/X86_64/FillIteration.lean | 32 +++ .../Proof/Argon2/X86_64/FillIterationCT.lean | 49 +++++ .../Argon2/X86_64/FillIterationPrepare.lean | 31 +++ .../Proof/Argon2/X86_64/FillIterations.lean | 73 +++++++ .../Argon2/X86_64/FillIterationsBody.lean | 65 ++++++ .../Argon2/X86_64/FillIterationsBodyCT.lean | 62 ++++++ .../Proof/Argon2/X86_64/FillIterationsCT.lean | 51 +++++ .../Argon2/X86_64/FillIterationsFrame.lean | 47 +++++ .../Proof/Argon2/X86_64/FillKernel.lean | 67 +++++++ .../Proof/Argon2/X86_64/FillKernelArgs.lean | 80 ++++++++ .../Proof/Argon2/X86_64/FillKernelCT.lean | 57 ++++++ .../Argon2/X86_64/FillKernelInvariant.lean | 53 +++++ .../Proof/Argon2/X86_64/FillKernelLayout.lean | 101 ++++++++++ .../Argon2/X86_64/FillKernelMappingCT.lean | 114 +++++++++++ .../Proof/Argon2/X86_64/FillKernelMatrix.lean | 60 ++++++ .../Argon2/X86_64/FillKernelPrepare.lean | 67 +++++++ .../Proof/Argon2/X86_64/FillKernelSpec.lean | 32 +++ .../Proof/Argon2/X86_64/FillKernelStable.lean | 16 ++ .../Proof/Argon2/X86_64/FillLaneAdvance.lean | 69 +++++++ .../Proof/Argon2/X86_64/FillLanes.lean | 64 ++++++ .../Proof/Argon2/X86_64/FillLanesBody.lean | 54 +++++ .../Proof/Argon2/X86_64/FillLanesBodyCT.lean | 53 +++++ .../Proof/Argon2/X86_64/FillLanesCT.lean | 52 +++++ .../Proof/Argon2/X86_64/FillPassCounter.lean | 42 ++++ .../Proof/Argon2/X86_64/FillPassSave.lean | 79 ++++++++ .../Proof/Argon2/X86_64/FillPointers.lean | 99 ++++++++++ .../Proof/Argon2/X86_64/FillPointersArgs.lean | 76 +++++++ .../Proof/Argon2/X86_64/FillPointersCT.lean | 17 ++ .../Proof/Argon2/X86_64/FillPointersLit.lean | 10 + .../Proof/Argon2/X86_64/FillPointersNat.lean | 51 +++++ .../Proof/Argon2/X86_64/FillSegment.lean | 70 +++++++ .../Proof/Argon2/X86_64/FillSegmentBody.lean | 63 ++++++ .../Argon2/X86_64/FillSegmentBodyCT.lean | 82 ++++++++ .../Proof/Argon2/X86_64/FillSegmentCT.lean | 54 +++++ .../Proof/Argon2/X86_64/FillSetup.lean | 75 +++++++ .../Argon2/X86_64/FillSetupDimensions.lean | 50 +++++ .../Argon2/X86_64/FillSetupEnvironment.lean | 71 +++++++ .../Proof/Argon2/X86_64/FillSetupFinish.lean | 38 ++++ .../Proof/Argon2/X86_64/FillSetupReset.lean | 45 +++++ .../Proof/Argon2/X86_64/FillSlice.lean | 34 ++++ .../Proof/Argon2/X86_64/FillSliceAdvance.lean | 36 ++++ .../Proof/Argon2/X86_64/FillSliceCT.lean | 59 ++++++ .../Proof/Argon2/X86_64/FillSlicePrepare.lean | 31 +++ .../Proof/Argon2/X86_64/FillSlices.lean | 65 ++++++ .../Proof/Argon2/X86_64/FillSlicesBody.lean | 52 +++++ .../Proof/Argon2/X86_64/FillSlicesBodyCT.lean | 49 +++++ .../Proof/Argon2/X86_64/FillSlicesCT.lean | 59 ++++++ .../Proof/Argon2/X86_64/FillWrite.lean | 52 +++++ .../Proof/Argon2/X86_64/FillWriteCT.lean | 16 ++ .../Proof/Argon2/X86_64/FillWriteCover.lean | 34 ++++ .../Proof/Argon2/X86_64/FillWriteLit.lean | 10 + .../Proof/Argon2/X86_64/FillWritePrefix.lean | 90 +++++++++ .../Proof/Argon2/X86_64/FillWriteWord.lean | 40 ++++ .../Proof/Argon2/X86_64/FinalCall.lean | 94 +++++++++ .../Proof/Argon2/X86_64/FinalCallCT.lean | 36 ++++ .../Proof/Argon2/X86_64/FinalOutput.lean | 41 ++++ .../Proof/Argon2/X86_64/FinalOutputArgs.lean | 47 +++++ .../Proof/Argon2/X86_64/FinalOutputCT.lean | 56 ++++++ .../Proof/Argon2/X86_64/FinalOutputReady.lean | 39 ++++ .../Proof/Argon2/X86_64/FinalReduction.lean | 34 ++++ .../Proof/Argon2/X86_64/FinishReady.lean | 43 ++++ .../Proof/Argon2/X86_64/FinishStage.lean | 51 +++++ .../Proof/Argon2/X86_64/FinishStageCT.lean | 53 +++++ .../Proof/Argon2/X86_64/FirstLane.lean | 61 ++++++ .../Proof/Argon2/X86_64/FirstLaneCT.lean | 21 ++ .../Proof/Argon2/X86_64/FirstLaneLit.lean | 10 + .../Proof/Argon2/X86_64/InitFill.lean | 79 ++++++++ .../Proof/Argon2/X86_64/InitFillCT.lean | 90 +++++++++ .../Proof/Argon2/X86_64/InitFillFrames.lean | 49 +++++ .../Proof/Argon2/X86_64/InitFillReady.lean | 77 ++++++++ .../Proof/Argon2/X86_64/MemoryInit.lean | 4 +- .../Proof/Argon2/X86_64/MemoryInitDone.lean | 45 +++++ .../Argon2/X86_64/MemoryInitRepresent.lean | 21 ++ .../Proof/Argon2/X86_64/Parameters.lean | 50 +++++ .../Proof/Argon2/X86_64/ParametersCT.lean | 16 ++ .../Proof/Argon2/X86_64/ParametersLit.lean | 10 + .../Proof/Argon2/X86_64/ParametersSteps.lean | 46 +++++ .../Proof/Argon2/X86_64/RandomSource.lean | 100 ++++++++++ .../Proof/Argon2/X86_64/RandomSourceCT.lean | 74 +++++++ .../Argon2/X86_64/RandomSourceCounter.lean | 37 ++++ .../Argon2/X86_64/RandomSourcePrepare.lean | 60 ++++++ .../Argon2/X86_64/RandomSourceState.lean | 23 +++ .../Proof/Argon2/X86_64/ReduceBlock.lean | 34 ++++ .../Proof/Argon2/X86_64/ReduceBlockCT.lean | 15 ++ .../Proof/Argon2/X86_64/ReduceBlockLit.lean | 8 + .../Proof/Argon2/X86_64/ReduceLane.lean | 89 +++++++++ .../Proof/Argon2/X86_64/ReduceLaneCT.lean | 45 +++++ .../Proof/Argon2/X86_64/ReduceLanes.lean | 55 ++++++ .../Proof/Argon2/X86_64/ReduceLanesBody.lean | 61 ++++++ .../Argon2/X86_64/ReduceLanesBodyCT.lean | 58 ++++++ .../Proof/Argon2/X86_64/ReduceLanesCT.lean | 36 ++++ .../Proof/Argon2/X86_64/ReducePointers.lean | 69 +++++++ .../Proof/Argon2/X86_64/ReducePointersCT.lean | 18 ++ .../Proof/Argon2/X86_64/ReductionClear.lean | 64 ++++++ .../Proof/Argon2/X86_64/ReductionInit.lean | 72 +++++++ .../Proof/Argon2/X86_64/ReductionInitCT.lean | 54 +++++ .../Argon2/X86_64/ReductionLoopState.lean | 36 ++++ .../Proof/Argon2/X86_64/ReductionState.lean | 58 ++++++ .../Proof/Argon2/X86_64/ReferenceMap.lean | 39 ++++ .../Proof/Argon2/X86_64/ReferenceMapArgs.lean | 70 +++++++ .../Proof/Argon2/X86_64/ReferenceMapCT.lean | 30 +++ .../Argon2/X86_64/ReferenceMapFinish.lean | 48 +++++ .../Proof/Argon2/X86_64/ReferenceMapLane.lean | 83 ++++++++ .../Argon2/X86_64/ReferenceMapLaneCT.lean | 115 +++++++++++ .../Proof/Argon2/X86_64/ReferenceMapLit.lean | 10 + .../Argon2/X86_64/ReferenceMapRelative.lean | 55 ++++++ .../Argon2/X86_64/ReferenceMapState.lean | 185 ++++++++++++++++++ .../Argon2/X86_64/ReferenceMapWindow.lean | 52 +++++ .../Argon2/X86_64/ReferenceMapWindowCT.lean | 24 +++ .../Proof/Argon2/X86_64/SegmentSetup.lean | 43 ++++ .../Proof/Argon2/X86_64/SegmentSetupCT.lean | 120 ++++++++++++ .../Argon2/X86_64/SegmentSetupCheck.lean | 49 +++++ .../Argon2/X86_64/SegmentSetupPrepare.lean | 68 +++++++ .../Argon2/X86_64/SegmentSetupReady.lean | 67 +++++++ .../Argon2/X86_64/SegmentSetupReset.lean | 48 +++++ .../Argon2/X86_64/SegmentSetupSteps.lean | 88 +++++++++ .../Argon2/X86_64/SegmentSetupTrace.lean | 100 ++++++++++ 231 files changed, 11383 insertions(+), 2 deletions(-) create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressCache.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressCalls.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressHeader.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressMode.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/BlockAddress.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/ClearBlock.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/DependentWord.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillBlock.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillColumn.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillCompress.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillFinish.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillIteration.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillIterations.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillKernel.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillLanes.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillPointers.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSegment.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSetup.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSlice.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSlices.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FillWrite.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FinalOutput.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FinalReduction.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/Finish.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/FirstLane.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/InitFill.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/Parameters.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/RandomSource.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/ReduceBlock.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/ReduceLane.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/ReduceLanes.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/ReducePointers.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/ReductionInit.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/ReferenceMap.lean create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/SegmentSetup.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/AddressInput.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/FillPositions.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/FillStep.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/FinalReduction.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/Iterations.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/IterationsIndices.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/Lanes.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/LanesIndices.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/Matrix.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/Segment.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/SegmentIndices.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/SegmentStart.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/Serialization.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/Slices.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/SlicesIndices.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCache.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheInvariant.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheMatrix.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheMeta.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheSave.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheSelect.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheSelectCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheState.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheWord.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheWordCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCalls.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsArgs.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsClear.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsLayout.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsMx.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsPrepare.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsStage.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressGeneration.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressGenerationCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeader.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeaderCorrect.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeaderLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeaderWords.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressInputCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressMode.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressModeCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressModeLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressModeSteps.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/BlockAddress.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/BlockAddressLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/BlockStore.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ClearBlock.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ClearBlockLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWord.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordPointer.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordState.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillAllocation.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlock.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlockCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlockCounter.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlockFrame.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCacheInvariant.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillColumn.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillColumnCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillColumnLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompress.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressArgs.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressCall.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressCallCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressOperation.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressOperationCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressOperationMx.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressSetup.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillContext.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillFinish.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillFinishCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillFinishReady.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillHeader.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIndex.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIteration.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationPrepare.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterations.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsBody.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsBodyCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsFrame.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernel.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelArgs.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelInvariant.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelLayout.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelMappingCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelMatrix.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelPrepare.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelSpec.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelStable.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLaneAdvance.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanes.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanesBody.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanesBodyCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanesCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPassCounter.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPassSave.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointers.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersArgs.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersNat.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegment.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegmentBody.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegmentBodyCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegmentCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetup.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupDimensions.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupEnvironment.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupFinish.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupReset.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlice.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSliceAdvance.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSliceCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicePrepare.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlices.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicesBody.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicesBodyCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicesCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWrite.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteCover.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWritePrefix.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteWord.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalCall.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalCallCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutput.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutputArgs.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutputCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutputReady.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalReduction.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FinishReady.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FinishStage.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FinishStageCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FirstLane.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FirstLaneCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/FirstLaneLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFill.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFillCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFillFrames.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFillReady.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/MemoryInitDone.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/MemoryInitRepresent.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/Parameters.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ParametersCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ParametersLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ParametersSteps.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSource.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourceCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourceCounter.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourcePrepare.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourceState.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceBlock.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceBlockCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceBlockLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLane.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLaneCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanes.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanesBody.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanesBodyCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanesCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReducePointers.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReducePointersCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionClear.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionInit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionInitCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionLoopState.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionState.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMap.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapArgs.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapFinish.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapLane.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapLaneCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapRelative.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapState.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapWindow.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapWindowCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetup.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupCheck.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupPrepare.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupReady.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupReset.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupSteps.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupTrace.lean diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressCache.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressCache.lean new file mode 100644 index 000000000..f0d7f3a3d --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressCache.lean @@ -0,0 +1,32 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.AddressCalls + +/-! Cache one address block per 128 segment positions. Frame offset eight holds +its one-based counter; initializing it to zero forces generation even when the +first filled index is two. Only public counters control regeneration. +-/ + +namespace VG.Impl.Argon2.X86_64.AddressCache + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def check : List Instr := [ + .mov .rax (.reg .r15), .shift .shr .rax 7, .alu .add .rax (.imm 1), + .alu .cmp .rax (.mem (at_ .rbp 8))] + +def save : List Instr := [.store (at_ .rbp 8) .rax] + +def select : Prog isa := .seq (.block check) + (.ite .e (.block []) (.seq (.block save) AddressCalls.code)) + +def wordArgs : List Instr := [ + .mov .rcx (.mem (at_ .rbp 248)), .mov .rax (.reg .r15), .alu .and .rax (.imm 127)] + +def wordRead : List Instr := [ + .mov .rdi (.mem { base := .rcx, index := some .rax, scale := 8, disp := 6144 })] + +def word : Prog isa := .seq (.block wordArgs) (.block wordRead) + +def code : Prog isa := .seq select word + +end VG.Impl.Argon2.X86_64.AddressCache diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressCalls.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressCalls.lean new file mode 100644 index 000000000..df34e03ec --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressCalls.lean @@ -0,0 +1,37 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.AddressHeader +import VerifiedGarbage.Impl.Argon2.X86_64.ClearBlock +import VerifiedGarbage.Spec.Argon2.Contract + +/-! Independent-address generation in the shared 16 KiB scratch allocation. +G uses `[0,4096)`, temporary output `[4096,5120)`, input `[5120,6144)`, +address output `[6144,7168)`, and the zero block `[7168,8192)`. Every stage +reloads the scratch pointer from frame offset 248 after a compression call. +-/ + +namespace VG.Impl.Argon2.X86_64.AddressCalls + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def pointer (offset : Nat) : List Instr := [ + .mov .rdi (.mem (at_ .rbp 248)), .alu .add .rdi (.imm (BitVec.ofNat 32 offset))] + +def args (x y out : Nat) : List Instr := [ + .mov .rcx (.mem (at_ .rbp 248)), + .mov .rdi (.reg .rcx), .alu .add .rdi (.imm (BitVec.ofNat 32 x)), + .mov .rsi (.reg .rcx), .alu .add .rsi (.imm (BitVec.ofNat 32 y)), + .mov .rdx (.reg .rcx), .alu .add .rdx (.imm (BitVec.ofNat 32 out))] + +def stage (x y out : Nat) : Prog isa := .seq (.block (args x y out)) + (.call Spec.Argon2.compressApi.name VG.Impl.Argon2.X86_64.compress) + +def calls : Prog isa := .seq (stage 7168 5120 4096) (stage 7168 4096 6144) + +def clearAt (offset : Nat) : Prog isa := .seq (.block (pointer offset)) ClearBlock.code + +def prepare : Prog isa := .seq (clearAt 5120) (.seq (clearAt 7168) + (.seq (.block (pointer 5120)) AddressHeader.code)) + +def code : Prog isa := .seq prepare calls + +end VG.Impl.Argon2.X86_64.AddressCalls diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressHeader.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressHeader.lean new file mode 100644 index 000000000..a05863e8d --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressHeader.lean @@ -0,0 +1,31 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.Compress + +/-! Fill the first seven words of an independently generated address input. +The input pointer is `rdi`; its remaining words were cleared once. The frame +holds pass (0), address counter (8), passes (72), variant (112), blocks (240). +Lane and slice remain in `rbx` and `r14`. The counter is supplied after the +public address-generation loop advances it to its one-based value. +-/ + +namespace VG.Impl.Argon2.X86_64.AddressHeader + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def registerWord (i : Nat) (r : Reg) : List Instr := [.store (at_ .rdi (8 * i)) r] + +def frameWord (i offset : Nat) : List Instr := + [.mov .rax (.mem (at_ .rbp offset)), .store (at_ .rdi (8 * i)) .rax] + +def frameOffset (i : Nat) : Nat := + if i = 0 then 0 else if i = 3 then 240 else if i = 4 then 72 else if i = 5 then 112 else 8 + +def field (i : Nat) : List Instr := + if i = 1 then registerWord i .rbx else if i = 2 then registerWord i .r14 + else frameWord i (frameOffset i) + +def fields (n : Nat) : List Instr := (List.range n).flatMap field + +def code : Prog isa := .block (fields 7) + +end VG.Impl.Argon2.X86_64.AddressHeader diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressMode.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressMode.lean new file mode 100644 index 000000000..b5adabb1a --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/AddressMode.lean @@ -0,0 +1,27 @@ +import VerifiedGarbage.TCB.X86_64.Isa +import VerifiedGarbage.Impl.Argon2.X86_64.Compress + +/-! Determine the segment's address mode from public variant, pass and slice. +The mask in `r10` is one for independent addressing and zero otherwise. +-/ + +namespace VG.Impl.Argon2.X86_64.AddressMode + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def kind : List Instr := [ + .mov .rax (.mem (at_ .rbp 112)), + .mov .r10 (.reg .rax), .alu .xor .r10 (.imm 1), .alu .cmp .r10 (.imm 1), .alu .sbb .r10 (.reg .r10), + .mov .r8 (.reg .rax), .alu .xor .r8 (.imm 2), .alu .cmp .r8 (.imm 1), .alu .sbb .r8 (.reg .r8)] + +def pass : List Instr := [ + .mov .r9 (.mem (at_ .rbp 0)), .alu .cmp .r9 (.imm 1), .alu .sbb .r9 (.reg .r9)] + +def slice : List Instr := [ + .alu .cmp .r14 (.imm 2), .alu .sbb .r11 (.reg .r11), + .alu .and .r8 (.reg .r9), .alu .and .r8 (.reg .r11), .alu .or .r10 (.reg .r8), .alu .and .r10 (.imm 1)] + +def code : Prog isa := .seq (.block kind) (.seq (.block pass) (.block slice)) + +end VG.Impl.Argon2.X86_64.AddressMode diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/BlockAddress.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/BlockAddress.lean new file mode 100644 index 000000000..a259440e0 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/BlockAddress.lean @@ -0,0 +1,20 @@ +import VerifiedGarbage.TCB.X86_64.Isa + +/-! Lane-major matrix addressing. The matrix base is in `r8`, the lane +in `rax`, the column in `rcx`, and the lane length in `r12`. The resulting +block pointer is returned in `rax`. Scalar multiplication and ten doublings +work on the baseline ISA, including when the reference coordinates are secret. +-/ + +namespace VG.Impl.Argon2.X86_64.BlockAddress + +open VG.X86_64 + +def flatten : List Instr := [.mul .r12, .alu .add .rax (.reg .rcx)] + +def scale : List Instr := List.replicate 10 (.alu .add .rax (.reg .rax)) + +def code : Prog isa := + .seq (.block flatten) (.seq (.block scale) (.block [.alu .add .rax (.reg .r8)])) + +end VG.Impl.Argon2.X86_64.BlockAddress diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/ClearBlock.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ClearBlock.lean new file mode 100644 index 000000000..856a8bbde --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ClearBlock.lean @@ -0,0 +1,18 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.Compress + +/-! Clear one 1024-byte address-generation block. The destination in `rdi` +is public; neither the old contents nor any input value affects the trace. +-/ + +namespace VG.Impl.Argon2.X86_64.ClearBlock + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def word (i : Nat) : List Instr := [.store (at_ .rdi (8 * i)) .rax] + +def words (n : Nat) : List Instr := (List.range n).flatMap word + +def code : Prog isa := .seq (.block [.mov .rax (.imm 0)]) (.block (words 128)) + +end VG.Impl.Argon2.X86_64.ClearBlock diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/DependentWord.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/DependentWord.lean new file mode 100644 index 000000000..726cfc16d --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/DependentWord.lean @@ -0,0 +1,21 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillKernel + +/-! Read the previous cell's first word for data-dependent addressing. Only the +public loop position and matrix base determine the read address. +-/ + +namespace VG.Impl.Argon2.X86_64.DependentWord + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def args : List Instr := [.mov .rcx (.reg .rdi), .mov .rax (.reg .rbx)] + +def pointer : Prog isa := .seq (.block FillKernel.matrix) + (.seq FillColumn.code (.seq (.block args) BlockAddress.code)) + +def read : List Instr := [.mov .rdi (.mem (at_ .rax 0))] + +def code : Prog isa := .seq pointer (.block read) + +end VG.Impl.Argon2.X86_64.DependentWord diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillBlock.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillBlock.lean new file mode 100644 index 000000000..159a7fc3f --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillBlock.lean @@ -0,0 +1,12 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.RandomSource +import VerifiedGarbage.Impl.Argon2.X86_64.FillKernel + +/-! Select the random word and update one active matrix cell. -/ + +namespace VG.Impl.Argon2.X86_64.FillBlock + +open VG.X86_64 + +def code : Prog isa := .seq RandomSource.code FillKernel.code + +end VG.Impl.Argon2.X86_64.FillBlock diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillColumn.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillColumn.lean new file mode 100644 index 000000000..0eb460e65 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillColumn.lean @@ -0,0 +1,26 @@ +import VerifiedGarbage.TCB.X86_64.Isa + +/-! Current and preceding columns in the filling loop. The public slice, +segment length and offset are in `r14`, `r13` and `r15`, and the lane length +is in `r12`. `rcx` receives the current column; `rdi` receives its cyclic +predecessor. Only the public column-zero test controls a branch. +-/ + +namespace VG.Impl.Argon2.X86_64.FillColumn + +open VG.X86_64 + +def current : List Instr := [ + .mov .rax (.reg .r14), .mul .r13, .mov .rcx (.reg .rax), + .alu .add .rcx (.reg .r15)] + +def select : Prog isa := .ite .e + (.block [.mov .rdi (.reg .r12)]) (.block [.mov .rdi (.reg .rcx)]) + +def previous : Prog isa := + .seq (.block [.alu .cmp .rcx (.imm 0)]) + (.seq select (.block [.alu .sub .rdi (.imm 1)])) + +def code : Prog isa := .seq (.block current) previous + +end VG.Impl.Argon2.X86_64.FillColumn diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillCompress.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillCompress.lean new file mode 100644 index 000000000..2c3e16236 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillCompress.lean @@ -0,0 +1,33 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillWrite +import VerifiedGarbage.Spec.Argon2.Contract + +/-! Compress the selected previous/reference blocks and update the current +matrix cell. Pointer setup supplied `r10` (current), `rdi` (previous), and +`rsi` (reference). The derivation frame holds the pass at offset zero and +scratch pointer at offset 248; offset 16 retains the destination across G. +The first 4096 scratch bytes belong to G, and its output is at offset 4096. +-/ + +namespace VG.Impl.Argon2.X86_64.FillCompress + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def saveCurrent : List Instr := [.store (at_ .rbp 16) .r10] + +def compressArgs : List Instr := [ + .mov .rcx (.mem (at_ .rbp 248)), .mov .rdx (.reg .rcx), .alu .add .rdx (.imm 4096)] + +def writeArgs : List Instr := [ + .mov .rdi (.mem (at_ .rbp 16)), .mov .rsi (.mem (at_ .rbp 248)), + .alu .add .rsi (.imm 4096), .mov .r9 (.mem (at_ .rbp 0))] + +def operation : Prog isa := + .seq (.call Spec.Argon2.compressApi.name VG.Impl.Argon2.X86_64.compress) + (.seq (.block writeArgs) FillWrite.code) + +def setup : Prog isa := .seq (.block saveCurrent) (.block compressArgs) + +def code : Prog isa := .seq setup operation + +end VG.Impl.Argon2.X86_64.FillCompress diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillFinish.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillFinish.lean new file mode 100644 index 000000000..b7a915d35 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillFinish.lean @@ -0,0 +1,13 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillIterations +import VerifiedGarbage.Impl.Argon2.X86_64.Finish + +/-! Complete all filling passes, reduce the lane endings, and compute the final tag. -/ + +namespace VG.Impl.Argon2.X86_64.FillFinish + +open VG.X86_64 + +def code (name : String) (h : HPrime.Hash) : Prog isa := + .seq FillIterations.loop (Finish.code name h) + +end VG.Impl.Argon2.X86_64.FillFinish diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillIteration.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillIteration.lean new file mode 100644 index 000000000..02397ec81 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillIteration.lean @@ -0,0 +1,13 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillSlices + +/-! Reset the slice coordinate before each filling pass. -/ + +namespace VG.Impl.Argon2.X86_64.FillIteration + +open VG.X86_64 + +def setup : List Instr := [.mov .r14 (.imm 0)] + +def code : Prog isa := .seq (.block setup) FillSlices.loop + +end VG.Impl.Argon2.X86_64.FillIteration diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillIterations.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillIterations.lean new file mode 100644 index 000000000..4b277234d --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillIterations.lean @@ -0,0 +1,20 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillIteration + +/-! Advance the public pass counter stored in the mutable header. -/ + +namespace VG.Impl.Argon2.X86_64.FillIterations + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def increment : List Instr := [.mov .rax (.mem (at_ .rbp 0)), .alu .add .rax (.imm 1)] + +def saveCheck : List Instr := [.store (at_ .rbp 0) .rax, .alu .cmp .rax (.mem (at_ .rbp 72))] + +def advance : Prog isa := .seq (.block increment) (.block saveCheck) + +def body : Prog isa := .seq FillIteration.code advance + +def loop : Prog isa := .loop body .b + +end VG.Impl.Argon2.X86_64.FillIterations diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillKernel.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillKernel.lean new file mode 100644 index 000000000..958ad44d8 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillKernel.lean @@ -0,0 +1,27 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ReferenceMap +import VerifiedGarbage.Impl.Argon2.X86_64.FillPointers +import VerifiedGarbage.Impl.Argon2.X86_64.FillCompress + +/-! Map the random word, prepare matrix pointers, and update one active cell. +The enclosing loops provide the position in callee-saved registers and the +frame; `rdi` contains either the cached independent word or the previous cell's +first word. The lane count and matrix base are reloaded after volatile calls. +-/ + +namespace VG.Impl.Argon2.X86_64.FillKernel + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def lanes : List Instr := [.mov .rsi (.mem (at_ .rbp 184))] +def matrix : List Instr := [.mov .r8 (.mem (at_ .rbp 232))] + +def mapping : Prog isa := .seq (.block lanes) ReferenceMap.code + +def pointers : Prog isa := .seq (.block matrix) FillPointers.code + +def prepare : Prog isa := .seq mapping pointers + +def code : Prog isa := .seq prepare FillCompress.code + +end VG.Impl.Argon2.X86_64.FillKernel diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillLanes.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillLanes.lean new file mode 100644 index 000000000..e16a3cc70 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillLanes.lean @@ -0,0 +1,16 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.SegmentSetup + +/-! Fill one slice's lanes serially, advancing only the public lane coordinate. -/ + +namespace VG.Impl.Argon2.X86_64.FillLanes + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def advance : List Instr := [.alu .add .rbx (.imm 1), .alu .cmp .rbx (.mem (at_ .rbp 184))] + +def body : Prog isa := .seq SegmentSetup.code (.block advance) + +def loop : Prog isa := .loop body .b + +end VG.Impl.Argon2.X86_64.FillLanes diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillPointers.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillPointers.lean new file mode 100644 index 000000000..9fdfcf4a3 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillPointers.lean @@ -0,0 +1,35 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.BlockAddress +import VerifiedGarbage.Impl.Argon2.X86_64.FillColumn + +/-! Prepare the block pointers for one filling operation. `r8` is the matrix +base; `rbx`, `r12`–`r15` retain the loop position. Reference mapping supplied +the reference lane and column in `r9` and `rdi`. The current pointer is saved +in `r10`, with the previous and reference pointers in `rdi` and `rsi`. +-/ + +namespace VG.Impl.Argon2.X86_64.FillPointers + +open VG.X86_64 + +def saveReference : List Instr := [.mov .rsi (.reg .rdi)] + +def currentArgs : List Instr := [.mov .rax (.reg .rbx)] + +def previousArgs : List Instr := [ + .mov .r10 (.reg .rax), .mov .rcx (.reg .rdi), .mov .rax (.reg .rbx)] + +def referenceArgs : List Instr := [ + .mov .r11 (.reg .rax), .mov .rcx (.reg .rsi), .mov .rax (.reg .r9)] + +def finishArgs : List Instr := [.mov .rsi (.reg .rax), .mov .rdi (.reg .r11)] + +def current : Prog isa := .seq (.block currentArgs) BlockAddress.code + +def previous : Prog isa := .seq (.block previousArgs) BlockAddress.code + +def reference : Prog isa := .seq (.block referenceArgs) BlockAddress.code + +def code : Prog isa := .seq (.block saveReference) (.seq FillColumn.code + (.seq current (.seq previous (.seq reference (.block finishArgs))))) + +end VG.Impl.Argon2.X86_64.FillPointers diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSegment.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSegment.lean new file mode 100644 index 000000000..ad175b09f --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSegment.lean @@ -0,0 +1,15 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillBlock + +/-! Advance the public index after each active cell, stopping at the segment length. -/ + +namespace VG.Impl.Argon2.X86_64.FillSegment + +open VG.X86_64 + +def advance : List Instr := [.alu .add .r15 (.imm 1), .alu .cmp .r15 (.reg .r13)] + +def body : Prog isa := .seq FillBlock.code (.block advance) + +def loop : Prog isa := .loop body .b + +end VG.Impl.Argon2.X86_64.FillSegment diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSetup.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSetup.lean new file mode 100644 index 000000000..b709d4114 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSetup.lean @@ -0,0 +1,18 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.Compress + +/-! Convert initialization's byte stride to filling dimensions and reset the public pass. -/ + +namespace VG.Impl.Argon2.X86_64.FillSetup + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def dimensions : List Instr := + [.mov .r12 (.reg .r13), .shift .shr .r12 10, .shift .shr .r13 12] + +def reset : List Instr := + [.mov .rax (.imm 0), .store (at_ .rbp 0) .rax, .mov .rbx (.imm 0), .mov .r14 (.imm 0)] + +def code : Prog isa := .seq (.block dimensions) (.block reset) + +end VG.Impl.Argon2.X86_64.FillSetup diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSlice.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSlice.lean new file mode 100644 index 000000000..1399c363e --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSlice.lean @@ -0,0 +1,13 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillLanes + +/-! Reset the lane coordinate and fill every lane of one slice. -/ + +namespace VG.Impl.Argon2.X86_64.FillSlice + +open VG.X86_64 + +def setup : List Instr := [.mov .rbx (.imm 0)] + +def code : Prog isa := .seq (.block setup) FillLanes.loop + +end VG.Impl.Argon2.X86_64.FillSlice diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSlices.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSlices.lean new file mode 100644 index 000000000..ce56d0f32 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillSlices.lean @@ -0,0 +1,15 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillSlice + +/-! Fill a pass's four slices in order, using the public slice coordinate. -/ + +namespace VG.Impl.Argon2.X86_64.FillSlices + +open VG.X86_64 + +def advance : List Instr := [.alu .add .r14 (.imm 1), .alu .cmp .r14 (.imm 4)] + +def body : Prog isa := .seq FillSlice.code (.block advance) + +def loop : Prog isa := .loop body .b + +end VG.Impl.Argon2.X86_64.FillSlices diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillWrite.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillWrite.lean new file mode 100644 index 000000000..f6d62956d --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FillWrite.lean @@ -0,0 +1,24 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.Compress + +/-! Write the compression result into the current matrix block. `rsi` points +to the temporary result and `rdi` to the matrix destination; `r9` is the public +pass number. Pass zero copies the result, and later passes XOR the old cell. +Both paths visit every word in ascending order. +-/ + +namespace VG.Impl.Argon2.X86_64.FillWrite + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def word (xorOld : Bool) (i : Nat) : List Instr := + [.mov .rax (.mem (at_ .rsi (8 * i)))] ++ + (if xorOld then [.alu .xor .rax (.mem (at_ .rdi (8 * i)))] else []) ++ + [.store (at_ .rdi (8 * i)) .rax] + +def words (xorOld : Bool) (n : Nat) : List Instr := (List.range n).flatMap (word xorOld) + +def code : Prog isa := .seq (.block [.alu .cmp .r9 (.imm 0)]) + (.ite .e (.block (words false 128)) (.block (words true 128))) + +end VG.Impl.Argon2.X86_64.FillWrite diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FinalOutput.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FinalOutput.lean new file mode 100644 index 000000000..d1896b47a --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FinalOutput.lean @@ -0,0 +1,19 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.HPrime +import VerifiedGarbage.Impl.Argon2.X86_64.Compress + +/-! Final H′: matrix block zero is the input, using the derivation's hash backend. -/ + +namespace VG.Impl.Argon2.X86_64.FinalOutput + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def args : List Instr := + [.mov .rdi (.mem (at_ .rbp 232)), .mov .rsi (.imm 1024), + .mov .rdx (.mem (at_ .rbp 256)), .mov .rcx (.mem (at_ .rbp 264)), + .mov .r8 (.mem (at_ .rbp 248))] + +def code (name : String) (h : HPrime.Hash) : Prog isa := + .seq (.block args) (.call name (HPrime.code h)) + +end VG.Impl.Argon2.X86_64.FinalOutput diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FinalReduction.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FinalReduction.lean new file mode 100644 index 000000000..162bf000b --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FinalReduction.lean @@ -0,0 +1,12 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ReductionInit +import VerifiedGarbage.Impl.Argon2.X86_64.ReduceLanes + +/-! Reduce all lane endings into matrix block zero for the final H′ call. -/ + +namespace VG.Impl.Argon2.X86_64.FinalReduction + +open VG.X86_64 + +def code : Prog isa := .seq ReductionInit.code ReduceLanes.loop + +end VG.Impl.Argon2.X86_64.FinalReduction diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/Finish.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/Finish.lean new file mode 100644 index 000000000..19b08e246 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/Finish.lean @@ -0,0 +1,13 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FinalReduction +import VerifiedGarbage.Impl.Argon2.X86_64.FinalOutput + +/-! The complete final reduction and H′, parameterized by the hash backend. -/ + +namespace VG.Impl.Argon2.X86_64.Finish + +open VG.X86_64 + +def code (name : String) (h : HPrime.Hash) : Prog isa := + .seq FinalReduction.code (FinalOutput.code name h) + +end VG.Impl.Argon2.X86_64.Finish diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/FirstLane.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FirstLane.lean new file mode 100644 index 000000000..3cad23882 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/FirstLane.lean @@ -0,0 +1,20 @@ +import VerifiedGarbage.TCB.X86_64.Isa + +/-! Force the current lane on the first slice of the first pass. + +The pass and slice are public in `r9` and `r14`. The current lane is in +`rbx`; `r8` initially contains J₂ modulo the lane count. Only the public +position controls a branch. +-/ + +namespace VG.Impl.Argon2.X86_64.FirstLane + +open VG.X86_64 + +def test : List Instr := [.mov .rax (.reg .r9), .alu .or .rax (.reg .r14)] + +def current : List Instr := [.mov .r8 (.reg .rbx)] + +def code : Prog isa := .seq (.block test) (.ite .e (.block current) (.block [])) + +end VG.Impl.Argon2.X86_64.FirstLane diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/InitFill.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/InitFill.lean new file mode 100644 index 000000000..ef47ef865 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/InitFill.lean @@ -0,0 +1,14 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.MemoryInit +import VerifiedGarbage.Impl.Argon2.X86_64.FillSetup +import VerifiedGarbage.Impl.Argon2.X86_64.FillFinish + +/-! All memory initialization, filling and finalization after H₀ has been computed. -/ + +namespace VG.Impl.Argon2.X86_64.InitFill + +open VG.X86_64 + +def code (name : String) (h : HPrime.Hash) : Prog isa := + .seq (MemoryInit.code name h) (.seq FillSetup.code (FillFinish.code name h)) + +end VG.Impl.Argon2.X86_64.InitFill diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/Parameters.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/Parameters.lean new file mode 100644 index 000000000..47893b7b1 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/Parameters.lean @@ -0,0 +1,19 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.Divide +import VerifiedGarbage.Impl.Argon2.X86_64.Compress + +/-! Compute the rounded lane length from the normalized memory cost and lane count. -/ + +namespace VG.Impl.Argon2.X86_64.Parameters + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def args : List Instr := + [.mov .rdi (.mem (at_ .rbp 176)), .mov .rsi (.mem (at_ .rbp 184)), + .alu .add .rsi (.reg .rsi), .alu .add .rsi (.reg .rsi)] + +def finish : List Instr := [.mov .r13 (.reg .r9), .alu .add .r13 (.reg .r13), .alu .add .r13 (.reg .r13)] + +def code : Prog isa := .seq (.block args) (.seq Divide.code (.block finish)) + +end VG.Impl.Argon2.X86_64.Parameters diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/RandomSource.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/RandomSource.lean new file mode 100644 index 000000000..e4ccc67f0 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/RandomSource.lean @@ -0,0 +1,17 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.AddressMode +import VerifiedGarbage.Impl.Argon2.X86_64.AddressCache +import VerifiedGarbage.Impl.Argon2.X86_64.DependentWord + +/-! Dispatch the filling random word using the public segment addressing mode. -/ + +namespace VG.Impl.Argon2.X86_64.RandomSource + +open VG.X86_64 + +def test : List Instr := [.alu .cmp .r10 (.imm 0)] + +def prepare : Prog isa := .seq AddressMode.code (.block test) + +def code : Prog isa := .seq prepare (.ite .e DependentWord.code AddressCache.code) + +end VG.Impl.Argon2.X86_64.RandomSource diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReduceBlock.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReduceBlock.lean new file mode 100644 index 000000000..1fb7532e9 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReduceBlock.lean @@ -0,0 +1,11 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillWrite + +/-! XOR a last-lane block at `rsi` into the accumulator at `rdi`. -/ + +namespace VG.Impl.Argon2.X86_64.ReduceBlock + +open VG.X86_64 + +def code : Prog isa := .block (FillWrite.words true 128) + +end VG.Impl.Argon2.X86_64.ReduceBlock diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReduceLane.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReduceLane.lean new file mode 100644 index 000000000..11f6371ff --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReduceLane.lean @@ -0,0 +1,12 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ReducePointers +import VerifiedGarbage.Impl.Argon2.X86_64.ReduceBlock + +/-! Accumulate one lane's last block into matrix block zero. -/ + +namespace VG.Impl.Argon2.X86_64.ReduceLane + +open VG.X86_64 + +def code : Prog isa := .seq ReducePointers.code ReduceBlock.code + +end VG.Impl.Argon2.X86_64.ReduceLane diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReduceLanes.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReduceLanes.lean new file mode 100644 index 000000000..e1abc56ce --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReduceLanes.lean @@ -0,0 +1,16 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ReduceLane +import VerifiedGarbage.Impl.Argon2.X86_64.FillLanes + +/-! Visit each public lane once to reduce its last block. -/ + +namespace VG.Impl.Argon2.X86_64.ReduceLanes + +open VG.X86_64 + +def advance : List Instr := FillLanes.advance + +def body : Prog isa := .seq ReduceLane.code (.block advance) + +def loop : Prog isa := .loop body .b + +end VG.Impl.Argon2.X86_64.ReduceLanes diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReducePointers.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReducePointers.lean new file mode 100644 index 000000000..e86b85988 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReducePointers.lean @@ -0,0 +1,19 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.BlockAddress +import VerifiedGarbage.Impl.Argon2.X86_64.Compress + +/-! Select block zero and the last block of the current public lane. -/ + +namespace VG.Impl.Argon2.X86_64.ReducePointers + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def setup : List Instr := + [.mov .r8 (.mem (at_ .rbp 232)), .mov .rax (.reg .rbx), + .mov .rcx (.reg .r12), .alu .sub .rcx (.imm 1)] + +def finish : List Instr := [.mov .rsi (.reg .rax), .mov .rdi (.reg .r8)] + +def code : Prog isa := .seq (.block setup) (.seq BlockAddress.code (.block finish)) + +end VG.Impl.Argon2.X86_64.ReducePointers diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReductionInit.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReductionInit.lean new file mode 100644 index 000000000..257454699 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReductionInit.lean @@ -0,0 +1,15 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ClearBlock +import VerifiedGarbage.Impl.Argon2.X86_64.Compress + +/-! Begin the final reduction at lane zero with a zero accumulator in matrix block zero. -/ + +namespace VG.Impl.Argon2.X86_64.ReductionInit + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def setup : List Instr := [.mov .rdi (.mem (at_ .rbp 232)), .mov .rbx (.imm 0)] + +def code : Prog isa := .seq (.block setup) ClearBlock.code + +end VG.Impl.Argon2.X86_64.ReductionInit diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReferenceMap.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReferenceMap.lean new file mode 100644 index 000000000..512a4e087 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/ReferenceMap.lean @@ -0,0 +1,44 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ReferenceLane +import VerifiedGarbage.Impl.Argon2.X86_64.FirstLane +import VerifiedGarbage.Impl.Argon2.X86_64.ReferenceStart +import VerifiedGarbage.Impl.Argon2.X86_64.ReferenceCount +import VerifiedGarbage.Impl.Argon2.X86_64.Relative +import VerifiedGarbage.Impl.Argon2.X86_64.Wrap + +/-! Complete mapping of J₁ and J₂ to a reference lane and column. + +`rdi` contains the random word and `rsi` the lane count. The current +lane is in `rbx`, lane and segment lengths in `r12` and `r13`, slice and +index in `r14` and `r15`. The pass counter is at the frame base `rbp`: +H₀'s first word is reused after memory initialization. `r9` and `rdi` +receive the reference lane and column. The input word is retained in `r11`. +-/ + +namespace VG.Impl.Argon2.X86_64.ReferenceMap + +open VG.X86_64 + +def loadPass : List Instr := [.mov .r9 (.mem { base := .rbp })] + +def laneArgs : List Instr := [.mov .rdi (.reg .r8), .mov .rsi (.reg .rbx)] + +def relativeArgs : List Instr := [ + .mov .r9 (.reg .rdi), .mov .rdi (.reg .r11), .mov .rsi (.reg .r8)] + +def wrapArgs : List Instr := [ + .mov .rdi (.reg .rax), .alu .add .rdi (.reg .r10), .mov .rsi (.reg .r12)] + +def chooseLane : Prog isa := + .seq ReferenceLane.code (.seq (.block loadPass) FirstLane.code) + +def prepareLanes : Prog isa := .seq chooseLane (.block laneArgs) + +def window : Prog isa := .seq ReferenceStart.code ReferenceCount.code + +def relative : Prog isa := .seq (.block relativeArgs) Relative.code + +def finish : Prog isa := .seq (.block wrapArgs) Wrap.code + +def code : Prog isa := .seq prepareLanes (.seq window (.seq relative finish)) + +end VG.Impl.Argon2.X86_64.ReferenceMap diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/SegmentSetup.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/SegmentSetup.lean new file mode 100644 index 000000000..365bc7360 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/SegmentSetup.lean @@ -0,0 +1,25 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillSegment +import VerifiedGarbage.Impl.Argon2.X86_64.AddressCache + +/-! Reset the address cache per segment and skip the two initialized cells. -/ + +namespace VG.Impl.Argon2.X86_64.SegmentSetup + +open VG.X86_64 +open VG.Impl.Argon2.X86_64 (at_) + +def reset : Prog isa := .seq (.block [.mov .rax (.imm 0)]) (.block AddressCache.save) + +def first : List Instr := [ + .mov .rcx (.mem (at_ .rbp 0)), .alu .or .rcx (.reg .r14), .alu .cmp .rcx (.imm 0)] + +def index : Prog isa := .seq (.block first) + (.ite .e (.block [.mov .r15 (.imm 2)]) (.block [.mov .r15 (.imm 0)])) + +def check : List Instr := [.alu .cmp .r15 (.reg .r13)] + +def prepare : Prog isa := .seq reset index + +def code : Prog isa := .seq prepare (.seq (.block check) (.ite .b FillSegment.loop (.block []))) + +end VG.Impl.Argon2.X86_64.SegmentSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/AddressInput.lean b/lean/VerifiedGarbage/Proof/Argon2/AddressInput.lean new file mode 100644 index 000000000..ef01d17f1 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/AddressInput.lean @@ -0,0 +1,19 @@ +import VerifiedGarbage.Spec.Argon2 + +/-! The input block of the reviewed independent-address specification. -/ + +namespace VG.Proof.Argon2 + +open VG.Spec.Argon2 + +def addressInput (p : Params) (pass lane slice counter : Nat) : Block := + zeroBlock |>.set 0 (BitVec.ofNat 64 pass) |>.set 1 (BitVec.ofNat 64 lane) + |>.set 2 (BitVec.ofNat 64 slice) |>.set 3 (BitVec.ofNat 64 p.blocks) + |>.set 4 (BitVec.ofNat 64 p.passes) |>.set 5 (BitVec.ofNat 64 p.variant.code) + |>.set 6 (BitVec.ofNat 64 counter) + +theorem addressBlock_eq (p : Params) (pass lane slice counter : Nat) : + addressBlock p pass lane slice counter = + compress zeroBlock (compress zeroBlock (addressInput p pass lane slice counter)) := rfl + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/FillPositions.lean b/lean/VerifiedGarbage/Proof/Argon2/FillPositions.lean new file mode 100644 index 000000000..850534bf1 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/FillPositions.lean @@ -0,0 +1,55 @@ +import VerifiedGarbage.Proof.Argon2.Dimensions +import VerifiedGarbage.Proof.Framework.Offset + +/-! Bounds for every block address used by the filling loop. -/ + +namespace VG.Proof.Argon2 + +open VG.Spec.Argon2 + +theorem previous_column_lt (p : Params) (hl : 0 < p.lanes) + (hm : 8 * p.lanes ≤ p.memory) (column : Nat) : + (column + p.laneLen - 1) % p.laneLen < p.laneLen := by + have seg := segmentLen_ge_two p hl hm + have lanes := laneLen_segments p hl + exact Nat.mod_lt _ (by omega) + +theorem cell_bytes (p : Params) (hl : 0 < p.lanes) {lane column : Nat} + (hlane : lane < p.lanes) (hcolumn : column < p.laneLen) : + (lane * p.laneLen + column) * 1024 + 1024 ≤ p.blocks * 1024 := by + have cell := cell_lt p hl hlane hcolumn + have scaled := Nat.mul_le_mul_right 1024 (show lane * p.laneLen + column + 1 ≤ p.blocks by omega) + simpa only [Nat.add_mul, Nat.one_mul] using scaled + +theorem current_cell_lt (p : Params) (hl : 0 < p.lanes) {lane slice index : Nat} + (hlane : lane < p.lanes) (hslice : slice < 4) (hindex : index < p.segmentLen) : + lane * p.laneLen + (slice * p.segmentLen + index) < p.blocks := + cell_lt p hl hlane (column_lt p hl hslice hindex) + +theorem previous_cell_lt (p : Params) (hl : 0 < p.lanes) + (hm : 8 * p.lanes ≤ p.memory) {lane column : Nat} (hlane : lane < p.lanes) : + lane * p.laneLen + ((column + p.laneLen - 1) % p.laneLen) < p.blocks := + cell_lt p hl hlane (previous_column_lt p hl hm column) + +theorem reference_cell_lt (p : Params) (hl : 0 < p.lanes) + (hm : 8 * p.lanes ≤ p.memory) (pass lane slice index : Nat) (random : Word) + (hlane : lane < p.lanes) : + let ref := reference p pass lane slice index random + ref.1 * p.laneLen + ref.2 < p.blocks := by + obtain ⟨laneBound, columnBound⟩ := reference_bounds p hl hm pass lane slice index random hlane + exact cell_lt p hl laneBound columnBound + +theorem cell_contains (base : VG.Addr) (p : Params) (hl : 0 < p.lanes) + (hm : p.memory < 2 ^ 32) {lane column : Nat} + (hlane : lane < p.lanes) (hcolumn : column < p.laneLen) : + (⟨base, p.blocks * 1024⟩ : VG.Region).Contains + (base + BitVec.ofNat 64 ((lane * p.laneLen + column) * 1024)) 1024 := by + have bytes := cell_bytes p hl hlane hcolumn + have blocks : p.blocks < 2 ^ 32 := Nat.lt_of_le_of_lt (blocks_le_memory p) hm + have total : p.blocks * 1024 < 2 ^ 64 := + Nat.lt_trans (Nat.mul_lt_mul_of_pos_right blocks (by decide : 0 < 1024)) (by decide +kernel) + exact VG.Offset.contains_base base + (d := (lane * p.laneLen + column) * 1024) (n := 1024) (k := p.blocks * 1024) + bytes (Nat.lt_of_le_of_lt (Nat.le_trans (Nat.le_add_right _ _) bytes) total) + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/FillStep.lean b/lean/VerifiedGarbage/Proof/Argon2/FillStep.lean new file mode 100644 index 000000000..cf2b7137c --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/FillStep.lean @@ -0,0 +1,41 @@ +import VerifiedGarbage.Spec.Argon2 + +/-! Expose the reviewed filling step's random word, matrix update and leakage log. -/ + +namespace VG.Proof.Argon2.FillStep + +open VG.Spec.Argon2 + +def random (p : Params) (pass lane slice index : Nat) (blocks : Array Block) : Word := + if independent p pass slice then + (addressBlock p pass lane slice (index / 128 + 1))[index % 128]'(Nat.mod_lt _ (by decide)) + else + (blocks[lane * p.laneLen + (slice * p.segmentLen + index + p.laneLen - 1) % p.laneLen]?.getD zeroBlock)[0] + +def update (p : Params) (pass lane slice index : Nat) (blocks : Array Block) (word : Word) : Array Block := + let column := slice * p.segmentLen + index + let current := lane * p.laneLen + column + let prev := blocks[lane * p.laneLen + (column + p.laneLen - 1) % p.laneLen]?.getD zeroBlock + let ref := reference p pass lane slice index word + let other := blocks[ref.1 * p.laneLen + ref.2]?.getD zeroBlock + let next := compress prev other + blocks.set! current (if pass = 0 then next else xorBlock next (blocks[current]?.getD zeroBlock)) + +theorem not_skipped (pass slice index : Nat) (active : pass ≠ 0 ∨ slice ≠ 0 ∨ 2 ≤ index) : + ¬(pass = 0 ∧ slice = 0 ∧ index < 2) := by omega + +theorem memory (p : Params) (pass lane slice index : Nat) (s : FillState) + (active : pass ≠ 0 ∨ slice ≠ 0 ∨ 2 ≤ index) : + (fillBlock p pass slice lane index s).memory = + update p pass lane slice index s.memory (random p pass lane slice index s.memory) := by + rw [fillBlock, ite_eq_right (not_skipped pass slice index active)] + rfl + +theorem indices (p : Params) (pass lane slice index : Nat) (s : FillState) + (active : pass ≠ 0 ∨ slice ≠ 0 ∨ 2 ≤ index) : + (fillBlock p pass slice lane index s).indices = if independent p pass slice then s.indices + else reference p pass lane slice index (random p pass lane slice index s.memory) :: s.indices := by + rw [fillBlock, ite_eq_right (not_skipped pass slice index active)] + rfl + +end VG.Proof.Argon2.FillStep diff --git a/lean/VerifiedGarbage/Proof/Argon2/FinalReduction.lean b/lean/VerifiedGarbage/Proof/Argon2/FinalReduction.lean new file mode 100644 index 000000000..aee7f3a1a --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/FinalReduction.lean @@ -0,0 +1,45 @@ +import VerifiedGarbage.Proof.Argon2.Matrix +import VerifiedGarbage.Proof.Argon2.Dimensions + +/-! The final lane reduction, without changing the reviewed finish specification. -/ + +namespace VG.Proof.Argon2 + +open VG VG.Spec.Argon2 + +def lastIndex (p : Params) (lane : Nat) : Nat := (lane + 1) * p.laneLen - 1 + +def reduction (p : Params) (memory : Array Block) (start count : Nat) (acc : Block) : Block := + (List.range' start count).foldl (fun b lane => xorBlock b (memory[lastIndex p lane]?.getD zeroBlock)) acc + +theorem reduction_zero (p : Params) (memory : Array Block) (start : Nat) (acc : Block) : + reduction p memory start 0 acc = acc := rfl + +theorem reduction_succ (p : Params) (memory : Array Block) (start count : Nat) (acc : Block) : + reduction p memory start (count + 1) acc = + reduction p memory (start + 1) count (xorBlock acc (memory[lastIndex p start]?.getD zeroBlock)) := by + simp only [reduction, List.range'_succ, List.foldl_cons] + +theorem finish_reduction (p : Params) (memory : Array Block) : + finish p memory = hPrime p.tagLen (serialize (reduction p memory 0 p.lanes zeroBlock)) := by + rw [finish, reduction, List.range_eq_range'] + rfl + +theorem lastIndex_bounds (p : Params) (positive : 0 < p.lanes) (minimum : 2 ≤ p.segmentLen) + (lane : Nat) (active : lane < p.lanes) : 0 < lastIndex p lane ∧ lastIndex p lane < p.blocks := by + have q : 8 ≤ p.laneLen := by + have eq := laneLen_segments p positive + omega + have total := blocks_lanes p positive + have product : (lane + 1) * p.laneLen ≤ p.lanes * p.laneLen := Nat.mul_le_mul_right _ (by omega) + have low : p.laneLen ≤ (lane + 1) * p.laneLen := by + simpa only [Nat.one_mul] using Nat.mul_le_mul_right p.laneLen (show 1 ≤ lane + 1 by omega) + unfold lastIndex + omega + +theorem xorBlock_comm (a b : Block) : xorBlock a b = xorBlock b a := by + apply Vector.ext + intro i hi + simp only [xorBlock, Vector.getElem_zipWith, BitVec.xor_comm] + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/Iterations.lean b/lean/VerifiedGarbage/Proof/Argon2/Iterations.lean new file mode 100644 index 000000000..2a72cbb32 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/Iterations.lean @@ -0,0 +1,24 @@ +import VerifiedGarbage.Spec.Argon2 + +/-! Pass folds used by the outer filling-loop invariant. -/ + +namespace VG.Proof.Argon2 + +open VG.Spec.Argon2 + +def iterations (p : Params) (start count : Nat) (state : FillState) : FillState := + (List.range' start count).foldl (fillPass p) state + +theorem iterations_zero (p : Params) (start : Nat) (state : FillState) : iterations p start 0 state = state := rfl + +theorem iterations_succ (p : Params) (start count : Nat) (state : FillState) : + iterations p start (count + 1) state = iterations p (start + 1) count (fillPass p state start) := by + unfold iterations + rw [List.range'_succ, List.foldl_cons] + +theorem iterations_fill (p : Params) (password salt secret ad : List Byte) : + iterations p 0 p.passes (initMemory p (initialHash p password salt secret ad)) = fill p password salt secret ad := by + unfold iterations fill + rw [List.range_eq_range'] + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/IterationsIndices.lean b/lean/VerifiedGarbage/Proof/Argon2/IterationsIndices.lean new file mode 100644 index 000000000..1957da1a9 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/IterationsIndices.lean @@ -0,0 +1,41 @@ +import VerifiedGarbage.Proof.Argon2.Iterations +import VerifiedGarbage.Proof.Argon2.SlicesIndices + +/-! Recover each pass's reviewed reference log from the complete filling log. -/ + +namespace VG.Proof.Argon2 + +open VG.Spec.Argon2 + +def passReferences (p : Params) (pass : Nat) : Nat := slicesReferences p pass 0 4 + +def iterationsReferences (p : Params) (start : Nat) : Nat → Nat + | 0 => 0 + | n + 1 => passReferences p start + iterationsReferences p (start + 1) n + +theorem pass_indices_drop (p : Params) (pass : Nat) (state : FillState) (minimum : 2 ≤ p.segmentLen) : + (fillPass p state pass).indices.drop (passReferences p pass) = state.indices := by + rw [← slices_pass p pass state] + exact slices_indices_drop p pass 0 4 state minimum + +theorem iterations_indices_drop (p : Params) (start count : Nat) (state : FillState) (minimum : 2 ≤ p.segmentLen) : + (iterations p start count state).indices.drop (iterationsReferences p start count) = state.indices := by + induction count generalizing start state with + | zero => rfl + | succ n ih => + rw [iterations_succ, iterationsReferences, + show passReferences p start + iterationsReferences p (start + 1) n = + iterationsReferences p (start + 1) n + passReferences p start from Nat.add_comm _ _, + ← List.drop_drop, ih, pass_indices_drop p start state minimum] + +theorem iterations_first_pass (p : Params) (start count : Nat) (leftState rightState : FillState) + (minimum : 2 ≤ p.segmentLen) + (indices : (iterations p start (count + 1) leftState).indices = + (iterations p start (count + 1) rightState).indices) : + (fillPass p leftState start).indices = (fillPass p rightState start).indices := by + have dropped := congrArg (List.drop (iterationsReferences p (start + 1) count)) indices + rw [iterations_succ, iterations_succ, iterations_indices_drop p (start + 1) count _ minimum, + iterations_indices_drop p (start + 1) count _ minimum] at dropped + exact dropped + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/Lanes.lean b/lean/VerifiedGarbage/Proof/Argon2/Lanes.lean new file mode 100644 index 000000000..92ff0eb1b --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/Lanes.lean @@ -0,0 +1,21 @@ +import VerifiedGarbage.Proof.Argon2.Segment + +/-! Lane folds used by the public filling loops. -/ + +namespace VG.Proof.Argon2 + +open VG.Spec.Argon2 + +def lanes (p : Params) (pass slice start count : Nat) (state : FillState) : FillState := + (List.range' start count).foldl (fun state lane => segment p pass lane slice 0 p.segmentLen state) state + +theorem lanes_zero (p : Params) (pass slice start : Nat) (state : FillState) : + lanes p pass slice start 0 state = state := rfl + +theorem lanes_succ (p : Params) (pass slice start count : Nat) (state : FillState) : + lanes p pass slice start (count + 1) state = + lanes p pass slice (start + 1) count (segment p pass start slice 0 p.segmentLen state) := by + unfold lanes + rw [List.range'_succ, List.foldl_cons] + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/LanesIndices.lean b/lean/VerifiedGarbage/Proof/Argon2/LanesIndices.lean new file mode 100644 index 000000000..f5f9d2070 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/LanesIndices.lean @@ -0,0 +1,61 @@ +import VerifiedGarbage.Proof.Argon2.Lanes +import VerifiedGarbage.Proof.Argon2.SegmentIndices +import VerifiedGarbage.Proof.Argon2.SegmentStart + +/-! Recover each segment's reference log from the complete lane fold. -/ + +namespace VG.Proof.Argon2 + +open VG.Spec.Argon2 + +def segmentReferences (p : Params) (pass slice : Nat) : Nat := + if independent p pass slice then 0 else p.segmentLen - segmentStart pass slice + +theorem fillBlock_independent_indices (p : Params) (pass lane slice index : Nat) (state : FillState) + (mode : independent p pass slice = true) : (fillBlock p pass slice lane index state).indices = state.indices := by + unfold fillBlock + split + · rfl + · simp only [mode, ite_true] + +theorem segment_independent_indices (p : Params) (pass lane slice start count : Nat) (state : FillState) + (mode : independent p pass slice = true) : (segment p pass lane slice start count state).indices = state.indices := by + induction count generalizing start state with + | zero => rfl + | succ n ih => + rw [segment_succ, ih, fillBlock_independent_indices p pass lane slice start state mode] + +theorem segment_references_drop (p : Params) (pass lane slice : Nat) (state : FillState) + (minimum : 2 ≤ p.segmentLen) : + (segment p pass lane slice 0 p.segmentLen state).indices.drop (segmentReferences p pass slice) = state.indices := by + cases mode : independent p pass slice + · rw [segment_start p pass lane slice state minimum] + change (segment p pass lane slice (segmentStart pass slice) (p.segmentLen - segmentStart pass slice) state).indices.drop + (if independent p pass slice then 0 else p.segmentLen - segmentStart pass slice) = state.indices + simp only [mode, Bool.false_eq_true, ite_false] + apply segment_indices_drop _ _ _ _ _ _ _ _ mode + unfold segmentStart; split <;> omega + · rw [segment_independent_indices p pass lane slice 0 p.segmentLen state mode] + simp only [segmentReferences, mode, ite_true, List.drop_zero] + +theorem lanes_indices_drop (p : Params) (pass slice start count : Nat) (state : FillState) + (minimum : 2 ≤ p.segmentLen) : + (lanes p pass slice start count state).indices.drop (count * segmentReferences p pass slice) = state.indices := by + induction count generalizing start state with + | zero => rw [Nat.zero_mul, lanes_zero, List.drop_zero] + | succ n ih => + rw [lanes_succ, Nat.add_mul, Nat.one_mul, ← List.drop_drop, + ih, segment_references_drop p pass start slice state minimum] + +theorem lanes_first_segment (p : Params) (pass slice start count : Nat) (leftState rightState : FillState) + (minimum : 2 ≤ p.segmentLen) + (indices : (lanes p pass slice start (count + 1) leftState).indices = + (lanes p pass slice start (count + 1) rightState).indices) : + (segment p pass start slice 0 p.segmentLen leftState).indices = + (segment p pass start slice 0 p.segmentLen rightState).indices := by + have dropped := congrArg (List.drop (count * segmentReferences p pass slice)) indices + rw [lanes_succ, lanes_succ, lanes_indices_drop p pass slice (start + 1) count _ minimum, + lanes_indices_drop p pass slice (start + 1) count _ minimum] at dropped + exact dropped + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/Matrix.lean b/lean/VerifiedGarbage/Proof/Argon2/Matrix.lean new file mode 100644 index 000000000..d2a7daea1 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/Matrix.lean @@ -0,0 +1,39 @@ +import VerifiedGarbage.Spec.Argon2.Contract +import VerifiedGarbage.Proof.Framework.Offset + +/-! Relate the lane-major assembly allocation to the specification's block array. -/ + +namespace VG.Proof.Argon2 + +open VG VG.Spec.Argon2 + +def matrixCell (base : Addr) (k : Nat) : Addr := base + BitVec.ofNat 64 (k * 1024) + +structure Represents (m : Mem) (base : Addr) (n : Nat) (blocks : Array Block) : Prop where + size : blocks.size = n + block : ∀ k < n, blockAt m (matrixCell base k) = blocks[k]?.getD zeroBlock + +theorem Represents.update {m m' : Mem} {base : Addr} {n : Nat} {blocks : Array Block} + (h : Represents m base n blocks) (k : Nat) (hk : k < n) (value : Block) + (written : blockAt m' (matrixCell base k) = value) + (kept : ∀ j < n, j ≠ k → blockAt m' (matrixCell base j) = blockAt m (matrixCell base j)) : + Represents m' base n (blocks.set! k value) := by + refine ⟨(Array.size_set! _ _ _).trans h.size, ?_⟩ + intro j hj + rw [Array.set!_eq_setIfInBounds, Array.getElem?_setIfInBounds] + by_cases equal : k = j + · rw [ite_eq_left equal, ite_eq_left (by rw [h.size]; exact hk), Option.getD_some] + rw [← equal]; exact written + · rw [ite_eq_right equal] + exact (kept j hj (Ne.symm equal)).trans (h.block j hj) + +theorem matrixCell_sub (base : Addr) (n k : Nat) (hk : k < n) : + Region.Sub ⟨matrixCell base k, 1024⟩ ⟨base, n * 1024⟩ := + Offset.sub_base base (by omega) + +theorem matrixCell_disjoint (base : Addr) (n i j : Nat) (bound : n * 1024 < 2 ^ 64) + (hi : i < n) (hj : j < n) (different : i ≠ j) : + (⟨matrixCell base i, 1024⟩ : Region).Disjoint ⟨matrixCell base j, 1024⟩ := + Offset.disjoint base (by omega) (by omega) (by omega) + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/Segment.lean b/lean/VerifiedGarbage/Proof/Argon2/Segment.lean new file mode 100644 index 000000000..5cbdfa8e4 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/Segment.lean @@ -0,0 +1,27 @@ +import VerifiedGarbage.Spec.Argon2 + +/-! Segment folds used by the filling-loop invariant. -/ + +namespace VG.Proof.Argon2 + +open VG.Spec.Argon2 + +def segment (p : Params) (pass lane slice start count : Nat) (state : FillState) : FillState := + (List.range' start count).foldl (fun state index => fillBlock p pass slice lane index state) state + +theorem segment_zero (p : Params) (pass lane slice start : Nat) (state : FillState) : + segment p pass lane slice start 0 state = state := rfl + +theorem segment_succ (p : Params) (pass lane slice start count : Nat) (state : FillState) : + segment p pass lane slice start (count + 1) state = + segment p pass lane slice (start + 1) count (fillBlock p pass slice lane start state) := by + unfold segment + rw [List.range'_succ, List.foldl_cons] + +theorem segment_append (p : Params) (pass lane slice start a b : Nat) (state : FillState) : + segment p pass lane slice start (a + b) state = + segment p pass lane slice (start + a) b (segment p pass lane slice start a state) := by + unfold segment + rw [← List.range'_append_1, List.foldl_append] + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/SegmentIndices.lean b/lean/VerifiedGarbage/Proof/Argon2/SegmentIndices.lean new file mode 100644 index 000000000..caf79489c --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/SegmentIndices.lean @@ -0,0 +1,38 @@ +import VerifiedGarbage.Proof.Argon2.Segment +import VerifiedGarbage.Proof.Argon2.FillStep + +/-! The reference log exposes exactly one coordinate per dependent active cell. -/ + +namespace VG.Proof.Argon2 + +open VG.Spec.Argon2 + +theorem segment_indices_drop (p : Params) (pass lane slice start count : Nat) (state : FillState) + (active : pass ≠ 0 ∨ slice ≠ 0 ∨ 2 ≤ start) (dependent : independent p pass slice = false) : + (segment p pass lane slice start count state).indices.drop count = state.indices := by + induction count generalizing start state with + | zero => rfl + | succ n ih => + rw [segment_succ] + have next : pass ≠ 0 ∨ slice ≠ 0 ∨ 2 ≤ start + 1 := by omega + rw [← List.drop_drop, ih (start + 1) (fillBlock p pass slice lane start state) next] + rw [FillStep.indices p pass lane slice start state active] + simp only [dependent, Bool.false_eq_true, ite_false, List.drop_succ_cons, List.drop_zero] + +theorem segment_first_reference (p : Params) (pass lane slice start count : Nat) + (leftState rightState : FillState) (active : pass ≠ 0 ∨ slice ≠ 0 ∨ 2 ≤ start) + (indices : (segment p pass lane slice start (count + 1) leftState).indices = + (segment p pass lane slice start (count + 1) rightState).indices) + (dependent : independent p pass slice = false) : + reference p pass lane slice start (FillStep.random p pass lane slice start leftState.memory) = + reference p pass lane slice start (FillStep.random p pass lane slice start rightState.memory) := by + have dropped := congrArg (List.drop count) indices + rw [segment_succ, segment_succ, + segment_indices_drop p pass lane slice (start + 1) count _ (by omega) dependent, + segment_indices_drop p pass lane slice (start + 1) count _ (by omega) dependent, + FillStep.indices p pass lane slice start leftState active, + FillStep.indices p pass lane slice start rightState active] at dropped + simp only [dependent, Bool.false_eq_true, ite_false] at dropped + exact (List.cons.inj dropped).1 + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/SegmentStart.lean b/lean/VerifiedGarbage/Proof/Argon2/SegmentStart.lean new file mode 100644 index 000000000..bb7d434c8 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/SegmentStart.lean @@ -0,0 +1,26 @@ +import VerifiedGarbage.Proof.Argon2.Segment + +/-! The first two cells of pass zero's first segment are already initialized. -/ + +namespace VG.Proof.Argon2 + +open VG.Spec.Argon2 + +def segmentStart (pass slice : Nat) : Nat := if pass = 0 ∧ slice = 0 then 2 else 0 + +theorem segment_first_two (p : Params) (lane : Nat) (state : FillState) : + segment p 0 lane 0 0 2 state = state := by + rw [segment_succ, segment_succ, segment_zero] + simp only [fillBlock, Nat.reduceAdd, and_self, Nat.reduceLT, ite_true] + +theorem segment_start (p : Params) (pass lane slice : Nat) (state : FillState) (minimum : 2 ≤ p.segmentLen) : + segment p pass lane slice 0 p.segmentLen state = + segment p pass lane slice (segmentStart pass slice) (p.segmentLen - segmentStart pass slice) state := by + by_cases first : pass = 0 ∧ slice = 0 + · obtain ⟨rfl, rfl⟩ := first + have append := segment_append p 0 lane 0 0 2 (p.segmentLen - 2) state + rw [show 2 + (p.segmentLen - 2) = p.segmentLen by omega, segment_first_two] at append + exact append + · simp only [segmentStart, first, ite_false, Nat.sub_zero] + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/Serialization.lean b/lean/VerifiedGarbage/Proof/Argon2/Serialization.lean new file mode 100644 index 000000000..adbae7e35 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/Serialization.lean @@ -0,0 +1,29 @@ +import VerifiedGarbage.Spec.Argon2.Contract +import VerifiedGarbage.Proof.Blake2.Stream + +/-! Serialize the final word block as the same 1024 bytes consumed by H′. -/ + +namespace VG.Proof.Argon2 + +open VG VG.Spec.Argon2 +open VG.Spec.Blake2 (bytesAt) + +theorem serialize_blockAt (m : Mem) (p : Addr) : serialize (blockAt m p) = bytesAt m p 1024 := by + have words : (blockAt m p).toList = + (List.range 128).map (fun j => m.readW (p + BitVec.ofNat 64 (8 * j)) 64) := by + rw [blockAt, Vector.toList_ofFn] + apply List.ext_getElem (by simp only [List.length_ofFn, List.length_map, List.length_range]) + intro i hi _ + simp only [List.length_ofFn] at hi + simp only [List.getElem_ofFn, List.getElem_map, List.getElem_range] + rfl + rw [serialize, words, List.flatMap_map] + have bytes := Proof.Blake2.bytesAt_words (w := 64) m p 128 + change bytesAt m p 1024 = _ at bytes + rw [bytes] + apply congrArg List.flatten + apply List.map_congr_left + intro j _ + exact Proof.Blake2.wordBytes_readW m _ (Or.inr rfl) + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/Slices.lean b/lean/VerifiedGarbage/Proof/Argon2/Slices.lean new file mode 100644 index 000000000..2d8114700 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/Slices.lean @@ -0,0 +1,23 @@ +import VerifiedGarbage.Proof.Argon2.Lanes + +/-! Slice folds expose the reviewed filling pass without changing its specification. -/ + +namespace VG.Proof.Argon2 + +open VG.Spec.Argon2 + +def slices (p : Params) (pass start count : Nat) (state : FillState) : FillState := + (List.range' start count).foldl (fun state slice => lanes p pass slice 0 p.lanes state) state + +theorem slices_zero (p : Params) (pass start : Nat) (state : FillState) : slices p pass start 0 state = state := rfl + +theorem slices_succ (p : Params) (pass start count : Nat) (state : FillState) : + slices p pass start (count + 1) state = slices p pass (start + 1) count (lanes p pass start 0 p.lanes state) := by + unfold slices + rw [List.range'_succ, List.foldl_cons] + +theorem slices_pass (p : Params) (pass : Nat) (state : FillState) : slices p pass 0 4 state = fillPass p state pass := by + unfold slices lanes segment fillPass + simp only [List.range_eq_range'] + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/SlicesIndices.lean b/lean/VerifiedGarbage/Proof/Argon2/SlicesIndices.lean new file mode 100644 index 000000000..319e38d2b --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/SlicesIndices.lean @@ -0,0 +1,38 @@ +import VerifiedGarbage.Proof.Argon2.Slices +import VerifiedGarbage.Proof.Argon2.LanesIndices + +/-! Reference-log suffixes span slices with different public addressing modes. -/ + +namespace VG.Proof.Argon2 + +open VG.Spec.Argon2 + +def sliceReferences (p : Params) (pass slice : Nat) : Nat := p.lanes * segmentReferences p pass slice + +def slicesReferences (p : Params) (pass start : Nat) : Nat → Nat + | 0 => 0 + | n + 1 => sliceReferences p pass start + slicesReferences p pass (start + 1) n + +theorem slices_indices_drop (p : Params) (pass start count : Nat) (state : FillState) + (minimum : 2 ≤ p.segmentLen) : + (slices p pass start count state).indices.drop (slicesReferences p pass start count) = state.indices := by + induction count generalizing start state with + | zero => rfl + | succ n ih => + rw [slices_succ, slicesReferences, + show sliceReferences p pass start + slicesReferences p pass (start + 1) n = + slicesReferences p pass (start + 1) n + sliceReferences p pass start from Nat.add_comm _ _, + ← List.drop_drop, ih] + exact lanes_indices_drop p pass start 0 p.lanes state minimum + +theorem slices_first_lane_fold (p : Params) (pass start count : Nat) (leftState rightState : FillState) + (minimum : 2 ≤ p.segmentLen) + (indices : (slices p pass start (count + 1) leftState).indices = + (slices p pass start (count + 1) rightState).indices) : + (lanes p pass start 0 p.lanes leftState).indices = (lanes p pass start 0 p.lanes rightState).indices := by + have dropped := congrArg (List.drop (slicesReferences p pass (start + 1) count)) indices + rw [slices_succ, slices_succ, slices_indices_drop p pass (start + 1) count _ minimum, + slices_indices_drop p pass (start + 1) count _ minimum] at dropped + exact dropped + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCache.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCache.lean new file mode 100644 index 000000000..1ed6dcf14 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCache.lean @@ -0,0 +1,49 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheSelect +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheWord + +/-! Complete cached random-word selection against RFC 9106's address block. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCache + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.AddressCache + +structure Done (s t : State) (p : Params) (pass lane slice : Nat) : Prop where + selected : Selected s t p pass lane slice + random : t.gpr .rdi = + (addressBlock p pass lane slice (wanted s))[(s.gpr .r15).toNat % 128]'(Nat.mod_lt _ (by decide)) + +theorem code_ok (p : Params) (pass lane slice old : Nat) (s : State) + (h : Ready p pass lane slice old s) : + WP isa code s (Done s · p pass lane slice) := by + unfold code + refine WP.seq ((selected_ok p pass lane slice old s h).mono ?_) + intro a selected + refine (word_ok a selected.layout).mono ?_ + rintro t ⟨random, keeps⟩ + have regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r := by + intro r hr + have ne : r ∉ [Reg.rcx, .rax, .rdi] := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + exact (keeps.regs r ne).trans (selected.regs r hr) + have bp := keeps.regs .rbp (by decide) + have sp := keeps.regs .rsp (by decide) + have work' : AddressCalls.work t = AddressCalls.work a := by + unfold AddressCalls.work; rw [bp, keeps.mem] + have layout : AddressCalls.Ready t := by + constructor + · rw [keeps.rd, keeps.wr, bp]; exact selected.layout.frameRead + · rw [work', keeps.wr]; exact selected.layout.workWrite + · rw [bp, work']; exact selected.layout.frameWork + · rw [bp, sp]; exact selected.layout.frameStack + · rw [sp, work']; exact selected.layout.stackWork + refine ⟨⟨?_, layout, work'.trans selected.work_eq, regs, + keeps.rd.trans selected.rd, keeps.wr.trans selected.wr, ?_, + keeps.mxcsr.trans selected.mxcsr, ?_⟩, ?_⟩ + · rw [keeps.mem]; exact selected.block + · rw [keeps.mem]; exact selected.frame + · rw [bp, keeps.mem]; exact selected.counterWord + · rw [selected.work_eq, selected.regs .r15 (by simp [calleeSaved]), selected.block] at random + exact random + +end VG.Proof.Argon2.X86_64.AddressCache diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheInvariant.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheInvariant.lean new file mode 100644 index 000000000..19fdec209 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheInvariant.lean @@ -0,0 +1,87 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheState + +/-! Cache validity does not depend on the current index, so advancing an index +retains it. Counter zero requires no cached contents; every other counter +identifies its specified independent-address block. +-/ + +namespace VG.Proof.Argon2.X86_64.AddressCache + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Invariant (p : Params) (pass lane slice old : Nat) (s : State) : Prop where + layout : AddressCalls.Ready s + reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8 + write : InRegions s.wr (off (s.gpr .rbp) 8) 8 + words : AddressHeader.Words p pass lane slice old s + bound : old < 2 ^ 64 + cached : old = 0 ∨ blockAt s.mem (off (AddressCalls.work s) 6144) = addressBlock p pass lane slice old + +theorem wanted_bound (s : State) : wanted s < 2 ^ 64 := by + unfold wanted + have := (s.gpr .r15).isLt + omega + +theorem Invariant.ready {p : Params} {pass lane slice old : Nat} {s : State} + (h : Invariant p pass lane slice old s) : Ready p pass lane slice old s := by + refine ⟨h.layout, h.reads, h.write, h.words, ?_⟩ + intro same + have word : BitVec.ofNat 64 (wanted s) = BitVec.ofNat 64 old := by + unfold wanted + rw [← counter_nat]; exact same.trans h.words.counterWord + have equal := (ReferenceMap.word_eq _ _ (wanted_bound s) h.bound).mp word + rcases h.cached with zero | cached + · exfalso + exact counter_ne_zero _ (same.trans (h.words.counterWord.trans (by rw [zero]; rfl))) + · rw [← equal] at cached + exact cached + +theorem Selected.invariant {s t : State} {p : Params} {pass lane slice old : Nat} + (ready : Ready p pass lane slice old s) (h : Selected s t p pass lane slice) : + Invariant p pass lane slice (wanted s) t := by + have bp := h.regs .rbp (by simp [calleeSaved]) + refine ⟨h.layout, ?_, ?_, h.words ready, wanted_bound s, Or.inr ?_⟩ + · rw [h.rd, h.wr, bp]; exact ready.reads + · rw [h.wr, bp]; exact ready.write + · rw [h.work_eq]; exact h.block + +theorem Invariant.zero {p : Params} {pass lane slice : Nat} {s : State} + (h : Ready p pass lane slice 0 s) : Invariant p pass lane slice 0 s := + ⟨h.layout, h.reads, h.write, h.words, by decide, Or.inl rfl⟩ + +theorem Invariant.of_state {p : Params} {pass lane slice old : Nat} {s t : State} + (h : Invariant p pass lane slice old s) + (regs : ∀ r ∈ [Reg.rsp, .rbp, .rbx, .r14], t.gpr r = s.gpr r) + (mem : t.mem = s.mem) (rd : t.rd = s.rd) (wr : t.wr = s.wr) : + Invariant p pass lane slice old t := by + have bp := regs .rbp (by simp) + have sp := regs .rsp (by simp) + have work : AddressCalls.work t = AddressCalls.work s := by + unfold AddressCalls.work; rw [mem, bp] + refine ⟨?_, ?_, ?_, ?_, h.bound, ?_⟩ + · constructor + · rw [rd, wr, bp]; exact h.layout.frameRead + · rw [wr, work]; exact h.layout.workWrite + · rw [bp, work]; exact h.layout.frameWork + · rw [bp, sp]; exact h.layout.frameStack + · rw [sp, work]; exact h.layout.stackWork + · rw [rd, wr, bp]; exact h.reads + · rw [wr, bp]; exact h.write + · exact ⟨by rw [mem, bp]; exact h.words.passWord, + (regs .rbx (by simp)).trans h.words.laneWord, + (regs .r14 (by simp)).trans h.words.sliceWord, + by rw [mem, bp]; exact h.words.blocksWord, + by rw [mem, bp]; exact h.words.passesWord, + by rw [mem, bp]; exact h.words.variantWord, + by rw [mem, bp]; exact h.words.counterWord⟩ + · rw [mem, work]; exact h.cached + +theorem Invariant.of_keeps {p : Params} {pass lane slice old : Nat} {s t : State} + (h : Invariant p pass lane slice old s) (k : Divide.Keeps ReferenceMap.changed s t) : + Invariant p pass lane slice old t := by + apply h.of_state _ k.mem k.rd k.wr + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl <;> exact k.regs _ (by decide) + +end VG.Proof.Argon2.X86_64.AddressCache diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheMatrix.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheMatrix.lean new file mode 100644 index 000000000..bb635e73e --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheMatrix.lean @@ -0,0 +1,59 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheState +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelInvariant +import VerifiedGarbage.Proof.Argon2.Matrix + +/-! Independent-address generation leaves every matrix cell intact. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCache + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem Selected.filling_ready {s t : State} {p : Params} {pass lane slice index : Nat} + (cacheLayout : AddressCalls.Ready s) (h : FillKernel.Ready p pass lane slice index s) + (done : Selected s t p pass lane slice) : FillKernel.Ready p pass lane slice index t := by + have bp := done.regs .rbp (by simp [calleeSaved]) + have sp := done.regs .rsp (by simp [calleeSaved]) + have matrix' : FillKernel.matrix t = FillKernel.matrix s := done.frame_word cacheLayout 232 (by decide) (by decide) + have work' : FillKernel.work t = FillKernel.work s := done.frame_word cacheLayout 248 (by decide) (by decide) + refine ⟨?_, h.bounds, ?_, (done.frame_word cacheLayout 0 (by decide) (by decide)).trans h.passWord, + (done.frame_word cacheLayout 184 (by decide) (by decide)).trans h.lanesWord⟩ + · constructor + · rw [done.rd, done.wr, bp]; exact h.layout.frameRead + · rw [done.wr, bp]; exact h.layout.frameWrite + · rw [matrix', done.wr]; exact h.layout.matrixWrite + · rw [work', done.wr]; exact h.layout.workWrite + · rw [matrix', work']; exact h.layout.matrixWork + · rw [matrix', bp]; exact h.layout.matrixFrame + · rw [matrix', sp]; exact h.layout.matrixStack + · rw [bp, work']; exact h.layout.frameWork + · rw [bp, sp]; exact h.layout.frameStack + · rw [sp, work']; exact h.layout.stackWork + · exact ⟨(done.regs .rbx (by simp [calleeSaved])).trans h.position.current, + (done.regs .r12 (by simp [calleeSaved])).trans h.position.laneLength, + (done.regs .r13 (by simp [calleeSaved])).trans h.position.segmentLength, + (done.regs .r14 (by simp [calleeSaved])).trans h.position.slice, + (done.regs .r15 (by simp [calleeSaved])).trans h.position.index⟩ + +theorem Selected.represents {s t : State} {p : Params} {pass lane slice index : Nat} + (cacheLayout : AddressCalls.Ready s) (h : FillKernel.Ready p pass lane slice index s) + (matrixWork : (⟨FillKernel.matrix s, p.blocks * 1024⟩ : Region).Disjoint ⟨AddressCalls.work s, 8192⟩) + (done : Selected s t p pass lane slice) (blocks : Array Block) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks blocks) : + Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks blocks := by + have base : FillKernel.matrix t = FillKernel.matrix s := done.frame_word cacheLayout 232 (by decide) (by decide) + rw [base] + refine ⟨represented.size, ?_⟩ + intro k hk + have kept : blockAt t.mem (Proof.Argon2.matrixCell (FillKernel.matrix s) k) = + blockAt s.mem (Proof.Argon2.matrixCell (FillKernel.matrix s) k) := by + apply FillCompress.block_frame done.frame + intro r hr + simp only [writes, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · exact matrixWork.sub_left (Proof.Argon2.matrixCell_sub _ _ _ hk) + · exact h.layout.matrixStack.sub_left (Proof.Argon2.matrixCell_sub _ _ _ hk) + · exact (h.layout.matrixFrame.sub_left (Proof.Argon2.matrixCell_sub _ _ _ hk)).sub_right + (Offset.sub_base _ (by decide)) + exact kept.trans (represented.block k hk) + +end VG.Proof.Argon2.X86_64.AddressCache diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheMeta.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheMeta.lean new file mode 100644 index 000000000..5a2664356 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheMeta.lean @@ -0,0 +1,77 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceStart +import VerifiedGarbage.Impl.Argon2.X86_64.AddressCache +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCallsPrepare + +/-! Public cache counters and indexed-word arguments. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCache + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.AddressCache + +def counter (index : Addr) : Addr := (index >>> 7) + 1 + +theorem check_ok (s : State) + (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 8) 8) : + WP isa (.block check) s fun t => t.gpr .rax = counter (s.gpr .r15) ∧ + t.zf = decide (counter (s.gpr .r15) = s.mem.readW (off (s.gpr .rbp) 8) 64) ∧ + Divide.Keeps [.rax] s t := by + apply WP.of_runBlock + simp only [check, counter, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + execShift, execAlu, State.load64, ea_at, hr, RegUpd.gpr_setReg, RegUpd.gpr_setFlags, + RegUpd.gpr_arithFlags, RegUpd.mem_setReg, RegUpd.mem_setFlags, RegUpd.mem_arithFlags, + RegUpd.rd_setReg, RegUpd.rd_setFlags, RegUpd.rd_arithFlags, + RegUpd.wr_setReg, RegUpd.wr_setFlags, RegUpd.wr_arithFlags, + RegUpd.zf_arithFlags, reduceCtorEq, ite_true, ite_false, and_self, + show 1 ≤ (7 : Nat) ∧ (7 : Nat) ≤ 63 from by decide, + show BitVec.signExtend 64 (1 : BitVec 32) = (1 : Addr) from rfl, + Option.map_some, Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, ?_, ?_⟩ + · apply Bool.eq_iff_iff.mpr + simp only [beq_iff_eq, ReferenceStart.sub_zero_iff] + exact ⟨fun h => decide_eq_true h, of_decide_eq_true⟩ + · constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_setFlags, RegUpd.gpr_arithFlags, hr, ite_false] + all_goals rfl + +theorem counter_nat (index : Addr) : counter index = BitVec.ofNat 64 (index.toNat / 128 + 1) := by + have shifted : index >>> 7 = BitVec.ofNat 64 (index.toNat / 128) := by + apply BitVec.eq_of_toNat_eq + rw [BitVec.toNat_ushiftRight, Nat.shiftRight_eq_div_pow, BitVec.toNat_ofNat, + Nat.mod_eq_of_lt (by have := index.isLt; omega)] + unfold counter + rw [shifted] + exact (BitVec.ofNat_add _ _).symm + +theorem counter_ne_zero (index : Addr) : counter index ≠ 0 := by + have bound : index.toNat / 128 + 1 < 2 ^ 64 := by have := index.isLt; omega + intro h + have nat := congrArg BitVec.toNat h + rw [counter_nat, BitVec.toNat_ofNat, Nat.mod_eq_of_lt bound] at nat + change index.toNat / 128 + 1 = 0 at nat + omega + +theorem wordArgs_ok (s : State) + (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 248) 8) : + WP isa (.block wordArgs) s fun t => t.gpr .rcx = AddressCalls.work s ∧ + t.gpr .rax = s.gpr .r15 &&& 127 ∧ Divide.Keeps [.rcx, .rax] s t := by + apply WP.of_runBlock + simp only [wordArgs, AddressCalls.work, runBlock_cons, runStep_some, runBlock_nil, + exec, readSrc, State.load64, ea_at, hr, execAlu, + RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, reduceCtorEq, ite_true, ite_false, + show BitVec.signExtend 64 (127 : BitVec 32) = (127 : Addr) from rfl, + Option.map_some, Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr.1, hr.2, ite_false] + all_goals rfl + +theorem index_nat (index : Addr) : index &&& 127 = BitVec.ofNat 64 (index.toNat % 128) := by + apply BitVec.eq_of_toNat_eq + rw [BitVec.toNat_and, BitVec.toNat_ofNat, Nat.mod_eq_of_lt (by omega)] + exact Nat.and_two_pow_sub_one_eq_mod index.toNat 7 + +end VG.Proof.Argon2.X86_64.AddressCache diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheSave.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheSave.lean new file mode 100644 index 000000000..f69d79251 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheSave.lean @@ -0,0 +1,65 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheMeta + +/-! Save the public cache counter without disturbing scratch or header fields. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCache + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.AddressCache + +theorem save_ok (s : State) (hw : InRegions s.wr (off (s.gpr .rbp) 8) 8) : + WP isa (.block save) s fun t => + t.mem = s.mem.writeW (off (s.gpr .rbp) 8) (s.gpr .rax) ∧ + t.gpr = s.gpr ∧ t.rd = s.rd ∧ t.wr = s.wr ∧ t.mxcsr = s.mxcsr := by + apply WP.of_runBlock + simp only [save, runBlock_cons, runStep_some, runBlock_nil, exec, State.store64, + ea_at, hw, ite_true, Option.some.injEq, exists_eq_left'] + exact ⟨trivial, trivial, trivial, trivial, trivial⟩ + +structure Saved (s t : State) : Prop where + mem : t.mem = s.mem.writeW (off (s.gpr .rbp) 8) (s.gpr .rax) + regs : t.gpr = s.gpr + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + ready : AddressCalls.Ready t + work_eq : AddressCalls.work t = AddressCalls.work s + frame : Frame [⟨off (s.gpr .rbp) 8, 8⟩] s.mem t.mem + +theorem save_ready (s : State) (h : AddressCalls.Ready s) + (hw : InRegions s.wr (off (s.gpr .rbp) 8) 8) : WP isa (.block save) s (Saved s) := by + refine (save_ok s hw).mono ?_ + rintro t ⟨mem, regs, rd, wr, mx⟩ + have work' : AddressCalls.work t = AddressCalls.work s := by + unfold AddressCalls.work + rw [regs, mem, Mem.readW_writeW_sep (Offset.sep _ (by decide) (by decide) (by decide)) (by decide)] + have ready : AddressCalls.Ready t := by + constructor + · rw [rd, wr, regs]; exact h.frameRead + · rw [work', wr]; exact h.workWrite + · rw [regs, work']; exact h.frameWork + · rw [regs]; exact h.frameStack + · rw [regs, work']; exact h.stackWork + refine ⟨mem, regs, rd, wr, mx, ready, work', ?_⟩ + rw [mem] + exact (Frame.refl _ _).writeW (r := ⟨off (s.gpr .rbp) 8, 8⟩) (by simp) _ + (Region.contains_self _ _) + +theorem Saved.read {s t : State} (h : Saved s t) (d : Nat) + (hd : d + 8 ≤ 8 ∨ 16 ≤ d) (bound : d + 8 ≤ 272) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [h.regs, h.mem] + exact Mem.readW_writeW_sep (Offset.sep _ hd (by omega) (by decide)) (by decide) + +theorem Saved.words {s t : State} {p : Params} {pass lane slice old counter : Nat} + (h : Saved s t) (words : AddressHeader.Words p pass lane slice old s) + (value : s.gpr .rax = BitVec.ofNat 64 counter) : + AddressHeader.Words p pass lane slice counter t := by + refine ⟨(h.read 0 (by decide) (by decide)).trans words.passWord, + ?_, ?_, (h.read 240 (by decide) (by decide)).trans words.blocksWord, + (h.read 72 (by decide) (by decide)).trans words.passesWord, + (h.read 112 (by decide) (by decide)).trans words.variantWord, ?_⟩ + · rw [h.regs]; exact words.laneWord + · rw [h.regs]; exact words.sliceWord + · rw [h.regs, h.mem, Mem.readW_writeW_self64, value] + +end VG.Proof.Argon2.X86_64.AddressCache diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheSelect.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheSelect.lean new file mode 100644 index 000000000..2426c91d0 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheSelect.lean @@ -0,0 +1,114 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheSave +import VerifiedGarbage.Proof.Argon2.X86_64.AddressGeneration + +/-! Regenerate only when the public one-based block counter changes. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCache + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.AddressCache + +def wanted (s : State) : Nat := (s.gpr .r15).toNat / 128 + 1 + +def writes (s : State) : List Region := + [⟨AddressCalls.work s, 8192⟩, below (s.gpr .rsp) 8, ⟨off (s.gpr .rbp) 8, 8⟩] + +structure Ready (p : Params) (pass lane slice old : Nat) (s : State) : Prop where + layout : AddressCalls.Ready s + reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8 + write : InRegions s.wr (off (s.gpr .rbp) 8) 8 + words : AddressHeader.Words p pass lane slice old s + cached : counter (s.gpr .r15) = s.mem.readW (off (s.gpr .rbp) 8) 64 → + blockAt s.mem (off (AddressCalls.work s) 6144) = addressBlock p pass lane slice (wanted s) + +theorem ready_zero (p : Params) (pass lane slice : Nat) (s : State) + (layout : AddressCalls.Ready s) + (reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8) + (write : InRegions s.wr (off (s.gpr .rbp) 8) 8) + (words : AddressHeader.Words p pass lane slice 0 s) : Ready p pass lane slice 0 s := + ⟨layout, reads, write, words, fun same => False.elim + (counter_ne_zero _ (same.trans words.counterWord))⟩ + +structure Selected (s t : State) (p : Params) (pass lane slice : Nat) : Prop where + block : blockAt t.mem (off (AddressCalls.work s) 6144) = addressBlock p pass lane slice (wanted s) + layout : AddressCalls.Ready t + work_eq : AddressCalls.work t = AddressCalls.work s + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (writes s) s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + counterWord : t.mem.readW (off (t.gpr .rbp) 8) 64 = counter (s.gpr .r15) + +theorem check_stable {s a : State} (h : AddressCalls.Ready s) (k : Divide.Keeps [.rax] s a) : + AddressCalls.Stable s a := by + apply AddressCalls.stable_of_frame h _ k.rd k.wr _ k.mxcsr + · intro r hr + apply k.regs + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + · rw [k.mem]; exact Frame.refl _ _ + +theorem selected_ok (p : Params) (pass lane slice old : Nat) (s : State) + (h : Ready p pass lane slice old s) : + WP isa select s (Selected s · p pass lane slice) := by + unfold select + refine WP.seq ((check_ok s (h.reads 8 (by simp))).mono ?_) + rintro a ⟨value, flag, keeps⟩ + have stableA := check_stable h.layout keeps + refine WP.ite (decide (counter (s.gpr .r15) = s.mem.readW (off (s.gpr .rbp) 8) 64)) + (by simp only [eval, flag]) ?_ ?_ + · intro same + have equal := of_decide_eq_true same + apply WP.of_runBlock + simp only [runBlock_nil, Option.some.injEq, exists_eq_left'] + refine ⟨?_, stableA.ready, stableA.work_eq, stableA.regs, keeps.rd, keeps.wr, + ?_, keeps.mxcsr, ?_⟩ + · rw [keeps.mem]; exact h.cached equal + · rw [keeps.mem]; exact Frame.refl _ _ + · rw [stableA.regs .rbp (by simp [calleeSaved]), keeps.mem]; exact equal.symm + · intro _ + have write : InRegions a.wr (off (a.gpr .rbp) 8) 8 := by + rw [keeps.wr, stableA.regs .rbp (by simp [calleeSaved])]; exact h.write + refine WP.seq ((save_ready a stableA.ready write).mono ?_) + intro b saved + have words : AddressHeader.Words p pass lane slice (wanted s) b := by + apply saved.words (stableA.words h.layout h.words) + rw [value, counter_nat]; rfl + have reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (b.rd ++ b.wr) (off (b.gpr .rbp) d) 8 := by + rw [saved.rd, saved.wr, saved.regs]; exact stableA.reads h.reads + refine (AddressCalls.code_ok p pass lane slice (wanted s) b saved.ready reads words).mono ?_ + rintro t ⟨generated, mx⟩ + have workB : AddressCalls.work b = AddressCalls.work s := saved.work_eq.trans stableA.work_eq + have regsB (r : Reg) (hr : r ∈ calleeSaved) : b.gpr r = s.gpr r := + (congrFun saved.regs r).trans (stableA.regs r hr) + have regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r := + fun r hr => (generated.regs r hr).trans (regsB r hr) + have firstFrame : Frame (writes s) s.mem b.mem := by + have frame := saved.frame + rw [stableA.regs .rbp (by simp [calleeSaved]), keeps.mem] at frame + exact frame.mono (by intro r hr; simp only [List.mem_singleton] at hr; subst r; simp [writes]) + have finalFrame : Frame (writes s) b.mem t.mem := by + have frame := generated.frame + rw [AddressCalls.writes, workB, regsB .rsp (by simp [calleeSaved])] at frame + exact frame.mono (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl <;> simp [writes]) + refine ⟨?_, generated.ready, generated.work.trans workB, regs, + generated.rd.trans (saved.rd.trans keeps.rd), generated.wr.trans (saved.wr.trans keeps.wr), + firstFrame.trans finalFrame, mx.trans (saved.mxcsr.trans keeps.mxcsr), ?_⟩ + · have block := generated.block + rw [workB] at block + exact block + · have preserved : t.mem.readW (off (b.gpr .rbp) 8) 64 = b.mem.readW (off (b.gpr .rbp) 8) 64 := + generated.frame.readW (r := ⟨b.gpr .rbp, 272⟩) + (Offset.contains_base _ (by decide) (by decide)) (by + intro r hr + simp only [AddressCalls.writes, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact saved.ready.frameWork + · exact saved.ready.frameStack) (by decide) + rw [generated.regs .rbp (by simp [calleeSaved]), preserved, saved.regs, saved.mem, + Mem.readW_writeW_self64, value] + +end VG.Proof.Argon2.X86_64.AddressCache diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheSelectCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheSelectCT.lean new file mode 100644 index 000000000..5652b3b09 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheSelectCT.lean @@ -0,0 +1,118 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheSelect +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheWordCT + +/-! Cache regeneration branches only on public counters. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCache + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.AddressCache + +structure CacheRelated (s t : State) : Prop where + prepare : AddressCalls.PrepareRelated s t + indices : s.gpr .r15 = t.gpr .r15 + counters : s.mem.readW (off (s.gpr .rbp) 8) 64 = t.mem.readW (off (t.gpr .rbp) 8) 64 + leftWrite : InRegions s.wr (off (s.gpr .rbp) 8) 8 + rightWrite : InRegions t.wr (off (t.gpr .rbp) 8) 8 + +structure CheckedRelated (s t : State) : Prop where + related : CacheRelated s t + values : s.gpr .rax = t.gpr .rax + flags : s.zf = t.zf + +theorem check_rel : RelCT isa + (fun s t => ∀ r ∈ [Reg.rbp, .r15], s.gpr r = t.gpr r) + (.block check) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp, .r15]) + (fun _ _ h => Taint.agree_ofRegs h) (by taint_decide) + +theorem check_public_rel : RelCT isa CacheRelated (.block check) CheckedRelated := by + have trace := check_rel.mono (P' := CacheRelated) (by + intro s t h r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact h.prepare.related.bases + · exact h.indices) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => + ⟨check_ok s (h.prepare.leftReads 8 (by simp)), check_ok t (h.prepare.rightReads 8 (by simp))⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ⟨va, fa, ka⟩, ⟨vb, fb, kb⟩⟩ := h + have sa := check_stable hp.prepare.related.left ka + have sb := check_stable hp.prepare.related.right kb + have index : a.gpr .r15 = b.gpr .r15 := (sa.regs .r15 (by simp [calleeSaved])).trans + (hp.indices.trans (sb.regs .r15 (by simp [calleeSaved])).symm) + have counters : a.mem.readW (off (a.gpr .rbp) 8) 64 = b.mem.readW (off (b.gpr .rbp) 8) 64 := by + rw [ka.mem, kb.mem, sa.regs .rbp (by simp [calleeSaved]), sb.regs .rbp (by simp [calleeSaved])] + exact hp.counters + refine ⟨⟨⟨hp.prepare.related.of_stable sa sb, sa.reads hp.prepare.leftReads, + sb.reads hp.prepare.rightReads⟩, index, counters, ?_, ?_⟩, ?_, ?_⟩ + · rw [ka.wr, sa.regs .rbp (by simp [calleeSaved])]; exact hp.leftWrite + · rw [kb.wr, sb.regs .rbp (by simp [calleeSaved])]; exact hp.rightWrite + · rw [va, vb, hp.indices] + · rw [fa, fb, hp.indices, hp.counters] + +theorem save_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block save) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +theorem save_public_rel : RelCT isa CheckedRelated (.block save) AddressCalls.PrepareRelated := by + have trace := save_rel.mono (P' := CheckedRelated) + (fun _ _ h => h.related.prepare.related.bases) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => + ⟨save_ready s h.related.prepare.related.left h.related.leftWrite, + save_ready t h.related.prepare.related.right h.related.rightWrite⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + refine ⟨⟨ha.ready, hb.ready, ?_, ?_, ha.work_eq.trans + (hp.related.prepare.related.work.trans hb.work_eq.symm)⟩, ?_, ?_⟩ + · rw [ha.regs, hb.regs]; exact hp.related.prepare.related.bases + · rw [ha.regs, hb.regs]; exact hp.related.prepare.related.stacks + · rw [ha.rd, ha.wr, ha.regs]; exact hp.related.prepare.leftReads + · rw [hb.rd, hb.wr, hb.regs]; exact hp.related.prepare.rightReads + +theorem select_trace : RelCT isa CacheRelated select (fun _ _ => True) := by + have noop : RelCT isa (fun _ _ : State => True) (.block []) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by intro r hr; simp at hr)) (by taint_decide) + have branches : RelCT isa CheckedRelated + (.ite .e (.block []) (.seq (.block save) Impl.Argon2.X86_64.AddressCalls.code)) + (fun _ _ => True) := + RelCT.ite (by intro s t h; simp only [eval, h.flags]) + (noop.mono (fun _ _ _ => trivial) (fun _ _ h => h)) + ((save_public_rel.seq AddressCalls.code_rel).mono (fun _ _ h => h.1) (fun _ _ _ => trivial)) + exact check_public_rel.seq branches + +structure ReadyRelated (p : Spec.Argon2.Params) (pass lane slice old : Nat) (s t : State) : Prop where + left : Ready p pass lane slice old s + right : Ready p pass lane slice old t + pubs : CacheRelated s t + +theorem select_public_rel (p : Spec.Argon2.Params) (pass lane slice old : Nat) : + RelCT isa (ReadyRelated p pass lane slice old) select WordRelated := by + have trace := select_trace.mono (P' := ReadyRelated p pass lane slice old) + (fun _ _ h => h.pubs) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => + ⟨selected_ok p pass lane slice old s h.left, selected_ok p pass lane slice old t h.right⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + refine ⟨⟨ha.layout, hb.layout, ?_, ?_, ha.work_eq.trans + (hp.pubs.prepare.related.work.trans hb.work_eq.symm)⟩, ?_⟩ + · exact (ha.regs .rbp (by simp [calleeSaved])).trans + (hp.pubs.prepare.related.bases.trans (hb.regs .rbp (by simp [calleeSaved])).symm) + · exact (ha.regs .rsp (by simp [calleeSaved])).trans + (hp.pubs.prepare.related.stacks.trans (hb.regs .rsp (by simp [calleeSaved])).symm) + · exact (ha.regs .r15 (by simp [calleeSaved])).trans + (hp.pubs.indices.trans (hb.regs .r15 (by simp [calleeSaved])).symm) + +theorem code_rel (p : Spec.Argon2.Params) (pass lane slice old : Nat) : + RelCT isa (ReadyRelated p pass lane slice old) code (fun _ _ => True) := + (select_public_rel p pass lane slice old).seq word_rel + +end VG.Proof.Argon2.X86_64.AddressCache diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheState.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheState.lean new file mode 100644 index 000000000..fbed7f0c3 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheState.lean @@ -0,0 +1,53 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCache +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelStable + +/-! Retain the filling header and allocation across independent-address regeneration. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCache + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem Selected.frame_word {s t : State} {p : Params} {pass lane slice : Nat} + (layout : AddressCalls.Ready s) (h : Selected s t p pass lane slice) + (d : Nat) (bound : d + 8 ≤ 272) (separate : d + 8 ≤ 8 ∨ 16 ≤ d) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [h.regs .rbp (by simp [calleeSaved])] + have sub : Region.Sub ⟨off (s.gpr .rbp) d, 8⟩ ⟨s.gpr .rbp, 272⟩ := Offset.sub_base _ bound + exact h.frame.readW (r := ⟨off (s.gpr .rbp) d, 8⟩) (Region.contains_self _ _) (by + intro r hr + simp only [writes, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · exact layout.frameWork.sub_left sub + · exact layout.frameStack.sub_left sub + · exact Offset.disjoint _ separate (by omega) (by decide)) (by decide) + +theorem Selected.words {s t : State} {p : Params} {pass lane slice old : Nat} + (ready : Ready p pass lane slice old s) (h : Selected s t p pass lane slice) : + AddressHeader.Words p pass lane slice (wanted s) t := by + exact ⟨(h.frame_word ready.layout 0 (by decide) (by decide)).trans ready.words.passWord, + (h.regs .rbx (by simp [calleeSaved])).trans ready.words.laneWord, + (h.regs .r14 (by simp [calleeSaved])).trans ready.words.sliceWord, + (h.frame_word ready.layout 240 (by decide) (by decide)).trans ready.words.blocksWord, + (h.frame_word ready.layout 72 (by decide) (by decide)).trans ready.words.passesWord, + (h.frame_word ready.layout 112 (by decide) (by decide)).trans ready.words.variantWord, + h.counterWord.trans (counter_nat _)⟩ + +theorem Ready.of_keeps {p : Params} {pass lane slice old : Nat} {s t : State} + (h : Ready p pass lane slice old s) (k : Divide.Keeps ReferenceMap.changed s t) : + Ready p pass lane slice old t := by + have regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r := by + intro r hr + apply k.regs + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + have stable := AddressCalls.stable_of_frame h.layout regs k.rd k.wr + (by rw [k.mem]; exact Frame.refl _ _) k.mxcsr + refine ⟨stable.ready, stable.reads h.reads, ?_, stable.words h.layout h.words, ?_⟩ + · rw [k.wr, k.regs .rbp (by decide)]; exact h.write + · intro same + have wanted' : wanted t = wanted s := by unfold wanted; rw [k.regs .r15 (by decide)] + rw [k.mem, k.regs .rbp (by decide), k.regs .r15 (by decide)] at same + rw [k.mem, stable.work_eq, wanted'] + exact h.cached same + +end VG.Proof.Argon2.X86_64.AddressCache diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheWord.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheWord.lean new file mode 100644 index 000000000..095983bc2 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheWord.lean @@ -0,0 +1,61 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheMeta + +/-! Read exactly the public indexed word of the cached address block. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCache + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.AddressCache + +def wordAddress (s : State) : Addr := + s.gpr .rcx + s.gpr .rax * BitVec.ofNat 64 8 + BitVec.ofInt 64 6144 + +theorem wordRead_ok (s : State) (hr : InRegions (s.rd ++ s.wr) (wordAddress s) 8) : + WP isa (.block wordRead) s fun t => t.gpr .rdi = s.mem.readW (wordAddress s) 64 ∧ + Divide.Keeps [.rdi] s t := by + dsimp only [wordAddress] at hr + apply WP.of_runBlock + simp only [wordRead, wordAddress, runBlock_cons, runStep_some, runBlock_nil, + exec, readSrc, State.load64, State.ea, hr, Option.map_some, + Option.some.injEq, exists_eq_left', RegUpd.gpr_setReg, ite_true] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, hr, ite_false] + all_goals rfl + +theorem wordAddress_args {s a : State} + (scratch : a.gpr .rcx = AddressCalls.work s) + (index : a.gpr .rax = s.gpr .r15 &&& 127) : + wordAddress a = off (off (AddressCalls.work s) 6144) (8 * ((s.gpr .r15).toNat % 128)) := by + unfold wordAddress off + rw [scratch, index, index_nat, ← BitVec.ofNat_mul, Nat.mul_comm] + change AddressCalls.work s + BitVec.ofNat 64 (8 * ((s.gpr .r15).toNat % 128)) + + BitVec.ofNat 64 6144 = _ + rw [BitVec.add_assoc, BitVec.add_comm (BitVec.ofNat 64 (8 * ((s.gpr .r15).toNat % 128))) + (BitVec.ofNat 64 6144), ← BitVec.add_assoc] + +theorem word_ok (s : State) (h : AddressCalls.Ready s) : + WP isa Impl.Argon2.X86_64.AddressCache.word s fun t => t.gpr .rdi = + (blockAt s.mem (off (AddressCalls.work s) 6144))[(s.gpr .r15).toNat % 128]'(Nat.mod_lt _ (by decide)) ∧ + Divide.Keeps [.rcx, .rax, .rdi] s t := by + unfold Impl.Argon2.X86_64.AddressCache.word + refine WP.seq ((wordArgs_ok s h.frameRead).mono ?_) + rintro a ⟨scratch, index, keeps⟩ + have address := wordAddress_args scratch index + have read : InRegions (a.rd ++ a.wr) (wordAddress a) 8 := by + rw [address, keeps.rd, keeps.wr] + have cover := AddressCalls.work_cover s h 6144 1024 (by decide) + have writable := cover _ _ ⟨⟨off (AddressCalls.work s) 6144, 1024⟩, by simp, + Offset.contains_base _ (d := 8 * ((s.gpr .r15).toNat % 128)) (n := 8) (k := 1024) + (by have := Nat.mod_lt (s.gpr .r15).toNat (by decide : 0 < 128); omega) (by omega)⟩ + obtain ⟨r, hr, hc⟩ := writable + exact ⟨r, List.mem_append_right _ hr, hc⟩ + refine (wordRead_ok a read).mono ?_ + rintro t ⟨value, tail⟩ + refine ⟨?_, (keeps.mono (by decide)).trans (tail.mono (by decide))⟩ + rw [value, address, keeps.mem] + change s.mem.readW _ 64 = (blockAt _ _)[(⟨_, Nat.mod_lt _ (by decide)⟩ : Fin 128)] + rw [blockAt_get] + +end VG.Proof.Argon2.X86_64.AddressCache diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheWordCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheWordCT.lean new file mode 100644 index 000000000..dd5e7ad90 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCacheWordCT.lean @@ -0,0 +1,45 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheWord +import VerifiedGarbage.Proof.Argon2.X86_64.AddressGenerationCT + +/-! The cached random word is secret; its read address is public. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCache + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.AddressCache + +structure WordRelated (s t : State) : Prop where + layout : AddressCalls.Related s t + indices : s.gpr .r15 = t.gpr .r15 + +theorem wordArgs_rel : RelCT isa + (fun s t => ∀ r ∈ [Reg.rbp, .r15], s.gpr r = t.gpr r) + (.block wordArgs) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp, .r15]) + (fun _ _ h => Taint.agree_ofRegs h) (by taint_decide) + +theorem wordRead_rel : RelCT isa + (fun s t => ∀ r ∈ [Reg.rcx, .rax], s.gpr r = t.gpr r) + (.block wordRead) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rcx, .rax]) + (fun _ _ h => Taint.agree_ofRegs h) (by taint_decide) + +theorem word_rel : RelCT isa WordRelated Impl.Argon2.X86_64.AddressCache.word (fun _ _ => True) := by + have trace := wordArgs_rel.mono (P' := WordRelated) (by + intro s t h r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact h.layout.bases + · exact h.indices) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => + ⟨wordArgs_ok s h.layout.left.frameRead, wordArgs_ok t h.layout.right.frameRead⟩) + have args : RelCT isa WordRelated (.block wordArgs) + (fun s t => ∀ r ∈ [Reg.rcx, .rax], s.gpr r = t.gpr r) := full.mono (fun _ _ h => h) (by + intro a b h r hr + obtain ⟨_, s, t, hp, ⟨sa, ia, _⟩, ⟨sb, ib, _⟩⟩ := h + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact sa.trans (hp.layout.work.trans sb.symm) + · exact ia.trans ((congrArg (· &&& 127) hp.indices).trans ib.symm)) + exact args.seq wordRead_rel + +end VG.Proof.Argon2.X86_64.AddressCache diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCalls.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCalls.lean new file mode 100644 index 000000000..9018d7867 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCalls.lean @@ -0,0 +1,64 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCallsStage +import VerifiedGarbage.Proof.Argon2.AddressInput + +/-! Both compression calls produce exactly the reviewed independent-address block. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCalls + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.AddressCalls + +def writes (s : State) : List Region := [⟨work s, 8192⟩, below (s.gpr .rsp) 8] + +structure Generated (s t : State) (p : Params) (pass lane slice counter : Nat) : Prop where + block : blockAt t.mem (off (work s) 6144) = addressBlock p pass lane slice counter + ready : Ready t + work : work t = work s + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (writes s) s.mem t.mem + +theorem stage_frame_full {s t : State} {out : Nat} (bound : out + 1024 ≤ 8192) + (hf : Frame (stageWrites s out) s.mem t.mem) : Frame (writes s) s.mem t.mem := by + apply hf.sub + intro r hr + simp only [stageWrites, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · exact ⟨⟨work s, 8192⟩, by simp [writes], Offset.sub_base _ bound⟩ + · exact ⟨⟨work s, 8192⟩, by simp [writes], Region.sub_prefix (by decide)⟩ + · exact ⟨_, by simp [writes], fun _ h => h⟩ + +theorem zero_preserved {s t : State} (h : Ready s) + (hf : Frame (stageWrites s 4096) s.mem t.mem) : + blockAt t.mem (off (work s) 7168) = blockAt s.mem (off (work s) 7168) := by + apply FillCompress.block_frame hf + intro r hr + simp only [stageWrites, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · exact Offset.disjoint _ (by decide) (by decide) (by decide) + · exact Offset.disjoint_base _ (by decide) (by decide) + · exact (h.stackWork.sub_right (Offset.sub_base _ (by decide))).symm + +theorem calls_ok (p : Params) (pass lane slice counter : Nat) (s : State) (h : Ready s) + (zero : blockAt s.mem (off (work s) 7168) = zeroBlock) + (input : blockAt s.mem (off (work s) 5120) = Proof.Argon2.addressInput p pass lane slice counter) : + WP isa calls s (Generated s · p pass lane slice counter) := by + unfold calls + refine WP.seq ((stage_ok s h 7168 5120 4096 (by decide) (by decide) (by decide) + (by decide) (by decide) (by decide)).mono ?_) + intro a first + refine (stage_ok a first.ready 7168 4096 6144 (by decide) (by decide) (by decide) + (by decide) (by decide) (by decide)).mono ?_ + intro t second + refine ⟨?_, second.ready, second.work.trans first.work, + fun r hr => (second.regs r hr).trans (first.regs r hr), + second.rd.trans first.rd, second.wr.trans first.wr, ?_⟩ + · have result := second.result + rw [first.work, zero_preserved h first.frame, zero, first.result, zero, input] at result + rw [Proof.Argon2.addressBlock_eq] + exact result + · have next := stage_frame_full (by decide) second.frame + simp only [writes, first.work, first.regs .rsp (by simp [calleeSaved])] at next + exact (stage_frame_full (by decide) first.frame).trans next + +end VG.Proof.Argon2.X86_64.AddressCalls diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsArgs.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsArgs.lean new file mode 100644 index 000000000..1794c8b57 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsArgs.lean @@ -0,0 +1,49 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.AddressCalls +import VerifiedGarbage.Proof.Argon2.X86_64.AddressHeaderWords + +/-! Independent-address compression arguments from one fixed frame read. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCalls + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.AddressCalls + +def work (s : State) : Addr := s.mem.readW (off (s.gpr .rbp) 248) 64 + +def displacement (n : Nat) : Addr := BitVec.signExtend 64 (BitVec.ofNat 32 n) + +theorem pointer_ok (s : State) (offset : Nat) + (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 248) 8) : + WP isa (.block (pointer offset)) s fun t => + t.gpr .rdi = work s + displacement offset ∧ Divide.Keeps [.rdi] s t := by + apply WP.of_runBlock + simp only [pointer, work, displacement, runBlock_cons, runStep_some, runBlock_nil, + exec, readSrc, State.load64, ea_at, hr, execAlu, RegUpd.gpr_setReg, + RegUpd.gpr_arithFlags, ite_true, Option.map_some, + Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr, ite_false] + all_goals rfl + +theorem args_ok (s : State) (x y out : Nat) + (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 248) 8) : + WP isa (.block (args x y out)) s fun t => + t.gpr .rcx = work s ∧ t.gpr .rdi = work s + displacement x ∧ + t.gpr .rsi = work s + displacement y ∧ t.gpr .rdx = work s + displacement out ∧ + Divide.Keeps [.rcx, .rdi, .rsi, .rdx] s t := by + apply WP.of_runBlock + simp only [args, work, displacement, runBlock_cons, runStep_some, runBlock_nil, + exec, readSrc, State.load64, ea_at, hr, execAlu, RegUpd.gpr_setReg, + RegUpd.gpr_arithFlags, reduceCtorEq, ite_true, ite_false, Option.map_some, + Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, trivial, trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr.1, hr.2.1, + hr.2.2.1, hr.2.2.2, ite_false] + all_goals rfl + +end VG.Proof.Argon2.X86_64.AddressCalls diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsCT.lean new file mode 100644 index 000000000..35a0234b3 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsCT.lean @@ -0,0 +1,94 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCalls +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressCallCT + +/-! The two address-generation compression calls have public fixed addresses. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCalls + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.AddressCalls + +structure Related (s t : State) : Prop where + left : Ready s + right : Ready t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + work : work s = work t + +structure CallRelated (s t : State) : Prop where + left : FillCompress.CallReady s + right : FillCompress.CallReady t + args : ∀ r ∈ [Reg.rdi, .rsi, .rdx, .rcx, .rsp], s.gpr r = t.gpr r + +theorem first_args_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block (args 7168 5120 4096)) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +theorem second_args_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block (args 7168 4096 6144)) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +theorem args_public_rel (x y out : Nat) + (hx : 4096 ≤ x) (hy : 4096 ≤ y) (ho : 4096 ≤ out) + (bx : x + 1024 ≤ 8192) (by_ : y + 1024 ≤ 8192) (bo : out + 1024 ≤ 8192) + (argTrace : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block (args x y out)) (fun _ _ => True)) : + RelCT isa Related (.block (args x y out)) CallRelated := by + have trace := argTrace.mono (P' := Related) (fun _ _ hp => hp.bases) (fun _ _ h => h) + have full := trace.wpDep (fun s t hp => + ⟨args_nat_ok s hp.left x y out (by omega) (by omega) (by omega), + args_nat_ok t hp.right x y out (by omega) (by omega) (by omega)⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + refine ⟨args_call_ready s a hp.left x y out hx hy ho bx by_ bo ha, + args_call_ready t b hp.right x y out hx hy ho bx by_ bo hb, ?_⟩ + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl + · exact ha.left.trans ((congrArg (fun p => off p x) hp.work).trans hb.left.symm) + · exact ha.right.trans ((congrArg (fun p => off p y) hp.work).trans hb.right.symm) + · exact ha.output.trans ((congrArg (fun p => off p out) hp.work).trans hb.output.symm) + · exact ha.scratch.trans (hp.work.trans hb.scratch.symm) + · exact (ha.keeps.regs .rsp (by decide)).trans + (hp.stacks.trans (hb.keeps.regs .rsp (by decide)).symm) + +theorem stage_rel (x y out : Nat) + (hx : 4096 ≤ x) (hy : 4096 ≤ y) (ho : 4096 ≤ out) + (bx : x + 1024 ≤ 8192) (by_ : y + 1024 ≤ 8192) (bo : out + 1024 ≤ 8192) + (argTrace : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block (args x y out)) (fun _ _ => True)) : + RelCT isa Related (stage x y out) Related := by + have call := FillCompress.call_rel Spec.Argon2.compressApi.name (P := CallRelated) + (fun _ _ hp => ⟨hp.left, hp.right, hp.args .rdi (by simp), hp.args .rsi (by simp), + hp.args .rdx (by simp), hp.args .rcx (by simp), hp.args .rsp (by simp)⟩) + have trace := (args_public_rel x y out hx hy ho bx by_ bo argTrace).seq call + have full := trace.wpDep (fun s t hp => + ⟨stage_ok s hp.left x y out hx hy ho bx by_ bo, + stage_ok t hp.right x y out hx hy ho bx by_ bo⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + exact ⟨ha.ready, hb.ready, + (ha.regs .rbp (by simp [calleeSaved])).trans + (hp.bases.trans (hb.regs .rbp (by simp [calleeSaved])).symm), + (ha.regs .rsp (by simp [calleeSaved])).trans + (hp.stacks.trans (hb.regs .rsp (by simp [calleeSaved])).symm), + ha.work.trans (hp.work.trans hb.work.symm)⟩ + +theorem calls_rel : RelCT isa Related calls Related := + (stage_rel 7168 5120 4096 (by decide) (by decide) (by decide) + (by decide) (by decide) (by decide) first_args_rel).seq + (stage_rel 7168 4096 6144 (by decide) (by decide) (by decide) + (by decide) (by decide) (by decide) second_args_rel) + +end VG.Proof.Argon2.X86_64.AddressCalls diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsClear.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsClear.lean new file mode 100644 index 000000000..33149882b --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsClear.lean @@ -0,0 +1,64 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCallsStage +import VerifiedGarbage.Proof.Argon2.X86_64.ClearBlock + +/-! Clear an address-generation block, retaining the allocation invariants. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCalls + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.AddressCalls + +structure Cleared (s t : State) (offset : Nat) : Prop where + block : blockAt t.mem (off (work s) offset) = zeroBlock + ready : Ready t + work_eq : work t = work s + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨off (work s) offset, 1024⟩] s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + +theorem clearAt_ok (s : State) (h : Ready s) (offset : Nat) (bound : offset + 1024 ≤ 8192) : + WP isa (clearAt offset) s (Cleared s · offset) := by + unfold clearAt + refine WP.seq ((pointer_ok s offset h.frameRead).mono ?_) + rintro a ⟨dest, keeps⟩ + rw [displacement_eq offset (by omega)] at dest + have write : Covers [⟨a.gpr .rdi, 1024⟩] a.wr := by + rw [dest, keeps.wr]; exact work_cover s h offset 1024 bound + refine (ClearBlock.code_ok a write).mono ?_ + rintro t ⟨zero, frame, tk, mx⟩ + have regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r := by + intro r hr + have ne : r ≠ .rax ∧ r ≠ .rdi := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + exact (tk.1 r ne.1).trans (keeps.regs r (by simpa only [List.mem_cons, List.not_mem_nil, or_false] using ne.2)) + have rd := tk.2.1.trans keeps.rd + have wr := tk.2.2.trans keeps.wr + have hf : Frame [⟨off (work s) offset, 1024⟩] s.mem t.mem := by + rw [dest, keeps.mem] at frame; exact frame + have bigger : Frame (stageWrites s offset) s.mem t.mem := + hf.mono (by + intro r hr + simp only [List.mem_singleton] at hr + subst r + simp [stageWrites]) + obtain ⟨ready, work'⟩ := ready_of_frame h offset bound regs rd wr bigger + rw [dest] at zero + exact ⟨zero, ready, work', regs, rd, wr, hf, mx.trans keeps.mxcsr⟩ + +theorem Cleared.full_frame {s t : State} {offset : Nat} (h : Cleared s t offset) + (bound : offset + 1024 ≤ 8192) : Frame [⟨work s, 8192⟩] s.mem t.mem := by + apply h.frame.sub + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact ⟨⟨work s, 8192⟩, by simp, Offset.sub_base _ bound⟩ + +theorem frame_word {s t : State} (h : Ready s) (frame : Frame [⟨work s, 8192⟩] s.mem t.mem) + (d : Nat) (bound : d + 8 ≤ 272) : + t.mem.readW (off (s.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := + frame.readW (r := ⟨s.gpr .rbp, 272⟩) (Offset.contains_base _ bound (by omega)) + (by intro r hr; simp only [List.mem_singleton] at hr; subst r; exact h.frameWork) (by decide) + +end VG.Proof.Argon2.X86_64.AddressCalls diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsLayout.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsLayout.lean new file mode 100644 index 000000000..81cc0442b --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsLayout.lean @@ -0,0 +1,79 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCallsArgs +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressCall + +/-! Permissions and separation for either address-generation compression call. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCalls + +open VG VG.X86_64 + +structure Ready (s : State) : Prop where + frameRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 248) 8 + workWrite : Covers [⟨work s, 8192⟩] s.wr + frameWork : (⟨s.gpr .rbp, 272⟩ : Region).Disjoint ⟨work s, 8192⟩ + frameStack : (⟨s.gpr .rbp, 272⟩ : Region).Disjoint (below (s.gpr .rsp) 8) + stackWork : (below (s.gpr .rsp) 8).Disjoint ⟨work s, 8192⟩ + +theorem displacement_eq (n : Nat) (bound : n ≤ 8192) : displacement n = BitVec.ofNat 64 n := by + have n32 : n < 2 ^ 32 := by omega + have msb : (BitVec.ofNat 32 n).msb = false := by + rw [BitVec.msb_eq_false_iff_two_mul_lt, BitVec.toNat_ofNat, Nat.mod_eq_of_lt n32] + omega + unfold displacement + rw [BitVec.signExtend_eq_setWidth_of_msb_false msb, + BitVec.setWidth_ofNat_of_le_of_lt (by decide) n32] + +theorem work_cover (s : State) (h : Ready s) (d n : Nat) (hd : d + n ≤ 8192) : + Covers [⟨off (work s) d, n⟩] s.wr := by + have sub : Covers [⟨off (work s) d, n⟩] [⟨work s, 8192⟩] := by + apply Covers.of_sub + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact ⟨⟨work s, 8192⟩, by simp, d, rfl, hd⟩ + exact fun p n hp => h.workWrite p n (sub p n hp) + +structure Args (s a : State) (x y out : Nat) : Prop where + scratch : a.gpr .rcx = work s + left : a.gpr .rdi = off (work s) x + right : a.gpr .rsi = off (work s) y + output : a.gpr .rdx = off (work s) out + keeps : Divide.Keeps [.rcx, .rdi, .rsi, .rdx] s a + +theorem args_nat_ok (s : State) (h : Ready s) (x y out : Nat) + (hx : x ≤ 8192) (hy : y ≤ 8192) (ho : out ≤ 8192) : + WP isa (.block (Impl.Argon2.X86_64.AddressCalls.args x y out)) s (Args s · x y out) := by + refine (args_ok s x y out h.frameRead).mono ?_ + rintro a ⟨scratch, left, right, output, keeps⟩ + rw [displacement_eq x hx] at left + rw [displacement_eq y hy] at right + rw [displacement_eq out ho] at output + exact ⟨scratch, left, right, output, keeps⟩ + +theorem args_call_ready (s a : State) (h : Ready s) (x y out : Nat) + (hx : 4096 ≤ x) (hy : 4096 ≤ y) (ho : 4096 ≤ out) + (bx : x + 1024 ≤ 8192) (by_ : y + 1024 ≤ 8192) (bo : out + 1024 ≤ 8192) + (args : Args s a x y out) : FillCompress.CallReady a := by + have read (d : Nat) (hd : d + 1024 ≤ 8192) : + Covers [⟨off (work s) d, 1024⟩] (a.rd ++ a.wr) := by + rw [args.keeps.rd, args.keeps.wr] + intro p n hp + obtain ⟨r, hr, hc⟩ := work_cover s h d 1024 hd p n hp + exact ⟨r, List.mem_append_right _ hr, hc⟩ + have sp : a.gpr .rsp = s.gpr .rsp := args.keeps.regs .rsp (by decide) + refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · rw [args.left]; exact read x bx + · rw [args.right]; exact read y by_ + · rw [args.output, args.keeps.wr]; exact work_cover s h out 1024 bo + · rw [args.scratch, args.keeps.wr] + simpa only [off, show BitVec.ofNat 64 0 = 0#64 from rfl, BitVec.add_zero] + using work_cover s h 0 4096 (by decide) + · rw [args.left, args.scratch]; exact Offset.disjoint_base _ hx (by omega) + · rw [args.right, args.scratch]; exact Offset.disjoint_base _ hy (by omega) + · rw [args.output, args.scratch]; exact Offset.disjoint_base _ ho (by omega) + · rw [sp, args.left]; exact h.stackWork.sub_right (Offset.sub_base _ bx) + · rw [sp, args.right]; exact h.stackWork.sub_right (Offset.sub_base _ by_) + · rw [sp, args.output]; exact h.stackWork.sub_right (Offset.sub_base _ bo) + · rw [sp, args.scratch]; exact h.stackWork.sub_right (Region.sub_prefix (by decide)) + +end VG.Proof.Argon2.X86_64.AddressCalls diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsMx.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsMx.lean new file mode 100644 index 000000000..faed3e606 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsMx.lean @@ -0,0 +1,32 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCalls + +/-! The baseline independent-address calls preserve all MXCSR bits. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCalls + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.AddressCalls + +theorem compression_noMx : VG.Impl.Argon2.X86_64.compress.allInstrs (fun i => !loadsMxcsr i) = true := + by lit_decide + +theorem stage_noMx (x y out : Nat) : (stage x y out).allInstrs (fun i => !loadsMxcsr i) = true := by + change ((Code.block (args x y out) : Prog isa).allInstrs (fun i => !loadsMxcsr i) && + VG.Impl.Argon2.X86_64.compress.allInstrs (fun i => !loadsMxcsr i)) = true + rw [compression_noMx] + rfl + +theorem calls_noMx : calls.allInstrs (fun i => !loadsMxcsr i) = true := by + change ((stage 7168 5120 4096).allInstrs (fun i => !loadsMxcsr i) && + (stage 7168 4096 6144).allInstrs (fun i => !loadsMxcsr i)) = true + rw [stage_noMx, stage_noMx] + rfl + +theorem calls_mx_ok (p : Spec.Argon2.Params) (pass lane slice counter : Nat) (s : State) (h : Ready s) + (zero : Spec.Argon2.blockAt s.mem (off (work s) 7168) = Spec.Argon2.zeroBlock) + (input : Spec.Argon2.blockAt s.mem (off (work s) 5120) = + Proof.Argon2.addressInput p pass lane slice counter) : + WP isa calls s fun t => Generated s t p pass lane slice counter ∧ t.mxcsr = s.mxcsr := + WP.mono_mx calls_noMx (calls_ok p pass lane slice counter s h zero input) + (fun _ generated mx => ⟨generated, mx⟩) + +end VG.Proof.Argon2.X86_64.AddressCalls diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsPrepare.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsPrepare.lean new file mode 100644 index 000000000..ef17576d0 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsPrepare.lean @@ -0,0 +1,159 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCallsClear +import VerifiedGarbage.Proof.Argon2.X86_64.AddressHeaderCorrect + +/-! Prepare the independent-address input and zero block from arbitrary scratch. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCalls + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.AddressCalls + +structure Stable (s t : State) : Prop where + ready : Ready t + work_eq : work t = work s + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨work s, 8192⟩] s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + +theorem Stable.trans {s a t : State} (h : Stable s a) (k : Stable a t) : Stable s t := by + have hf := k.frame + rw [h.work_eq] at hf + exact ⟨k.ready, k.work_eq.trans h.work_eq, fun r hr => (k.regs r hr).trans (h.regs r hr), + k.rd.trans h.rd, k.wr.trans h.wr, h.frame.trans hf, k.mxcsr.trans h.mxcsr⟩ + +theorem Cleared.stable {s t : State} {offset : Nat} (h : Cleared s t offset) + (bound : offset + 1024 ≤ 8192) : Stable s t := + ⟨h.ready, h.work_eq, h.regs, h.rd, h.wr, h.full_frame bound, h.mxcsr⟩ + +theorem stable_of_frame {s t : State} (h : Ready s) + (regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r) (rd : t.rd = s.rd) (wr : t.wr = s.wr) + (frame : Frame [⟨work s, 8192⟩] s.mem t.mem) (mx : t.mxcsr = s.mxcsr) : Stable s t := by + have bp := regs .rbp (by simp [calleeSaved]) + have sp := regs .rsp (by simp [calleeSaved]) + have work' : work t = work s := by + unfold work + rw [bp, frame_word h frame 248 (by decide)] + refine ⟨⟨?_, ?_, ?_, ?_, ?_⟩, work', regs, rd, wr, frame, mx⟩ + · rw [rd, wr, bp]; exact h.frameRead + · rw [work', wr]; exact h.workWrite + · rw [bp, work']; exact h.frameWork + · rw [bp, sp]; exact h.frameStack + · rw [sp, work']; exact h.stackWork + +theorem Stable.reads {s t : State} (h : Stable s t) + (reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8) : + ∀ d ∈ [0, 8, 72, 112, 240], InRegions (t.rd ++ t.wr) (off (t.gpr .rbp) d) 8 := by + rw [h.rd, h.wr, h.regs .rbp (by simp [calleeSaved])] + exact reads + +theorem Stable.words {s t : State} {p : Params} {pass lane slice counter : Nat} (ready : Ready s) (h : Stable s t) + (words : AddressHeader.Words p pass lane slice counter s) : + AddressHeader.Words p pass lane slice counter t := by + have bp := h.regs .rbp (by simp [calleeSaved]) + have read (d : Nat) (hd : d + 8 ≤ 272) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [bp]; exact frame_word ready h.frame d hd + exact ⟨(read 0 (by decide)).trans words.passWord, + (h.regs .rbx (by simp [calleeSaved])).trans words.laneWord, + (h.regs .r14 (by simp [calleeSaved])).trans words.sliceWord, + (read 240 (by decide)).trans words.blocksWord, + (read 72 (by decide)).trans words.passesWord, + (read 112 (by decide)).trans words.variantWord, + (read 8 (by decide)).trans words.counterWord⟩ + +theorem pointer_stable {s a : State} (h : Ready s) + (k : Divide.Keeps [.rdi] s a) : Stable s a := by + apply stable_of_frame h _ k.rd k.wr _ k.mxcsr + · intro r hr + apply k.regs + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + · rw [k.mem]; exact Frame.refl _ _ + +theorem Stable.input {s t : State} (ready : Ready s) (h : Stable s t) : + AddressHeader.input t = AddressHeader.input s := by + have value (i : Nat) (hi : i < 7) : AddressHeader.value t i = AddressHeader.value s i := by + unfold AddressHeader.value + rw [h.regs .rbx (by simp [calleeSaved]), h.regs .r14 (by simp [calleeSaved]), + h.regs .rbp (by simp [calleeSaved]), + frame_word ready h.frame (Impl.Argon2.X86_64.AddressHeader.frameOffset i) + (AddressHeader.offset_bound i hi)] + unfold AddressHeader.input + rw [value 0 (by decide), value 1 (by decide), value 2 (by decide), value 3 (by decide), + value 4 (by decide), value 5 (by decide), value 6 (by decide)] + +structure PreparedInput (s t : State) : Prop where + stable : Stable s t + zero : blockAt t.mem (off (work s) 7168) = zeroBlock + input : blockAt t.mem (off (work s) 5120) = AddressHeader.input s + +structure Prepared (s t : State) (p : Params) (pass lane slice counter : Nat) : Prop where + stable : Stable s t + zero : blockAt t.mem (off (work s) 7168) = zeroBlock + input : blockAt t.mem (off (work s) 5120) = Proof.Argon2.addressInput p pass lane slice counter + +theorem prepare_layout_ok (s : State) (h : Ready s) + (reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8) + : WP isa prepare s (PreparedInput s) := by + unfold prepare + refine WP.seq ((clearAt_ok s h 5120 (by decide)).mono ?_) + intro a inputClear + refine WP.seq ((clearAt_ok a inputClear.ready 7168 (by decide)).mono ?_) + intro b zeroClear + have stableB := (inputClear.stable (by decide)).trans (zeroClear.stable (by decide)) + have inputZero : blockAt b.mem (off (work s) 5120) = zeroBlock := by + have kept := FillCompress.block_frame zeroClear.frame (p := off (work s) 5120) (by + intro r hr + simp only [List.mem_singleton] at hr + subst r + rw [inputClear.work_eq] + exact Offset.disjoint _ (by decide) (by decide) (by decide)) + exact kept.trans inputClear.block + refine WP.seq ((pointer_ok b 5120 zeroClear.ready.frameRead).mono ?_) + rintro c ⟨dest, keeps⟩ + rw [displacement_eq 5120 (by decide)] at dest + have stableC := stableB.trans (pointer_stable zeroClear.ready keeps) + have dest' : c.gpr .rdi = off (work s) 5120 := by rw [dest, stableB.work_eq] + have write : Covers [⟨c.gpr .rdi, 1024⟩] c.wr := by + rw [dest, keeps.wr]; exact work_cover b zeroClear.ready 5120 1024 (by decide) + have sep : (⟨c.gpr .rbp, 272⟩ : Region).Disjoint ⟨c.gpr .rdi, 1024⟩ := by + rw [dest', stableC.regs .rbp (by simp [calleeSaved])] + exact h.frameWork.sub_right (Offset.sub_base _ (by decide)) + have zero : blockAt c.mem (c.gpr .rdi) = zeroBlock := by rw [dest', keeps.mem]; exact inputZero + refine (AddressHeader.code_ok c (stableC.reads reads) write sep zero).mono ?_ + rintro t ⟨input, frame, tk, mx⟩ + have regs : ∀ r ∈ calleeSaved, t.gpr r = c.gpr r := by + intro r hr + apply tk.1 + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + have frame' : Frame [⟨work c, 8192⟩] c.mem t.mem := by + apply frame.sub + intro r hr + simp only [List.mem_singleton] at hr + subst r + refine ⟨⟨work c, 8192⟩, by simp, ?_⟩ + rw [dest', stableC.work_eq] + exact Offset.sub_base _ (by decide) + have stableT := stableC.trans (stable_of_frame stableC.ready regs tk.2.1 tk.2.2 frame' mx) + refine ⟨stableT, ?_, ?_⟩ + · have kept := FillCompress.block_frame frame (p := off (work s) 7168) (by + intro r hr + simp only [List.mem_singleton] at hr + subst r + rw [dest'] + exact Offset.disjoint _ (by decide) (by decide) (by decide)) + rw [kept, keeps.mem, ← inputClear.work_eq] + exact zeroClear.block + · rw [dest'] at input + exact input.trans (stableC.input h) + +theorem prepare_ok (p : Params) (pass lane slice counter : Nat) (s : State) (h : Ready s) + (reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8) + (words : AddressHeader.Words p pass lane slice counter s) : + WP isa prepare s (Prepared s · p pass lane slice counter) := + (prepare_layout_ok s h reads).mono (fun _ k => + ⟨k.stable, k.zero, k.input.trans (AddressHeader.input_spec p pass lane slice counter s words)⟩) + +end VG.Proof.Argon2.X86_64.AddressCalls diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsStage.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsStage.lean new file mode 100644 index 000000000..a675ced48 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressCallsStage.lean @@ -0,0 +1,76 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCallsLayout +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressSetup + +/-! One verified G call within the independent-address scratch layout. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCalls + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.AddressCalls + +def stageWrites (s : State) (out : Nat) : List Region := + [⟨off (work s) out, 1024⟩, ⟨work s, 4096⟩, below (s.gpr .rsp) 8] + +structure StageDone (s t : State) (x y out : Nat) : Prop where + result : blockAt t.mem (off (work s) out) = Spec.Argon2.compress + (blockAt s.mem (off (work s) x)) (blockAt s.mem (off (work s) y)) + ready : Ready t + work : work t = work s + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (stageWrites s out) s.mem t.mem + +theorem Args.callee {s a : State} {x y out : Nat} (h : Args s a x y out) + (r : Reg) (hr : r ∈ calleeSaved) : a.gpr r = s.gpr r := by + apply h.keeps.regs + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + +theorem ready_of_frame {s t : State} (h : Ready s) (out : Nat) (ho : out + 1024 ≤ 8192) + (regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r) (rd : t.rd = s.rd) (wr : t.wr = s.wr) + (frame : Frame (stageWrites s out) s.mem t.mem) : Ready t ∧ work t = work s := by + have bp := regs .rbp (by simp [calleeSaved]) + have sp := regs .rsp (by simp [calleeSaved]) + have safe : ∀ r ∈ stageWrites s out, (⟨s.gpr .rbp, 272⟩ : Region).Disjoint r := by + intro r hr + simp only [stageWrites, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · exact h.frameWork.sub_right (Offset.sub_base _ ho) + · exact h.frameWork.sub_right (Region.sub_prefix (by decide)) + · exact h.frameStack + have read : t.mem.readW (off (s.gpr .rbp) 248) 64 = s.mem.readW (off (s.gpr .rbp) 248) 64 := + frame.readW (r := ⟨s.gpr .rbp, 272⟩) + (Offset.contains_base _ (by decide) (by decide)) safe (by decide) + have work' : work t = work s := by unfold work; rw [bp, read] + refine ⟨⟨?_, ?_, ?_, ?_, ?_⟩, work'⟩ + · rw [rd, wr, bp]; exact h.frameRead + · rw [work', wr]; exact h.workWrite + · rw [bp, work']; exact h.frameWork + · rw [bp, sp]; exact h.frameStack + · rw [sp, work']; exact h.stackWork + +theorem stage_ok (s : State) (h : Ready s) (x y out : Nat) + (hx : 4096 ≤ x) (hy : 4096 ≤ y) (ho : 4096 ≤ out) + (bx : x + 1024 ≤ 8192) (by_ : y + 1024 ≤ 8192) (bo : out + 1024 ≤ 8192) : + WP isa (stage x y out) s (StageDone s · x y out) := by + unfold stage + refine WP.seq ((args_nat_ok s h x y out (by omega) (by omega) (by omega)).mono ?_) + intro a args + have callReady := args_call_ready s a h x y out hx hy ho bx by_ bo args + refine (FillCompress.call_ok _ a callReady).mono ?_ + intro t called + have regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r := + fun r hr => (called.regs r hr).trans (args.callee r hr) + have rd := called.rd.trans args.keeps.rd + have wr := called.wr.trans args.keeps.wr + have frame : Frame (stageWrites s out) s.mem t.mem := by + have hf := called.frame + rw [args.output, args.scratch, args.callee .rsp (by simp [calleeSaved]), args.keeps.mem] at hf + exact hf + obtain ⟨ready, work'⟩ := ready_of_frame h out bo regs rd wr frame + refine ⟨?_, ready, work', regs, rd, wr, frame⟩ + have result := called.result + rw [args.output, args.left, args.right, args.keeps.mem] at result + exact result + +end VG.Proof.Argon2.X86_64.AddressCalls diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressGeneration.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressGeneration.lean new file mode 100644 index 000000000..ad97eb8d3 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressGeneration.lean @@ -0,0 +1,39 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCallsPrepare +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCallsMx + +/-! Complete independent-address generation against the reviewed algorithm. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCalls + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.AddressCalls + +theorem code_ok (p : Params) (pass lane slice counter : Nat) (s : State) (h : Ready s) + (reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8) + (words : AddressHeader.Words p pass lane slice counter s) : + WP isa code s fun t => Generated s t p pass lane slice counter ∧ t.mxcsr = s.mxcsr := by + unfold code + refine WP.seq ((prepare_ok p pass lane slice counter s h reads words).mono ?_) + intro a prepared + have zero : blockAt a.mem (off (work a) 7168) = zeroBlock := by + rw [prepared.stable.work_eq]; exact prepared.zero + have input : blockAt a.mem (off (work a) 5120) = Proof.Argon2.addressInput p pass lane slice counter := by + rw [prepared.stable.work_eq]; exact prepared.input + refine (calls_mx_ok p pass lane slice counter a prepared.stable.ready zero input).mono ?_ + rintro t ⟨generated, mx⟩ + have frame := generated.frame + rw [writes, prepared.stable.work_eq, prepared.stable.regs .rsp (by simp [calleeSaved])] at frame + have firstFrame : Frame (writes s) s.mem a.mem := + prepared.stable.frame.mono (by + intro r hr + simp only [List.mem_singleton] at hr + subst r + simp [writes]) + refine ⟨⟨?_, generated.ready, generated.work.trans prepared.stable.work_eq, + fun r hr => (generated.regs r hr).trans (prepared.stable.regs r hr), + generated.rd.trans prepared.stable.rd, generated.wr.trans prepared.stable.wr, + firstFrame.trans frame⟩, mx.trans prepared.stable.mxcsr⟩ + have block := generated.block + rw [prepared.stable.work_eq] at block + exact block + +end VG.Proof.Argon2.X86_64.AddressCalls diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressGenerationCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressGenerationCT.lean new file mode 100644 index 000000000..fe611aec5 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressGenerationCT.lean @@ -0,0 +1,95 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCallsCT +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCallsPrepare +import VerifiedGarbage.Proof.Argon2.X86_64.AddressInputCT + +/-! Independent-address generation keeps its entire trace independent of secrets. -/ + +namespace VG.Proof.Argon2.X86_64.AddressCalls + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.AddressCalls + +theorem Related.of_stable {s t a b : State} (h : Related s t) + (ha : Stable s a) (hb : Stable t b) : Related a b := + ⟨ha.ready, hb.ready, + (ha.regs .rbp (by simp [calleeSaved])).trans + (h.bases.trans (hb.regs .rbp (by simp [calleeSaved])).symm), + (ha.regs .rsp (by simp [calleeSaved])).trans + (h.stacks.trans (hb.regs .rsp (by simp [calleeSaved])).symm), + ha.work_eq.trans (h.work.trans hb.work_eq.symm)⟩ + +theorem input_pointer_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block (pointer 5120)) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +theorem zero_pointer_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block (pointer 7168)) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +structure PointRelated (s t : State) : Prop where + related : Related s t + pointer : s.gpr .rdi = t.gpr .rdi + +theorem pointer_public_rel (offset : Nat) + (trace : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block (pointer offset)) (fun _ _ => True)) : + RelCT isa Related (.block (pointer offset)) PointRelated := by + have publicTrace := trace.mono (P' := Related) (fun _ _ h => h.bases) (fun _ _ h => h) + have full := publicTrace.wpDep (fun s t h => + ⟨pointer_ok s offset h.left.frameRead, pointer_ok t offset h.right.frameRead⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ⟨pa, ka⟩, ⟨pb, kb⟩⟩ := h + exact ⟨hp.of_stable (pointer_stable hp.left ka) (pointer_stable hp.right kb), + pa.trans ((congrArg (· + displacement offset) hp.work).trans pb.symm)⟩ + +theorem clearAt_rel (offset : Nat) (bound : offset + 1024 ≤ 8192) + (trace : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block (pointer offset)) (fun _ _ => True)) : + RelCT isa Related (clearAt offset) Related := by + have clear := ClearBlock.code_rel.mono (P' := PointRelated) + (fun _ _ h => h.pointer) (fun _ _ h => h) + have blocks := (pointer_public_rel offset trace).seq clear + have full := blocks.wpDep (fun s t h => + ⟨clearAt_ok s h.left offset bound, clearAt_ok t h.right offset bound⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + exact hp.of_stable (ha.stable bound) (hb.stable bound) + +structure PrepareRelated (s t : State) : Prop where + related : Related s t + leftReads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8 + rightReads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (t.rd ++ t.wr) (off (t.gpr .rbp) d) 8 + +theorem prepare_rel : RelCT isa PrepareRelated prepare Related := by + have header := AddressHeader.code_rel.mono (P' := PointRelated) (by + intro s t h r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact h.pointer + · exact h.related.bases) (fun _ _ h => h) + have trace := (clearAt_rel 5120 (by decide) input_pointer_rel).seq + ((clearAt_rel 7168 (by decide) zero_pointer_rel).seq + ((pointer_public_rel 5120 input_pointer_rel).seq header)) + have narrowed := trace.mono (P' := PrepareRelated) (fun _ _ h => h.related) (fun _ _ h => h) + have full := narrowed.wpDep (fun s t h => + ⟨prepare_layout_ok s h.related.left h.leftReads, + prepare_layout_ok t h.related.right h.rightReads⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + exact hp.related.of_stable ha.stable hb.stable + +theorem code_rel : RelCT isa PrepareRelated code Related := prepare_rel.seq calls_rel + +end VG.Proof.Argon2.X86_64.AddressCalls diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeader.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeader.lean new file mode 100644 index 000000000..3d8cc32cb --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeader.lean @@ -0,0 +1,100 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressHeaderWords +import VerifiedGarbage.Proof.Framework.X86_64.Inline + +/-! Compose the seven input fields, preserving frame reads across every write. -/ + +namespace VG.Proof.Argon2.X86_64.AddressHeader + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.AddressHeader + +def value (s : State) (i : Nat) : Addr := + if i = 1 then s.gpr .rbx else if i = 2 then s.gpr .r14 + else s.mem.readW (off (s.gpr .rbp) (frameOffset i)) 64 + +def headerMem (s : State) (p : Addr) : Nat → Mem + | 0 => s.mem + | n + 1 => (headerMem s p n).writeW (off p (8 * n)) (value s n) + +theorem field_ok (s : State) (i : Nat) + (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) (frameOffset i)) 8) + (hw : InRegions s.wr (off (s.gpr .rdi) (8 * i)) 8) : + WP isa (.block (field i)) s fun t => + t.mem = s.mem.writeW (off (s.gpr .rdi) (8 * i)) (value s i) ∧ + CopyKeeps s t ∧ t.mxcsr = s.mxcsr := by + unfold field value + by_cases one : i = 1 + · simp only [one, ite_true] + refine (registerWord_ok s 1 .rbx (one ▸ hw)).mono ?_ + rintro t ⟨mem, regs, rd, wr, mx⟩ + exact ⟨mem, ⟨fun r _ => congrFun regs r, rd, wr⟩, mx⟩ + · simp only [one, ite_false] + by_cases two : i = 2 + · simp only [two, ite_true] + refine (registerWord_ok s 2 .r14 (two ▸ hw)).mono ?_ + rintro t ⟨mem, regs, rd, wr, mx⟩ + exact ⟨mem, ⟨fun r _ => congrFun regs r, rd, wr⟩, mx⟩ + · simp only [two, ite_false] + refine (frameWord_ok s i (frameOffset i) hr hw).mono ?_ + rintro t ⟨mem, regs, rd, wr, mx⟩ + exact ⟨mem, ⟨regs, rd, wr⟩, mx⟩ + +theorem offset_bound : ∀ i < 7, frameOffset i + 8 ≤ 272 := by decide +kernel + +theorem offset_read (s : State) (i : Nat) + (reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8) : + InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) (frameOffset i)) 8 := by + apply reads + unfold frameOffset + split <;> [simp; skip] + split <;> [simp; skip] + split <;> [simp; skip] + split <;> simp + +theorem value_kept {s t : State} (keeps : CopyKeeps s t) + (hf : Frame [⟨s.gpr .rdi, 1024⟩] s.mem t.mem) + (sep : (⟨s.gpr .rbp, 272⟩ : Region).Disjoint ⟨s.gpr .rdi, 1024⟩) + (i : Nat) (hi : i < 7) : value t i = value s i := by + unfold value + rw [keeps.1 .rbx (by decide), keeps.1 .r14 (by decide), keeps.1 .rbp (by decide)] + have read : t.mem.readW (off (s.gpr .rbp) (frameOffset i)) 64 = + s.mem.readW (off (s.gpr .rbp) (frameOffset i)) 64 := + hf.readW (r := ⟨s.gpr .rbp, 272⟩) + (Offset.contains_base _ (offset_bound i hi) + (Nat.lt_of_le_of_lt (Nat.le_trans (Nat.le_add_right _ _) (offset_bound i hi)) (by decide))) + (by intro r hr; simp only [List.mem_singleton] at hr; subst r; exact sep) (by decide) + rw [read] + +theorem prefix_ok (n : Nat) (hn : n ≤ 7) (s : State) + (reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8) + (write : Covers [⟨s.gpr .rdi, 1024⟩] s.wr) + (sep : (⟨s.gpr .rbp, 272⟩ : Region).Disjoint ⟨s.gpr .rdi, 1024⟩) : + WP isa (.block (fields n)) s fun t => + t.mem = headerMem s (s.gpr .rdi) n ∧ + Frame [⟨s.gpr .rdi, 1024⟩] s.mem t.mem ∧ CopyKeeps s t ∧ t.mxcsr = s.mxcsr := by + induction n with + | zero => exact WP.block_nil ⟨rfl, Frame.refl _ _, CopyKeeps.refl s, rfl⟩ + | succ n ih => + simp only [fields, List.range_succ, List.flatMap_append, List.flatMap_cons, + List.flatMap_nil, List.append_nil] + apply WP.block_append + refine (ih (by omega)).mono ?_ + rintro a ⟨mem, frame, keeps, mx⟩ + have dest : a.gpr .rdi = s.gpr .rdi := keeps.1 .rdi (by decide) + have read : InRegions (a.rd ++ a.wr) (off (a.gpr .rbp) (frameOffset n)) 8 := by + rw [keeps.2.1, keeps.2.2, keeps.1 .rbp (by decide)] + exact offset_read s n reads + have writable : InRegions a.wr (off (a.gpr .rdi) (8 * n)) 8 := by + rw [dest, keeps.2.2] + exact write _ _ ⟨⟨s.gpr .rdi, 1024⟩, by simp, + Offset.contains_base _ (d := 8 * n) (n := 8) (k := 1024) (by omega) (by omega)⟩ + refine (field_ok a n read writable).mono ?_ + rintro t ⟨mem', keeps', mx'⟩ + have value' := value_kept keeps frame sep n (by omega) + refine ⟨?_, ?_, keeps.trans keeps', mx'.trans mx⟩ + · rw [mem', dest, value', mem] + rfl + · rw [mem', dest] + exact frame.writeW (r := ⟨s.gpr .rdi, 1024⟩) (by simp) _ + (Offset.contains_base _ (by omega) (by omega)) + +end VG.Proof.Argon2.X86_64.AddressHeader diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeaderCorrect.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeaderCorrect.lean new file mode 100644 index 000000000..9bd6730f4 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeaderCorrect.lean @@ -0,0 +1,64 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressHeader +import VerifiedGarbage.Proof.Argon2.AddressInput + +/-! The prepared input agrees with RFC 9106's seven public address words. -/ + +namespace VG.Proof.Argon2.X86_64.AddressHeader + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.AddressHeader + +def input (s : State) : Block := + zeroBlock |>.set 0 (value s 0) |>.set 1 (value s 1) |>.set 2 (value s 2) + |>.set 3 (value s 3) |>.set 4 (value s 4) |>.set 5 (value s 5) |>.set 6 (value s 6) + +theorem headerMem_block (s : State) (p : Addr) (zero : blockAt s.mem p = zeroBlock) : + blockAt (headerMem s p 7) p = input s := by + rw [headerMem, blockAt_write_nat _ p 6 (by decide), + headerMem, blockAt_write_nat _ p 5 (by decide), + headerMem, blockAt_write_nat _ p 4 (by decide), + headerMem, blockAt_write_nat _ p 3 (by decide), + headerMem, blockAt_write_nat _ p 2 (by decide), + headerMem, blockAt_write_nat _ p 1 (by decide), + headerMem, blockAt_write_nat _ p 0 (by decide), headerMem, zero] + rfl + +theorem code_ok (s : State) + (reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8) + (write : Covers [⟨s.gpr .rdi, 1024⟩] s.wr) + (sep : (⟨s.gpr .rbp, 272⟩ : Region).Disjoint ⟨s.gpr .rdi, 1024⟩) + (zero : blockAt s.mem (s.gpr .rdi) = zeroBlock) : + WP isa code s fun t => blockAt t.mem (s.gpr .rdi) = input s ∧ + Frame [⟨s.gpr .rdi, 1024⟩] s.mem t.mem ∧ CopyKeeps s t ∧ t.mxcsr = s.mxcsr := by + refine (prefix_ok 7 (by decide) s reads write sep).mono ?_ + rintro t ⟨mem, frame, keeps, mx⟩ + exact ⟨by rw [mem]; exact headerMem_block s _ zero, frame, keeps, mx⟩ + +structure Words (p : Params) (pass lane slice counter : Nat) (s : State) : Prop where + passWord : s.mem.readW (off (s.gpr .rbp) 0) 64 = BitVec.ofNat 64 pass + laneWord : s.gpr .rbx = BitVec.ofNat 64 lane + sliceWord : s.gpr .r14 = BitVec.ofNat 64 slice + blocksWord : s.mem.readW (off (s.gpr .rbp) 240) 64 = BitVec.ofNat 64 p.blocks + passesWord : s.mem.readW (off (s.gpr .rbp) 72) 64 = BitVec.ofNat 64 p.passes + variantWord : s.mem.readW (off (s.gpr .rbp) 112) 64 = BitVec.ofNat 64 p.variant.code + counterWord : s.mem.readW (off (s.gpr .rbp) 8) 64 = BitVec.ofNat 64 counter + +theorem input_spec (p : Params) (pass lane slice counter : Nat) (s : State) + (h : Words p pass lane slice counter s) : + input s = Proof.Argon2.addressInput p pass lane slice counter := by + simp (config := {decide := true}) only [input, value, frameOffset, + ite_true, ite_false, h.passWord, h.laneWord, h.sliceWord, h.blocksWord, + h.passesWord, h.variantWord, h.counterWord, Proof.Argon2.addressInput] + +theorem code_spec_ok (p : Params) (pass lane slice counter : Nat) (s : State) + (reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8) + (write : Covers [⟨s.gpr .rdi, 1024⟩] s.wr) + (sep : (⟨s.gpr .rbp, 272⟩ : Region).Disjoint ⟨s.gpr .rdi, 1024⟩) + (zero : blockAt s.mem (s.gpr .rdi) = zeroBlock) + (words : Words p pass lane slice counter s) : + WP isa code s fun t => + blockAt t.mem (s.gpr .rdi) = Proof.Argon2.addressInput p pass lane slice counter ∧ + Frame [⟨s.gpr .rdi, 1024⟩] s.mem t.mem ∧ CopyKeeps s t ∧ t.mxcsr = s.mxcsr := + (code_ok s reads write sep zero).mono (fun _ h => + ⟨h.1.trans (input_spec p pass lane slice counter s words), h.2⟩) + +end VG.Proof.Argon2.X86_64.AddressHeader diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeaderLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeaderLit.lean new file mode 100644 index 000000000..8a55ac4cc --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeaderLit.lean @@ -0,0 +1,10 @@ +import VerifiedGarbage.Proof.Framework.X86_64.Lit +import VerifiedGarbage.Impl.Argon2.X86_64.AddressHeader + +/-! Checked literal of the independent-address input header. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.AddressHeader.code + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeaderWords.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeaderWords.lean new file mode 100644 index 000000000..ed964636e --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressHeaderWords.lean @@ -0,0 +1,38 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.AddressHeader +import VerifiedGarbage.Proof.Argon2.X86_64.BlockStore +import VerifiedGarbage.Proof.Argon2.X86_64.DivideStep + +/-! Short independent-address input header writes. -/ + +namespace VG.Proof.Argon2.X86_64.AddressHeader + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.AddressHeader + +theorem registerWord_ok (s : State) (i : Nat) (r : Reg) + (hw : InRegions s.wr (off (s.gpr .rdi) (8 * i)) 8) : + WP isa (.block (registerWord i r)) s fun t => + t.mem = s.mem.writeW (off (s.gpr .rdi) (8 * i)) (s.gpr r) ∧ + t.gpr = s.gpr ∧ t.rd = s.rd ∧ t.wr = s.wr ∧ t.mxcsr = s.mxcsr := by + apply WP.of_runBlock + simp only [registerWord, runBlock_cons, runStep_some, runBlock_nil, exec, + State.store64, ea_at, hw, ite_true, Option.some.injEq, exists_eq_left'] + exact ⟨trivial, trivial, trivial, trivial, trivial⟩ + +theorem frameWord_ok (s : State) (i offset : Nat) + (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) offset) 8) + (hw : InRegions s.wr (off (s.gpr .rdi) (8 * i)) 8) : + WP isa (.block (frameWord i offset)) s fun t => + t.mem = s.mem.writeW (off (s.gpr .rdi) (8 * i)) + (s.mem.readW (off (s.gpr .rbp) offset) 64) ∧ + (∀ r, r ≠ .rax → t.gpr r = s.gpr r) ∧ + t.rd = s.rd ∧ t.wr = s.wr ∧ t.mxcsr = s.mxcsr := by + apply WP.of_runBlock + simp only [frameWord, runBlock_cons, runStep_some, runBlock_nil, exec, + readSrc, State.load64, State.store64, ea_at, hr, hw, + RegUpd.gpr_setReg, RegUpd.mem_setReg, RegUpd.rd_setReg, RegUpd.wr_setReg, + reduceCtorEq, ite_true, ite_false, Option.map_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, ?_, trivial, trivial, rfl⟩ + intro r hr + simp only [hr, ite_false] + +end VG.Proof.Argon2.X86_64.AddressHeader diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressInputCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressInputCT.lean new file mode 100644 index 000000000..08d706d40 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressInputCT.lean @@ -0,0 +1,26 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ClearBlockLit +import VerifiedGarbage.Proof.Argon2.X86_64.AddressHeaderLit +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! Clearing and header preparation visit fixed offsets of public pointers. -/ + +namespace VG.Proof.Argon2.X86_64 + +open VG VG.X86_64 + +theorem ClearBlock.code_rel : RelCT isa (fun s t => s.gpr .rdi = t.gpr .rdi) + Impl.Argon2.X86_64.ClearBlock.code (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rdi]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +theorem AddressHeader.code_rel : RelCT isa + (fun s t => ∀ r ∈ [Reg.rdi, .rbp], s.gpr r = t.gpr r) + Impl.Argon2.X86_64.AddressHeader.code (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rdi, .rbp]) + (fun _ _ h => Taint.agree_ofRegs h) (by taint_decide) + +end VG.Proof.Argon2.X86_64 diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressMode.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressMode.lean new file mode 100644 index 000000000..80bf96752 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressMode.lean @@ -0,0 +1,73 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressModeSteps +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapState + +/-! The segment mode is exactly the reviewed Argon2d/i/id addressing predicate. -/ + +namespace VG.Proof.Argon2.X86_64.AddressMode + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.AddressMode + +def value (s : State) : Addr := + let kind := s.mem.readW (off (s.gpr .rbp) 112) 64 + let pass := s.mem.readW (off (s.gpr .rbp) 0) 64 + (Divide.mask (decide (kind = 1)) ||| ((Divide.mask (decide (kind = 2)) &&& + Divide.mask (decide (pass = 0#64))) &&& Divide.mask (decide ((s.gpr .r14).toNat < 2)))) &&& 1 + +def changed : List Reg := [.rax, .r8, .r9, .r10, .r11] + +theorem code_ok (s : State) + (kindRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 112) 8) + (passRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 0) 8) : + WP isa code s fun t => t.gpr .r10 = value s ∧ Divide.Keeps changed s t := by + unfold code + refine WP.seq ((kind_ok s kindRead).mono ?_) + rintro a ⟨i, id, ka⟩ + have read : InRegions (a.rd ++ a.wr) (off (a.gpr .rbp) 0) 8 := by + rw [ka.rd, ka.wr, ka.regs .rbp (by decide)]; exact passRead + refine WP.seq ((pass_ok a read).mono ?_) + rintro b ⟨zero, kb⟩ + refine (slice_ok b).mono ?_ + rintro t ⟨result, kt⟩ + refine ⟨?_, ((ka.mono (by decide)).trans (kb.mono (by decide))).trans (kt.mono (by decide))⟩ + rw [result, kb.regs .r10 (by decide), i, kb.regs .r8 (by decide), id, zero, + ka.mem, ka.regs .rbp (by decide), kb.regs .r14 (by decide), ka.regs .r14 (by decide)] + rfl + +theorem masks : ∀ a b c d : Bool, + (Divide.mask a ||| ((Divide.mask b &&& Divide.mask c) &&& Divide.mask d)) &&& 1 = + (BitVec.ofBool (a || (b && c && d))).setWidth 64 := by decide +kernel + +theorem variants : ∀ v : Spec.Argon2.Variant, ∀ a b : Bool, + (decide (BitVec.ofNat 64 v.code = (1 : Addr)) || + (decide (BitVec.ofNat 64 v.code = (2 : Addr)) && a && b)) = + ((v == .i) || ((v == .id) && a && b)) := by + intro v + cases v <;> decide +kernel + +theorem value_spec (s : State) (p : Spec.Argon2.Params) (pass slice : Nat) + (kindWord : s.mem.readW (off (s.gpr .rbp) 112) 64 = BitVec.ofNat 64 p.variant.code) + (passWord : s.mem.readW (off (s.gpr .rbp) 0) 64 = BitVec.ofNat 64 pass) + (sliceWord : s.gpr .r14 = BitVec.ofNat 64 slice) + (passBound : pass < 2 ^ 64) (sliceBound : slice < 2 ^ 64) : + value s = (BitVec.ofBool (Spec.Argon2.independent p pass slice)).setWidth 64 := by + have passZero : BitVec.ofNat 64 pass = 0#64 ↔ pass = 0 := ReferenceMap.word_zero pass passBound + unfold value + rw [kindWord, passWord, sliceWord, masks, + ReferenceMap.word_nat slice sliceBound] + simp only [passZero] + unfold Spec.Argon2.independent + rw [variants, Bool.beq_eq_decide_eq pass 0] + +theorem code_spec_ok (s : State) (p : Spec.Argon2.Params) (pass slice : Nat) + (kindRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 112) 8) + (passRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 0) 8) + (kindWord : s.mem.readW (off (s.gpr .rbp) 112) 64 = BitVec.ofNat 64 p.variant.code) + (passWord : s.mem.readW (off (s.gpr .rbp) 0) 64 = BitVec.ofNat 64 pass) + (sliceWord : s.gpr .r14 = BitVec.ofNat 64 slice) + (passBound : pass < 2 ^ 64) (sliceBound : slice < 2 ^ 64) : + WP isa code s fun t => t.gpr .r10 = + (BitVec.ofBool (Spec.Argon2.independent p pass slice)).setWidth 64 ∧ Divide.Keeps changed s t := + (code_ok s kindRead passRead).mono (fun _ h => + ⟨h.1.trans (value_spec s p pass slice kindWord passWord sliceWord passBound sliceBound), h.2⟩) + +end VG.Proof.Argon2.X86_64.AddressMode diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressModeCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressModeCT.lean new file mode 100644 index 000000000..226a8e8c4 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressModeCT.lean @@ -0,0 +1,18 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.AddressModeLit +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! Mode selection has a fixed trace at the public frame base. -/ + +namespace VG.Proof.Argon2.X86_64.AddressMode + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.AddressMode + +theorem code_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) code (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +end VG.Proof.Argon2.X86_64.AddressMode diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressModeLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressModeLit.lean new file mode 100644 index 000000000..f5791234d --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressModeLit.lean @@ -0,0 +1,10 @@ +import VerifiedGarbage.Proof.Framework.X86_64.Lit +import VerifiedGarbage.Impl.Argon2.X86_64.AddressMode + +/-! Checked literal of the segment addressing-mode computation. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.AddressMode.code + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressModeSteps.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressModeSteps.lean new file mode 100644 index 000000000..6b7089bcc --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/AddressModeSteps.lean @@ -0,0 +1,76 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.AddressMode +import VerifiedGarbage.Proof.Argon2.X86_64.Memory +import VerifiedGarbage.Proof.Argon2.X86_64.DivideStep + +/-! Short mask computations for the public addressing-mode predicate. -/ + +namespace VG.Proof.Argon2.X86_64.AddressMode + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.AddressMode + +theorem zero_nat (x : Addr) : x.toNat < 1 ↔ x = 0#64 := by + constructor + · intro h; exact BitVec.eq_of_toNat_eq (Nat.lt_one_iff.mp h) + · intro h; rw [h]; decide + +theorem xor_nat (x y : Addr) : (x ^^^ y).toNat < 1 ↔ x = y := by + rw [zero_nat, BitVec.xor_eq_zero_iff] + +theorem kind_ok (s : State) (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 112) 8) : + WP isa (.block kind) s fun t => + t.gpr .r10 = Divide.mask (decide (s.mem.readW (off (s.gpr .rbp) 112) 64 = 1)) ∧ + t.gpr .r8 = Divide.mask (decide (s.mem.readW (off (s.gpr .rbp) 112) 64 = 2)) ∧ + Divide.Keeps [.rax, .r10, .r8] s t := by + apply WP.of_runBlock + simp only [kind, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, State.load64, + ea_at, hr, execAlu, RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, + RegUpd.cf_arithFlags, reduceCtorEq, ite_true, ite_false, + show BitVec.signExtend 64 (1 : BitVec 32) = (1 : Addr) from rfl, + show BitVec.signExtend 64 (2 : BitVec 32) = (2 : Addr) from rfl, + Option.map_some, Option.bind_some, Option.some.injEq, + exists_eq_left', Divide.sbb_mask, show (1 : Addr).toNat = 1 from rfl, xor_nat] + refine ⟨trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr.1, hr.2.1, hr.2.2, ite_false] + all_goals rfl + +theorem pass_ok (s : State) (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 0) 8) : + WP isa (.block pass) s fun t => + t.gpr .r9 = Divide.mask (decide (s.mem.readW (off (s.gpr .rbp) 0) 64 = 0#64)) ∧ + Divide.Keeps [.r9] s t := by + apply WP.of_runBlock + simp only [pass, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, State.load64, + ea_at, hr, execAlu, RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, + RegUpd.cf_arithFlags, ite_true, + show BitVec.signExtend 64 (1 : BitVec 32) = (1 : Addr) from rfl, + Option.map_some, Option.bind_some, Option.some.injEq, + exists_eq_left', Divide.sbb_mask, show (1 : Addr).toNat = 1 from rfl, zero_nat] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr, ite_false] + all_goals rfl + +theorem slice_ok (s : State) : WP isa (.block slice) s fun t => + t.gpr .r10 = (s.gpr .r10 ||| ((s.gpr .r8 &&& s.gpr .r9) &&& + Divide.mask (decide ((s.gpr .r14).toNat < 2)))) &&& 1 ∧ + Divide.Keeps [.r8, .r11, .r10] s t := by + apply WP.of_runBlock + simp only [slice, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, execAlu, + RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, RegUpd.cf_arithFlags, + reduceCtorEq, ite_true, ite_false, + show BitVec.signExtend 64 (1 : BitVec 32) = (1 : Addr) from rfl, + show BitVec.signExtend 64 (2 : BitVec 32) = (2 : Addr) from rfl, + Option.map_some, Option.bind_some, Option.some.injEq, + exists_eq_left', Divide.sbb_mask, show (2 : Addr).toNat = 2 from rfl] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr.1, hr.2.1, hr.2.2, ite_false] + all_goals rfl + +end VG.Proof.Argon2.X86_64.AddressMode diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/BlockAddress.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/BlockAddress.lean new file mode 100644 index 000000000..66b63e638 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/BlockAddress.lean @@ -0,0 +1,81 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.BlockAddress +import VerifiedGarbage.Proof.Argon2.X86_64.DivideStep +import VerifiedGarbage.Proof.Argon2.X86_64.MemoryInitScale +import VerifiedGarbage.Proof.Framework.X86_64.Abi +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! Fault-free matrix pointer calculation, with no memory accesses. -/ + +namespace VG.Proof.Argon2.X86_64.BlockAddress + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.BlockAddress + +theorem flatten_ok (s : State) : WP isa (.block flatten) s fun t => + t.gpr .rax = s.gpr .rax * s.gpr .r12 + s.gpr .rcx ∧ + Divide.Keeps [.rax, .rdx] s t := by + apply WP.of_runBlock + simp only [flatten, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + execMul, execAlu, RegUpd.gpr_setReg, RegUpd.gpr_setFlags, RegUpd.gpr_arithFlags, + reduceCtorEq, ite_true, ite_false, Option.bind_some, Option.some.injEq, + exists_eq_left', BitVec.ofNat_mul, BitVec.ofNat_toNat, BitVec.setWidth_eq] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_setFlags, RegUpd.gpr_arithFlags, + hr.1, hr.2, ite_false] + all_goals rfl + +theorem scale_ok (s : State) : WP isa (.block scale) s fun t => + t.gpr .rax = s.gpr .rax * 1024 ∧ Divide.Keeps [.rax] s t := by + refine WP.mono_mx (by decide +kernel) (MemoryInit.scale_ok s .rax 10) ?_ + intro t h mx + refine ⟨h.value, ?_, h.mem, h.rd, h.wr, mx⟩ + intro r hr + exact h.other r (by simpa only [List.mem_cons, List.not_mem_nil, or_false] using hr) + +theorem base_ok (s : State) : WP isa (.block [.alu .add .rax (.reg .r8)]) s fun t => + t.gpr .rax = s.gpr .rax + s.gpr .r8 ∧ Divide.Keeps [.rax] s t := by + apply WP.of_runBlock + simp only [runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, execAlu, + RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, Option.bind_some, + Option.some.injEq, exists_eq_left'] + refine ⟨by simp only [ite_true], ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + exact ite_eq_right hr + all_goals rfl + +theorem code_ok (s : State) : WP isa code s fun t => + t.gpr .rax = (s.gpr .rax * s.gpr .r12 + s.gpr .rcx) * 1024 + s.gpr .r8 ∧ + Divide.Keeps [.rax, .rdx] s t := by + unfold code + refine WP.seq ((flatten_ok s).mono ?_) + rintro a ⟨flat, ka⟩ + refine WP.seq ((scale_ok a).mono ?_) + rintro b ⟨scaled, kb⟩ + refine (base_ok b).mono ?_ + rintro t ⟨result, kt⟩ + refine ⟨?_, ka.trans ((kb.mono (by simp)).trans (kt.mono (by simp)))⟩ + rw [result, scaled, flat, kb.regs .r8 (by decide), ka.regs .r8 (by decide)] + +theorem code_nat_ok (s : State) (lane column q : Nat) + (hl : s.gpr .rax = BitVec.ofNat 64 lane) + (hc : s.gpr .rcx = BitVec.ofNat 64 column) + (hq : s.gpr .r12 = BitVec.ofNat 64 q) : + WP isa code s fun t => + t.gpr .rax = s.gpr .r8 + BitVec.ofNat 64 ((lane * q + column) * 1024) ∧ + Divide.Keeps [.rax, .rdx] s t := by + refine (code_ok s).mono ?_ + rintro t ⟨h, k⟩ + refine ⟨?_, k⟩ + rw [h, hl, hc, hq, ← BitVec.ofNat_mul, ← BitVec.ofNat_add] + change BitVec.ofNat 64 (lane * q + column) * BitVec.ofNat 64 1024 + s.gpr .r8 = _ + rw [← BitVec.ofNat_mul, BitVec.add_comm] + +theorem code_secret_rel : RelCT isa (fun _ _ => True) code (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by simp)) (by taint_decide) + +end VG.Proof.Argon2.X86_64.BlockAddress diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/BlockAddressLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/BlockAddressLit.lean new file mode 100644 index 000000000..af28ce7cd --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/BlockAddressLit.lean @@ -0,0 +1,10 @@ +import VerifiedGarbage.Proof.Framework.X86_64.Lit +import VerifiedGarbage.Impl.Argon2.X86_64.BlockAddress + +/-! A checked literal for lane-major matrix block addressing. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.BlockAddress.code + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/BlockStore.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/BlockStore.lean new file mode 100644 index 000000000..1d7af7460 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/BlockStore.lean @@ -0,0 +1,28 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.Initialize + +/-! A single matrix or scratch block word write as a vector update. -/ + +namespace VG.Proof.Argon2.X86_64 + +open VG VG.Spec.Argon2 + +theorem blockAt_write (m : Mem) (p : Addr) (i : Fin 128) (v : Word) : + blockAt (m.writeW (off p (8 * i.val)) v) p = (blockAt m p).set i v := by + apply Vector.ext + intro j hj + simp only [blockAt, Vector.getElem_ofFn, Vector.getElem_set] + by_cases eq : i.val = j + · subst j + simp only [ite_true] + change (m.writeW (off p (8 * i.val)) v).readW (off p (8 * i.val)) 64 = v + exact Mem.readW_writeW_self64 _ _ _ + · simp only [eq, ite_false] + change (m.writeW (off p (8 * i.val)) v).readW (off p (8 * j)) 64 = + m.readW (off p (8 * j)) 64 + exact Mem.readW_writeW_sep (Offset.sep p (by omega) (by omega) (by omega)) (by decide) + +theorem blockAt_write_nat (m : Mem) (p : Addr) (i : Nat) (hi : i < 128) (v : Word) : + blockAt (m.writeW (off p (8 * i)) v) p = (blockAt m p).set i v hi := + blockAt_write m p ⟨i, hi⟩ v + +end VG.Proof.Argon2.X86_64 diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ClearBlock.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ClearBlock.lean new file mode 100644 index 000000000..c08198c81 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ClearBlock.lean @@ -0,0 +1,78 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ClearBlock +import VerifiedGarbage.Proof.Argon2.X86_64.MemoryInitClear +import VerifiedGarbage.Proof.Argon2.X86_64.Initialize +import VerifiedGarbage.Proof.Framework.X86_64.Inline + +/-! Zero every word, preserving the enclosing loop's registers and memory. -/ + +namespace VG.Proof.Argon2.X86_64.ClearBlock + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.ClearBlock + +theorem word_ok (s : State) (i : Nat) + (hw : InRegions s.wr (off (s.gpr .rdi) (8 * i)) 8) : + WP isa (.block (Impl.Argon2.X86_64.ClearBlock.word i)) s fun t => + t.mem = s.mem.writeW (off (s.gpr .rdi) (8 * i)) (s.gpr .rax) ∧ + t.gpr = s.gpr ∧ t.rd = s.rd ∧ t.wr = s.wr ∧ t.mxcsr = s.mxcsr := by + apply WP.of_runBlock + simp only [Impl.Argon2.X86_64.ClearBlock.word, runBlock_cons, runStep_some, runBlock_nil, + exec, State.store64, ea_at, hw, ite_true, Option.some.injEq, exists_eq_left'] + exact ⟨trivial, trivial, trivial, trivial, trivial⟩ + +theorem prefix_ok (n : Nat) (hn : n ≤ 128) (s : State) (zero : s.gpr .rax = 0) + (write : Covers [⟨s.gpr .rdi, 1024⟩] s.wr) : + WP isa (.block (words n)) s fun t => + t.mem = MemoryInit.clearMem s.mem (s.gpr .rdi) n ∧ + t.gpr = s.gpr ∧ t.rd = s.rd ∧ t.wr = s.wr ∧ t.mxcsr = s.mxcsr := by + induction n with + | zero => exact WP.block_nil ⟨rfl, rfl, rfl, rfl, rfl⟩ + | succ n ih => + simp only [words, List.range_succ, List.flatMap_append, List.flatMap_cons, + List.flatMap_nil, List.append_nil] + apply WP.block_append + refine (ih (by omega)).mono ?_ + rintro a ⟨mem, regs, rd, wr, mx⟩ + have hw : InRegions a.wr (off (a.gpr .rdi) (8 * n)) 8 := by + rw [regs, wr] + exact write _ _ ⟨⟨s.gpr .rdi, 1024⟩, by simp, + Offset.contains_base _ (d := 8 * n) (n := 8) (k := 1024) (by omega) (by omega)⟩ + refine (word_ok a n hw).mono ?_ + rintro t ⟨mem', regs', rd', wr', mx'⟩ + refine ⟨?_, regs'.trans regs, rd'.trans rd, wr'.trans wr, mx'.trans mx⟩ + rw [mem', regs, zero, mem] + rfl + +theorem zero_ok (s : State) : WP isa (.block [.mov .rax (.imm 0)]) s fun t => + t.gpr .rax = 0 ∧ (∀ r, r ≠ .rax → t.gpr r = s.gpr r) ∧ + t.mem = s.mem ∧ t.rd = s.rd ∧ t.wr = s.wr ∧ t.mxcsr = s.mxcsr := by + apply WP.of_runBlock + simp only [runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + Option.map_some, Option.some.injEq, exists_eq_left', RegUpd.gpr_setReg, ite_true] + refine ⟨rfl, ?_, rfl, rfl, rfl, rfl⟩ + intro r hr + exact ite_eq_right hr + +theorem code_ok (s : State) (write : Covers [⟨s.gpr .rdi, 1024⟩] s.wr) : + WP isa code s fun t => blockAt t.mem (s.gpr .rdi) = zeroBlock ∧ + Frame [⟨s.gpr .rdi, 1024⟩] s.mem t.mem ∧ CopyKeeps s t ∧ t.mxcsr = s.mxcsr := by + unfold code + refine WP.seq ((zero_ok s).mono ?_) + rintro a ⟨zero, regs, mem, rd, wr, mx⟩ + have dest : a.gpr .rdi = s.gpr .rdi := regs .rdi (by decide) + have write' : Covers [⟨a.gpr .rdi, 1024⟩] a.wr := by rw [dest, wr]; exact write + refine (prefix_ok 128 (by decide) a zero write').mono ?_ + rintro t ⟨mem', regs', rd', wr', mx'⟩ + have cleared : t.mem = MemoryInit.clearMem s.mem (s.gpr .rdi) 128 := by + rw [mem', mem, dest] + refine ⟨?_, ?_, ⟨fun r hr => (congrFun regs' r).trans (regs r hr), rd'.trans rd, wr'.trans wr⟩, + mx'.trans mx⟩ + · rw [cleared] + apply Vector.ext + intro i hi + change (blockAt _ _)[(⟨i, hi⟩ : Fin 128)] = zeroBlock[(⟨i, hi⟩ : Fin 128)] + rw [blockAt_get, MemoryInit.clearMem_word _ _ 128 i (by decide) hi] + simp only [zeroBlock, Fin.getElem_fin, Vector.getElem_replicate] + · rw [cleared] + exact MemoryInit.clearMem_frame _ _ 128 (by decide) + +end VG.Proof.Argon2.X86_64.ClearBlock diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ClearBlockLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ClearBlockLit.lean new file mode 100644 index 000000000..0fd6d8f80 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ClearBlockLit.lean @@ -0,0 +1,10 @@ +import VerifiedGarbage.Proof.Framework.X86_64.Lit +import VerifiedGarbage.Impl.Argon2.X86_64.ClearBlock + +/-! Checked literal of a complete address-generation block clear. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.ClearBlock.code + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWord.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWord.lean new file mode 100644 index 000000000..4125b3273 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWord.lean @@ -0,0 +1,68 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DependentWordPointer +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelSpec + +/-! Select the specified secret random word from the public previous-cell address. -/ + +namespace VG.Proof.Argon2.X86_64.DependentWord + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.DependentWord + +theorem read_ok (s : State) (hr : InRegions (s.rd ++ s.wr) (s.gpr .rax) 8) : + WP isa (.block Impl.Argon2.X86_64.DependentWord.read) s fun t => t.gpr .rdi = s.mem.readW (s.gpr .rax) 64 ∧ Divide.Keeps [.rdi] s t := by + apply WP.of_runBlock + simp only [Impl.Argon2.X86_64.DependentWord.read, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, State.load64, + ea_at, BitVec.add_zero, hr, Option.map_some, + Option.some.injEq, exists_eq_left', RegUpd.gpr_setReg, ite_true] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, hr, ite_false] + all_goals rfl + +theorem code_ok (s : State) (p : Params) (pass lane slice index : Nat) + (h : FillKernel.Ready p pass lane slice index s) : WP isa code s fun t => + t.gpr .rdi = (blockAt s.mem (FillKernel.previous s p lane slice index))[0] ∧ + Divide.Keeps ReferenceMap.changed s t := by + unfold code + refine WP.seq ((pointer_ok s p pass lane slice index h).mono ?_) + rintro a ⟨pointer, keeps⟩ + have bound := Proof.Argon2.previous_column_lt p h.bounds.lanesPositive h.bounds.memoryMinimum + (slice * p.segmentLen + index) + have cover := h.layout.cell_cover h.bounds.lanesPositive h.bounds.laneBound bound + have hr : InRegions (a.rd ++ a.wr) (a.gpr .rax) 8 := by + rw [keeps.rd, keeps.wr, pointer] + have contains : (⟨FillKernel.previous s p lane slice index, 1024⟩ : Region).Contains + (FillKernel.previous s p lane slice index) 8 := + by simpa only [BitVec.add_zero] using (Offset.contains_base + (FillKernel.previous s p lane slice index) (d := 0) (n := 8) (k := 1024) (by decide) (by decide)) + obtain ⟨r, hr, hc⟩ := cover _ _ ⟨⟨FillKernel.previous s p lane slice index, 1024⟩, + by simp [FillKernel.previous, FillKernel.previousColumn, FillKernel.currentColumn], contains⟩ + exact ⟨r, List.mem_append_right _ hr, hc⟩ + refine (read_ok a hr).mono ?_ + rintro t ⟨random, tail⟩ + refine ⟨?_, keeps.trans (tail.mono (by decide))⟩ + rw [random, pointer, keeps.mem] + change s.mem.readW _ 64 = (blockAt _ _)[(⟨0, by decide⟩ : Fin 128)] + rw [blockAt_get] + change s.mem.readW _ 64 = s.mem.readW (_ + 0#64) 64 + rw [BitVec.add_zero] + +theorem code_spec_ok (s : State) (p : Params) (pass lane slice index : Nat) + (h : FillKernel.Ready p pass lane slice index s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) + (dependent : independent p pass slice = false) : WP isa code s fun t => + t.gpr .rdi = Proof.Argon2.FillStep.random p pass lane slice index state.memory ∧ + Divide.Keeps ReferenceMap.changed s t := by + refine (code_ok s p pass lane slice index h).mono ?_ + rintro t ⟨random, keeps⟩ + have bound := Proof.Argon2.previous_cell_lt p h.bounds.lanesPositive h.bounds.memoryMinimum + h.bounds.laneBound (column := FillKernel.currentColumn p slice index) + have block := represented.block (FillKernel.previousIndex p lane slice index) bound + change blockAt s.mem (FillKernel.previous s p lane slice index) = _ at block + rw [block] at random + refine ⟨?_, keeps⟩ + simpa only [Proof.Argon2.FillStep.random, dependent, Bool.false_eq_true, ite_false, FillKernel.previousIndex, FillKernel.previousColumn, + FillKernel.currentColumn] using random + +end VG.Proof.Argon2.X86_64.DependentWord diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordCT.lean new file mode 100644 index 000000000..408e40078 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordCT.lean @@ -0,0 +1,53 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DependentWord +import VerifiedGarbage.Proof.Argon2.X86_64.DependentWordLit +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! The previous cell is read at an address determined by public parameters. -/ + +namespace VG.Proof.Argon2.X86_64.DependentWord + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.DependentWord + +structure Related (p : Params) (pass lane slice index : Nat) (s t : State) : Prop where + left : FillKernel.Ready p pass lane slice index s + right : FillKernel.Ready p pass lane slice index t + bases : s.gpr .rbp = t.gpr .rbp + matrices : FillKernel.matrix s = FillKernel.matrix t + +theorem pointer_rel : RelCT isa + (fun s t => ∀ r ∈ [Reg.rbp, .r12, .r13, .r14, .r15], s.gpr r = t.gpr r) + pointer (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp, .r12, .r13, .r14, .r15]) + (fun _ _ h => Taint.agree_ofRegs h) (by taint_decide) + +theorem read_rel : RelCT isa (fun s t => s.gpr .rax = t.gpr .rax) (.block Impl.Argon2.X86_64.DependentWord.read) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rax]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +theorem code_rel (p : Params) (pass lane slice index : Nat) : + RelCT isa (Related p pass lane slice index) code (fun _ _ => True) := by + have trace := pointer_rel.mono (P' := Related p pass lane slice index) (by + intro s t h r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl + · exact h.bases + · exact h.left.position.laneLength.trans h.right.position.laneLength.symm + · exact h.left.position.segmentLength.trans h.right.position.segmentLength.symm + · exact h.left.position.slice.trans h.right.position.slice.symm + · exact h.left.position.index.trans h.right.position.index.symm) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => + ⟨pointer_ok s p pass lane slice index h.left, pointer_ok t p pass lane slice index h.right⟩) + have publicTrace : RelCT isa (Related p pass lane slice index) pointer + (fun s t => s.gpr .rax = t.gpr .rax) := full.mono (fun _ _ h => h) (by + intro a b h + obtain ⟨_, s, t, hp, ⟨pa, _⟩, ⟨pb, _⟩⟩ := h + have equal : FillKernel.previous s p lane slice index = FillKernel.previous t p lane slice index := by + unfold FillKernel.previous; rw [hp.matrices] + exact pa.trans (equal.trans pb.symm)) + exact publicTrace.seq read_rel + +end VG.Proof.Argon2.X86_64.DependentWord diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordLit.lean new file mode 100644 index 000000000..e0d6657f7 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordLit.lean @@ -0,0 +1,10 @@ +import VerifiedGarbage.Proof.Framework.X86_64.Lit +import VerifiedGarbage.Impl.Argon2.X86_64.DependentWord + +/-! Checked literal of the public predecessor-address computation. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.DependentWord.pointer + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordPointer.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordPointer.lean new file mode 100644 index 000000000..222bc410b --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordPointer.lean @@ -0,0 +1,52 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.DependentWord +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelArgs +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelPrepare + +/-! The data-dependent word's address is the specification's cyclic predecessor. -/ + +namespace VG.Proof.Argon2.X86_64.DependentWord + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.DependentWord + +theorem args_ok (s : State) : WP isa (.block args) s fun t => + t.gpr .rcx = s.gpr .rdi ∧ t.gpr .rax = s.gpr .rbx ∧ Divide.Keeps [.rcx, .rax] s t := by + apply WP.of_runBlock + simp only [args, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + RegUpd.gpr_setReg, reduceCtorEq, ite_true, ite_false, Option.map_some, + Option.some.injEq, exists_eq_left'] + refine ⟨trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, hr.1, hr.2, ite_false] + all_goals rfl + +theorem pointer_ok (s : State) (p : Params) (pass lane slice index : Nat) + (h : FillKernel.Ready p pass lane slice index s) : WP isa pointer s fun t => + t.gpr .rax = FillKernel.previous s p lane slice index ∧ Divide.Keeps ReferenceMap.changed s t := by + unfold pointer + refine WP.seq ((FillKernel.load_ok s .r8 232 (h.layout.frameRead 232 (by simp))).mono ?_) + rintro a ⟨base, ka⟩ + have k : Divide.Keeps ReferenceMap.changed s a := ka.mono (by decide) + have pos := h.position.of_keeps k + have columnBound := Proof.Argon2.column_lt p h.bounds.lanesPositive h.bounds.sliceBound h.bounds.indexBound + have segment := Proof.Argon2.segmentLen_ge_two p h.bounds.lanesPositive h.bounds.memoryMinimum + have q := Proof.Argon2.laneLen_segments p h.bounds.lanesPositive + refine WP.seq ((FillColumn.code_nat_ok a slice p.segmentLen index p.laneLen + pos.slice pos.segmentLength pos.index pos.laneLength (by omega) + (Nat.lt_trans h.bounds.laneLength_bound (by decide)) columnBound).mono ?_) + rintro b ⟨_, prev, kb⟩ + refine WP.seq ((args_ok b).mono ?_) + rintro c ⟨col, laneReg, kc⟩ + have col' := col.trans prev + have lane' : c.gpr .rax = BitVec.ofNat 64 lane := laneReg.trans ((kb.regs .rbx (by decide)).trans pos.current) + have length : c.gpr .r12 = BitVec.ofNat 64 p.laneLen := + (kc.regs .r12 (by decide)).trans ((kb.regs .r12 (by decide)).trans pos.laneLength) + refine (BlockAddress.code_nat_ok c lane + ((slice * p.segmentLen + index + p.laneLen - 1) % p.laneLen) p.laneLen lane' col' length).mono ?_ + rintro t ⟨address, kt⟩ + refine ⟨?_, ((k.trans (kb.mono (by decide))).trans (kc.mono (by decide))).trans (kt.mono (by decide))⟩ + rw [address, kc.regs .r8 (by decide), kb.regs .r8 (by decide), base] + rfl + +end VG.Proof.Argon2.X86_64.DependentWord diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordState.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordState.lean new file mode 100644 index 000000000..33d7648fe --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DependentWordState.lean @@ -0,0 +1,25 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DependentWord +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelStable + +/-! The dependent source hands the filling step its random word and unchanged matrix. -/ + +namespace VG.Proof.Argon2.X86_64.DependentWord + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.DependentWord + +theorem state_ok (s : State) (p : Params) (pass lane slice index : Nat) + (h : FillKernel.Ready p pass lane slice index s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) + (dependent : independent p pass slice = false) : WP isa code s fun t => + t.gpr .rdi = Proof.Argon2.FillStep.random p pass lane slice index state.memory ∧ + FillKernel.Ready p pass lane slice index t ∧ + Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks state.memory ∧ + Divide.Keeps ReferenceMap.changed s t := by + refine (code_spec_ok s p pass lane slice index h state represented dependent).mono ?_ + rintro t ⟨random, keeps⟩ + refine ⟨random, h.of_keeps keeps, ?_, keeps⟩ + have base : FillKernel.matrix t = FillKernel.matrix s := by + unfold FillKernel.matrix; rw [keeps.mem, keeps.regs .rbp (by decide)] + rw [keeps.mem, base]; exact represented + +end VG.Proof.Argon2.X86_64.DependentWord diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillAllocation.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillAllocation.lean new file mode 100644 index 000000000..82224d0b9 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillAllocation.lean @@ -0,0 +1,25 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelStable + +/-! Transport the allocation using just its public pointers and permissions. -/ + +namespace VG.Proof.Argon2.X86_64.FillKernel + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem Layout.of_preserved {p : Params} {s t : State} (h : Layout p s) + (bp : t.gpr .rbp = s.gpr .rbp) (sp : t.gpr .rsp = s.gpr .rsp) + (base : matrix t = matrix s) (scratch : work t = work s) + (rd : t.rd = s.rd) (wr : t.wr = s.wr) : Layout p t := by + constructor + · rw [rd, wr, bp]; exact h.frameRead + · rw [wr, bp]; exact h.frameWrite + · rw [base, wr]; exact h.matrixWrite + · rw [scratch, wr]; exact h.workWrite + · rw [base, scratch]; exact h.matrixWork + · rw [base, bp]; exact h.matrixFrame + · rw [base, sp]; exact h.matrixStack + · rw [bp, scratch]; exact h.frameWork + · rw [bp, sp]; exact h.frameStack + · rw [sp, scratch]; exact h.stackWork + +end VG.Proof.Argon2.X86_64.FillKernel diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlock.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlock.lean new file mode 100644 index 000000000..dd5f699f0 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlock.lean @@ -0,0 +1,47 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillBlock +import VerifiedGarbage.Proof.Argon2.X86_64.FillBlockFrame +import VerifiedGarbage.Proof.Argon2.X86_64.FillCacheInvariant +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelSpec + +/-! Complete active filling cell against the reviewed matrix transition. -/ + +namespace VG.Proof.Argon2.X86_64.FillBlock + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Done (s t : State) (p : Params) (pass lane slice index : Nat) (state : FillState) : Prop where + ready : ∃ old, RandomSource.Ready p pass lane slice index old t + represented : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks + (fillBlock p pass slice lane index state).memory + matrix : FillKernel.matrix t = FillKernel.matrix s + work : AddressCalls.work t = AddressCalls.work s + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (writes s p) s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + +theorem code_ok (s : State) (p : Params) (pass lane slice index old : Nat) + (h : RandomSource.Ready p pass lane slice index old s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) : + WP isa Impl.Argon2.X86_64.FillBlock.code s (Done s · p pass lane slice index state) := by + unfold Impl.Argon2.X86_64.FillBlock.code + refine WP.seq ((RandomSource.code_ok s p pass lane slice index old h state represented).mono ?_) + intro a source + obtain ⟨counter, ready⟩ := source.ready + have baseA : FillKernel.matrix a = FillKernel.matrix s := source.frame_word h 232 (by decide) (by decide) + have workA : AddressCalls.work a = AddressCalls.work s := source.frame_word h 248 (by decide) (by decide) + refine (FillKernel.code_spec_ok a p pass lane slice index ready.filling state source.represented source.random).mono ?_ + rintro t ⟨done, matrix⟩ + have baseT : FillKernel.matrix t = FillKernel.matrix a := done.frame_word ready.filling 232 (by decide) (by decide) + have workT : AddressCalls.work t = AddressCalls.work a := done.frame_word ready.filling 248 (by decide) (by decide) + refine ⟨⟨counter, ready.after_fill done⟩, ?_, baseT.trans baseA, workT.trans workA, ?_, + done.rd.trans source.rd, done.wr.trans source.wr, ?_, done.mxcsr.trans source.mxcsr⟩ + · rw [baseT]; exact matrix + · intro r hr; exact (done.regs r hr).trans (source.regs r hr) + · have frame := kernel_frame ready.filling done + rw [writes, baseA, workA, source.regs .rsp (by simp [calleeSaved]), + source.regs .rbp (by simp [calleeSaved])] at frame + exact (source_frame source).trans frame + +end VG.Proof.Argon2.X86_64.FillBlock diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlockCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlockCT.lean new file mode 100644 index 000000000..cf4a81680 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlockCT.lean @@ -0,0 +1,72 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillBlock +import VerifiedGarbage.Proof.Argon2.X86_64.RandomSourceCT +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelCT + +/-! An active filling cell leaks only its specified data-dependent reference. -/ + +namespace VG.Proof.Argon2.X86_64.FillBlock + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Related (p : Params) (pass lane slice index old : Nat) (leftState rightState : FillState) + (s t : State) : Prop where + source : RandomSource.Related p pass lane slice index old s t + leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory + references : independent p pass slice = false → + reference p pass lane slice index (Proof.Argon2.FillStep.random p pass lane slice index leftState.memory) = + reference p pass lane slice index (Proof.Argon2.FillStep.random p pass lane slice index rightState.memory) + +theorem Related.of_indices {p : Params} {pass lane slice index old : Nat} {s t : State} + {leftState rightState : FillState} (source : RandomSource.Related p pass lane slice index old s t) + (leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory) + (rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory) + (indices : (fillBlock p pass slice lane index leftState).indices = + (fillBlock p pass slice lane index rightState).indices) : + Related p pass lane slice index old leftState rightState s t := by + refine ⟨source, leftMatrix, rightMatrix, ?_⟩ + intro mode + rw [Proof.Argon2.FillStep.indices p pass lane slice index leftState source.left.filling.bounds.active, + Proof.Argon2.FillStep.indices p pass lane slice index rightState source.right.filling.bounds.active] at indices + simp only [mode, Bool.false_eq_true, ite_false] at indices + exact (List.cons.inj indices).1 + +theorem Related.reference_eq {p : Params} {pass lane slice index old : Nat} {s t : State} + {leftState rightState : FillState} (h : Related p pass lane slice index old leftState rightState s t) : + reference p pass lane slice index (Proof.Argon2.FillStep.random p pass lane slice index leftState.memory) = + reference p pass lane slice index (Proof.Argon2.FillStep.random p pass lane slice index rightState.memory) := by + cases mode : independent p pass slice + · exact h.references mode + · simp only [Proof.Argon2.FillStep.random, mode, ite_true] + +theorem source_public_rel (p : Params) (pass lane slice index old : Nat) (leftState rightState : FillState) : + RelCT isa (Related p pass lane slice index old leftState rightState) + Impl.Argon2.X86_64.RandomSource.code (FillKernel.Related p pass lane slice index) := by + intro s t ta tb a b hp ea eb + obtain ⟨traces, _⟩ := RandomSource.code_rel p pass lane slice index old _ _ _ _ _ _ hp.source ea eb + obtain ⟨_, a', runA, ha⟩ := RandomSource.code_ok s p pass lane slice index old hp.source.left leftState hp.leftMatrix + obtain ⟨_, b', runB, hb⟩ := RandomSource.code_ok t p pass lane slice index old hp.source.right rightState hp.rightMatrix + obtain ⟨_, sameA⟩ := Exec.det ea runA + obtain ⟨_, sameB⟩ := Exec.det eb runB + subst a'; subst b' + refine ⟨traces, ?_⟩ + obtain ⟨_, readyA⟩ := ha.ready + obtain ⟨_, readyB⟩ := hb.ready + refine ⟨readyA.filling, readyB.filling, ?_, ?_, ?_, ?_, ?_⟩ + · exact (ha.regs .rbp (by simp [calleeSaved])).trans + (hp.source.bases.trans (hb.regs .rbp (by simp [calleeSaved])).symm) + · exact (ha.regs .rsp (by simp [calleeSaved])).trans + (hp.source.stacks.trans (hb.regs .rsp (by simp [calleeSaved])).symm) + · exact (ha.frame_word hp.source.left 232 (by decide) (by decide)).trans + (hp.source.matrices.trans (hb.frame_word hp.source.right 232 (by decide) (by decide)).symm) + · exact (ha.frame_word hp.source.left 248 (by decide) (by decide)).trans + (hp.source.work.trans (hb.frame_word hp.source.right 248 (by decide) (by decide)).symm) + · rw [ha.random, hb.random]; exact hp.reference_eq + +theorem code_rel (p : Params) (pass lane slice index old : Nat) (leftState rightState : FillState) : + RelCT isa (Related p pass lane slice index old leftState rightState) + Impl.Argon2.X86_64.FillBlock.code (fun _ _ => True) := + (source_public_rel p pass lane slice index old leftState rightState).seq + (FillKernel.code_rel p pass lane slice index) + +end VG.Proof.Argon2.X86_64.FillBlock diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlockCounter.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlockCounter.lean new file mode 100644 index 000000000..df6e8aac4 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlockCounter.lean @@ -0,0 +1,26 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillBlock +import VerifiedGarbage.Proof.Argon2.X86_64.RandomSourceCounter + +/-! Compression preserves the public cache counter selected by the random source. -/ + +namespace VG.Proof.Argon2.X86_64.FillBlock + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem counter_run {s t : State} {trace : List Leak} {p : Params} {pass lane slice index old : Nat} + (h : RandomSource.Ready p pass lane slice index old s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) + (run : Exec isa Impl.Argon2.X86_64.FillBlock.code s trace t) : + t.mem.readW (off (t.gpr .rbp) 8) 64 = RandomSource.counterValue p pass slice index old := by + cases run with + | seq sourceRun kernelRun => + obtain ⟨_, a', runA, source⟩ := RandomSource.code_ok s p pass lane slice index old h state represented + obtain ⟨_, rfl⟩ := Exec.det sourceRun runA + obtain ⟨_, a', counterRun, counter⟩ := RandomSource.counter_ok s p pass lane slice index old h + obtain ⟨_, rfl⟩ := Exec.det sourceRun counterRun + obtain ⟨_, ready⟩ := source.ready + obtain ⟨_, t', runT, done⟩ := FillKernel.code_ok _ p pass lane slice index ready.filling + obtain ⟨_, rfl⟩ := Exec.det kernelRun runT + exact (done.frame_word ready.filling 8 (by decide) (by decide)).trans counter + +end VG.Proof.Argon2.X86_64.FillBlock diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlockFrame.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlockFrame.lean new file mode 100644 index 000000000..0243d7c91 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillBlockFrame.lean @@ -0,0 +1,37 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.RandomSourceState + +/-! Compose scratch writes with a matrix-cell write inside the derive allocation. -/ + +namespace VG.Proof.Argon2.X86_64.FillBlock + +open VG VG.X86_64 VG.Spec.Argon2 + +def writes (s : State) (p : Params) : List Region := + [⟨FillKernel.matrix s, p.blocks * 1024⟩, ⟨AddressCalls.work s, 8192⟩, + below (s.gpr .rsp) 8, ⟨off (s.gpr .rbp) 8, 16⟩] + +theorem source_frame {s t : State} {p : Params} {pass lane slice index : Nat} {state : FillState} + (done : RandomSource.Done s t p pass lane slice index state) : Frame (writes s p) s.mem t.mem := by + apply done.frame.sub + intro r hr + simp only [RandomSource.writes, AddressCache.writes, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · exact ⟨_, by simp [writes], fun _ h => h⟩ + · exact ⟨_, by simp [writes], fun _ h => h⟩ + · exact ⟨⟨off (s.gpr .rbp) 8, 16⟩, by simp [writes], Region.sub_prefix (by decide)⟩ + +theorem kernel_frame {s t : State} {p : Params} {pass lane slice index : Nat} + (ready : FillKernel.Ready p pass lane slice index s) (done : FillKernel.Done s t p pass lane slice index) : + Frame (writes s p) s.mem t.mem := by + apply done.frame.sub + intro r hr + simp only [FillKernel.writes, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl + · exact ⟨_, by simp [writes], FillKernel.cell_sub p _ ready.bounds.lanesPositive ready.bounds.laneBound + (Proof.Argon2.column_lt p ready.bounds.lanesPositive ready.bounds.sliceBound ready.bounds.indexBound)⟩ + · exact ⟨⟨AddressCalls.work s, 8192⟩, by simp [writes], Region.sub_prefix (by decide)⟩ + · exact ⟨_, by simp [writes], fun _ h => h⟩ + · exact ⟨_, by simp [writes], Offset.sub _ (d := 16) (n := 8) (e := 8) (k := 16) + (by decide) (by decide)⟩ + +end VG.Proof.Argon2.X86_64.FillBlock diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCacheInvariant.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCacheInvariant.lean new file mode 100644 index 000000000..0876b3625 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCacheInvariant.lean @@ -0,0 +1,57 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.RandomSourcePrepare + +/-! The cell update does not disturb the cached independent-address block. -/ + +namespace VG.Proof.Argon2.X86_64.RandomSource + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem Ready.after_fill {p : Params} {pass lane slice index old : Nat} {s t : State} + (h : Ready p pass lane slice index old s) (done : FillKernel.Done s t p pass lane slice index) : + Ready p pass lane slice index old t := by + have bp := done.regs .rbp (by simp [calleeSaved]) + have sp := done.regs .rsp (by simp [calleeSaved]) + have base : FillKernel.matrix t = FillKernel.matrix s := done.frame_word h.filling 232 (by decide) (by decide) + have work : AddressCalls.work t = AddressCalls.work s := done.frame_word h.filling 248 (by decide) (by decide) + refine ⟨done.retains h.filling, ?_, ?_⟩ + · refine ⟨?_, ?_, ?_, ?_, h.cache.bound, ?_⟩ + · constructor + · rw [done.rd, done.wr, bp]; exact h.cache.layout.frameRead + · rw [done.wr, work]; exact h.cache.layout.workWrite + · rw [bp, work]; exact h.cache.layout.frameWork + · rw [bp, sp]; exact h.cache.layout.frameStack + · rw [sp, work]; exact h.cache.layout.stackWork + · rw [done.rd, done.wr, bp]; exact h.cache.reads + · rw [done.wr, bp]; exact h.cache.write + · exact ⟨(done.frame_word h.filling 0 (by decide) (by decide)).trans h.cache.words.passWord, + (done.regs .rbx (by simp [calleeSaved])).trans h.cache.words.laneWord, + (done.regs .r14 (by simp [calleeSaved])).trans h.cache.words.sliceWord, + (done.frame_word h.filling 240 (by decide) (by decide)).trans h.cache.words.blocksWord, + (done.frame_word h.filling 72 (by decide) (by decide)).trans h.cache.words.passesWord, + (done.frame_word h.filling 112 (by decide) (by decide)).trans h.cache.words.variantWord, + (done.frame_word h.filling 8 (by decide) (by decide)).trans h.cache.words.counterWord⟩ + · rcases h.cache.cached with zero | cached + · exact Or.inl zero + · apply Or.inr + rw [work] + have kept : blockAt t.mem (off (AddressCalls.work s) 6144) = + blockAt s.mem (off (AddressCalls.work s) 6144) := by + apply FillCompress.block_frame done.frame + intro r hr + simp only [FillKernel.writes, List.mem_cons, List.not_mem_nil, or_false] at hr + have cacheSub : Region.Sub ⟨off (AddressCalls.work s) 6144, 1024⟩ ⟨AddressCalls.work s, 8192⟩ := + Offset.sub_base _ (by decide) + rcases hr with rfl | rfl | rfl | rfl + · exact (h.matrixWork.symm.sub_left cacheSub).sub_right + (FillKernel.cell_sub p _ h.filling.bounds.lanesPositive h.filling.bounds.laneBound + (Proof.Argon2.column_lt p h.filling.bounds.lanesPositive + h.filling.bounds.sliceBound h.filling.bounds.indexBound)) + · exact Offset.disjoint_base (AddressCalls.work s) (d := 6144) (n := 1024) (k := 5120) + (by decide) (by decide) + · exact h.cache.layout.stackWork.symm.sub_left cacheSub + · exact (h.cache.layout.frameWork.symm.sub_left cacheSub).sub_right + (Offset.sub_base _ (by decide)) + exact kept.trans cached + · rw [base, work]; exact h.matrixWork + +end VG.Proof.Argon2.X86_64.RandomSource diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillColumn.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillColumn.lean new file mode 100644 index 000000000..a146c8fae --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillColumn.lean @@ -0,0 +1,164 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillColumn +import VerifiedGarbage.Proof.Argon2.X86_64.DivideStep +import VerifiedGarbage.Proof.Argon2.Dimensions +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! Current-column arithmetic and the cyclic predecessor, preserving the +matrix, enclosing loop registers and MXCSR. -/ + +namespace VG.Proof.Argon2.X86_64.FillColumn + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FillColumn + +theorem current_ok (s : State) : WP isa (.block current) s fun t => + t.gpr .rcx = s.gpr .r14 * s.gpr .r13 + s.gpr .r15 ∧ + Divide.Keeps [.rax, .rdx, .rcx] s t := by + apply WP.of_runBlock + simp only [current, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + execMul, execAlu, RegUpd.gpr_setReg, RegUpd.gpr_setFlags, RegUpd.gpr_arithFlags, + reduceCtorEq, ite_true, ite_false, Option.map_some, Option.bind_some, + Option.some.injEq, exists_eq_left', BitVec.ofNat_mul, BitVec.ofNat_toNat, BitVec.setWidth_eq] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_setFlags, RegUpd.gpr_arithFlags, + hr.1, hr.2.1, hr.2.2, ite_false] + all_goals rfl + +theorem compare_ok (s : State) : WP isa (.block [.alu .cmp .rcx (.imm 0)]) s + fun t => t.zf = decide (s.gpr .rcx = 0) ∧ Divide.Keeps [] s t := by + apply WP.of_runBlock + simp only [runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, execAlu, + RegUpd.zf_arithFlags, Option.bind_some, Option.some.injEq, exists_eq_left', + show BitVec.signExtend 64 (0 : BitVec 32) = (0 : Addr) from rfl] + refine ⟨?_, ?_⟩ + · change (s.gpr .rcx - 0#64 == 0#64) = decide (s.gpr .rcx = 0#64) + rw [BitVec.sub_zero] + exact Bool.eq_iff_iff.mpr (by simp only [beq_iff_eq, decide_eq_true_eq]) + constructor + · intro r _; exact congrFun (RegUpd.gpr_arithFlags _ _ _ _) r + all_goals rfl + +theorem move_ok (s : State) (r : Reg) : WP isa (.block [.mov .rdi (.reg r)]) s + fun t => t.gpr .rdi = s.gpr r ∧ Divide.Keeps [.rdi] s t := by + apply WP.of_runBlock + simp only [runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + Option.map_some, Option.some.injEq, exists_eq_left', RegUpd.gpr_setReg, ite_true] + refine ⟨trivial, ?_⟩ + constructor + · intro q hq + simp only [List.mem_cons, List.not_mem_nil, or_false] at hq + exact ite_eq_right hq + all_goals rfl + +theorem decrement_ok (s : State) : WP isa (.block [.alu .sub .rdi (.imm 1)]) s + fun t => t.gpr .rdi = s.gpr .rdi - 1 ∧ Divide.Keeps [.rdi] s t := by + apply WP.of_runBlock + simp only [runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, execAlu, + RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, Option.bind_some, + Option.some.injEq, exists_eq_left', ite_true, + show BitVec.signExtend 64 (1 : BitVec 32) = (1 : Addr) from rfl] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + exact ite_eq_right hr + all_goals rfl + +theorem previous_ok (s : State) : WP isa previous s fun t => + t.gpr .rdi = (if s.gpr .rcx = 0 then s.gpr .r12 else s.gpr .rcx) - 1 ∧ + Divide.Keeps [.rdi] s t := by + unfold previous + refine WP.seq ((compare_ok s).mono ?_) + rintro a ⟨flag, ka⟩ + have selected : WP isa select a fun b => + b.gpr .rdi = (if s.gpr .rcx = 0 then s.gpr .r12 else s.gpr .rcx) ∧ + Divide.Keeps [.rdi] s b := by + unfold select + refine WP.ite (decide (s.gpr .rcx = 0)) (by simp only [eval, flag]) ?_ ?_ + · intro h + have zero := of_decide_eq_true h + refine (move_ok a .r12).mono ?_ + rintro b ⟨value, kb⟩ + exact ⟨by rw [value, ka.regs .r12 (by simp), ite_eq_left zero], + (ka.mono (by simp)).trans kb⟩ + · intro h + have nonzero := of_decide_eq_false h + refine (move_ok a .rcx).mono ?_ + rintro b ⟨value, kb⟩ + exact ⟨by rw [value, ka.regs .rcx (by simp), ite_eq_right nonzero], + (ka.mono (by simp)).trans kb⟩ + refine WP.seq (selected.mono ?_) + rintro b ⟨value, kb⟩ + refine (decrement_ok b).mono ?_ + rintro t ⟨result, kt⟩ + exact ⟨by rw [result, value], kb.trans kt⟩ + +theorem code_ok (s : State) : WP isa code s fun t => + let column := s.gpr .r14 * s.gpr .r13 + s.gpr .r15 + t.gpr .rcx = column ∧ + t.gpr .rdi = (if column = 0 then s.gpr .r12 else column) - 1 ∧ + Divide.Keeps [.rax, .rdx, .rcx, .rdi] s t := by + unfold code + refine WP.seq ((current_ok s).mono ?_) + rintro a ⟨column, ka⟩ + refine (previous_ok a).mono ?_ + rintro t ⟨previous, kt⟩ + refine ⟨(kt.regs .rcx (by decide)).trans column, ?_, + (ka.mono (by simp)).trans (kt.mono (by simp))⟩ + rw [previous, column, ka.regs .r12 (by decide)] + +theorem previous_nat (column q : Nat) (positive : 0 < q) (bound : column < q) : + (if column = 0 then q else column) - 1 = (column + q - 1) % q := by + by_cases zero : column = 0 + · simp only [zero, ite_true, Nat.zero_add] + exact (Nat.mod_eq_of_lt (by omega : q - 1 < q)).symm + · simp only [zero, ite_false] + have sub : column + q - 1 - q = column - 1 := by omega + rw [Nat.mod_eq_sub_mod (by omega : q ≤ column + q - 1), sub, + Nat.mod_eq_of_lt (by omega : column - 1 < q)] + +theorem previous_word_nat (column q : Nat) (positive : 0 < q) (qBound : q < 2 ^ 64) + (bound : column < q) : + (if BitVec.ofNat 64 column = 0#64 then BitVec.ofNat 64 q else BitVec.ofNat 64 column) - 1 = + BitVec.ofNat 64 ((column + q - 1) % q) := by + have zero : BitVec.ofNat 64 column = 0#64 ↔ + column = 0 := by + constructor + · intro h + have hn := congrArg BitVec.toNat h + rw [BitVec.toNat_ofNat, Nat.mod_eq_of_lt (Nat.lt_trans bound qBound)] at hn + exact hn + · intro h; rw [h] + simp only [zero] + by_cases h : column = 0 + · simp only [h, ite_true] + rw [show (1 : Addr) = BitVec.ofNat 64 1 from rfl, + Offset.ofNat_sub_ofNat positive, Nat.zero_add, Nat.mod_eq_of_lt (by omega : q - 1 < q)] + · simp only [h, ite_false] + rw [show (1 : Addr) = BitVec.ofNat 64 1 from rfl, + Offset.ofNat_sub_ofNat (by omega : 1 ≤ column), + ← previous_nat _ q positive bound, ite_eq_right h] + +theorem code_nat_ok (s : State) (slice segment index q : Nat) + (hs : s.gpr .r14 = BitVec.ofNat 64 slice) + (hg : s.gpr .r13 = BitVec.ofNat 64 segment) + (hi : s.gpr .r15 = BitVec.ofNat 64 index) + (hq : s.gpr .r12 = BitVec.ofNat 64 q) + (positive : 0 < q) (qBound : q < 2 ^ 64) + (bound : slice * segment + index < q) : + WP isa code s fun t => + t.gpr .rcx = BitVec.ofNat 64 (slice * segment + index) ∧ + t.gpr .rdi = BitVec.ofNat 64 ((slice * segment + index + q - 1) % q) ∧ + Divide.Keeps [.rax, .rdx, .rcx, .rdi] s t := by + refine (code_ok s).mono ?_ + rintro t ⟨column, previous, keeps⟩ + have word : s.gpr .r14 * s.gpr .r13 + s.gpr .r15 = + BitVec.ofNat 64 (slice * segment + index) := by + rw [hs, hg, hi, ← BitVec.ofNat_mul, ← BitVec.ofNat_add] + refine ⟨column.trans word, ?_, keeps⟩ + rw [previous, word, hq] + exact previous_word_nat _ q positive qBound bound + +end VG.Proof.Argon2.X86_64.FillColumn diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillColumnCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillColumnCT.lean new file mode 100644 index 000000000..b142465e6 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillColumnCT.lean @@ -0,0 +1,16 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillColumnLit +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! Public loop coordinates determine both columns and their branch trace. -/ + +namespace VG.Proof.Argon2.X86_64.FillColumn + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FillColumn + +theorem code_rel : RelCT isa + (fun s t => ∀ r ∈ [Reg.r14, .r13, .r15, .r12], s.gpr r = t.gpr r) code + (fun s t => ∀ r ∈ [Reg.rcx, .rdi], s.gpr r = t.gpr r) := + RelCT.taintRegs (τ := Taint.ofRegs [.r14, .r13, .r15, .r12]) + (fun _ _ h => Taint.agree_ofRegs h) [Reg.rcx, .rdi] (by taint_decide) + +end VG.Proof.Argon2.X86_64.FillColumn diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillColumnLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillColumnLit.lean new file mode 100644 index 000000000..6d12ca925 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillColumnLit.lean @@ -0,0 +1,10 @@ +import VerifiedGarbage.Proof.Framework.X86_64.Lit +import VerifiedGarbage.Impl.Argon2.X86_64.FillColumn + +/-! A checked literal for current and cyclic predecessor column calculation. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.FillColumn.code + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompress.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompress.lean new file mode 100644 index 000000000..726d9855c --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompress.lean @@ -0,0 +1,52 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressSetup +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressLit + +/-! The complete compression/update sequence from allocation and frame invariants. -/ + +namespace VG.Proof.Argon2.X86_64.FillCompress + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillCompress + +def writes (s : State) : List Region := + [⟨s.gpr .r10, 1024⟩, ⟨work s + 4096, 1024⟩, ⟨work s, 4096⟩, + below (s.gpr .rsp) 8, ⟨off (s.gpr .rbp) 16, 8⟩] + +structure Done (s t : State) : Prop where + block : blockAt t.mem (s.gpr .r10) = + let next := Spec.Argon2.compress (blockAt s.mem (s.gpr .rdi)) (blockAt s.mem (s.gpr .rsi)) + if pass s = 0 then next else xorBlock next (blockAt s.mem (s.gpr .r10)) + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (writes s) s.mem t.mem + +theorem code_ok (s : State) (h : Ready s) : WP isa code s (Done s) := by + unfold code + refine WP.seq ((setup_ok s h).mono ?_) + intro a prepared + refine (operation_ok a prepared.ready).mono ?_ + intro t done + refine ⟨?_, fun r hr => (done.regs r hr).trans (prepared.regs r hr), + done.rd.trans prepared.rd, done.wr.trans prepared.wr, ?_⟩ + · have block := done.block + rw [prepared.oldBlock, prepared.dest, prepared.counter, prepared.leftBlock, + prepared.rightBlock] at block + exact block + · have frame : Frame (writes s) a.mem t.mem := by + have original := done.frame + rw [prepared.dest] at original + simp only [callWrites, prepared.output, prepared.scratch, + prepared.regs .rsp (by simp [calleeSaved])] at original + exact original.mono (by intro r hr; exact List.mem_append_left _ hr) + have savedFrame : Frame (writes s) s.mem a.mem := prepared.frame.mono (by + intro r hr + simp only [prefixWrites, List.mem_singleton] at hr + subst r + simp [writes]) + exact savedFrame.trans frame + +theorem code_mx_ok (s : State) (h : Ready s) : + WP isa code s fun t => Done s t ∧ t.mxcsr = s.mxcsr := + WP.mono_mx (by lit_decide) (code_ok s h) (fun _ done mx => ⟨done, mx⟩) + +end VG.Proof.Argon2.X86_64.FillCompress diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressArgs.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressArgs.lean new file mode 100644 index 000000000..09255a953 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressArgs.lean @@ -0,0 +1,64 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillCompress +import VerifiedGarbage.Proof.Argon2.X86_64.Memory +import VerifiedGarbage.Proof.Argon2.X86_64.DivideStep + +/-! Save the current cell across G and reload the block-write arguments. -/ + +namespace VG.Proof.Argon2.X86_64.FillCompress + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FillCompress + +theorem saveCurrent_ok (s : State) + (hw : InRegions s.wr (off (s.gpr .rbp) 16) 8) : + WP isa (.block saveCurrent) s fun t => + t.mem = s.mem.writeW (off (s.gpr .rbp) 16) (s.gpr .r10) ∧ + t.gpr = s.gpr ∧ t.rd = s.rd ∧ t.wr = s.wr ∧ t.mxcsr = s.mxcsr := by + apply WP.of_runBlock + simp only [saveCurrent, runBlock_cons, runStep_some, runBlock_nil, exec, + State.store64, ea_at, hw, ite_true, Option.some.injEq, exists_eq_left'] + exact ⟨trivial, trivial, trivial, trivial, trivial⟩ + +theorem compressArgs_ok (s : State) + (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 248) 8) : + WP isa (.block compressArgs) s fun t => + t.gpr .rcx = s.mem.readW (off (s.gpr .rbp) 248) 64 ∧ + t.gpr .rdx = s.mem.readW (off (s.gpr .rbp) 248) 64 + 4096 ∧ + Divide.Keeps [.rcx, .rdx] s t := by + apply WP.of_runBlock + simp only [compressArgs, runBlock_cons, runStep_some, runBlock_nil, exec, + readSrc, State.load64, ea_at, hr, execAlu, RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, + reduceCtorEq, ite_true, ite_false, Option.map_some, Option.bind_some, + Option.some.injEq, exists_eq_left', + show BitVec.signExtend 64 (4096 : BitVec 32) = (4096 : Addr) from rfl] + refine ⟨trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr.1, hr.2, ite_false] + all_goals rfl + +theorem writeArgs_ok (s : State) + (destRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 16) 8) + (workRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 248) 8) + (passRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 0) 8) : + WP isa (.block writeArgs) s fun t => + t.gpr .rdi = s.mem.readW (off (s.gpr .rbp) 16) 64 ∧ + t.gpr .rsi = s.mem.readW (off (s.gpr .rbp) 248) 64 + 4096 ∧ + t.gpr .r9 = s.mem.readW (off (s.gpr .rbp) 0) 64 ∧ + Divide.Keeps [.rdi, .rsi, .r9] s t := by + apply WP.of_runBlock + simp only [writeArgs, runBlock_cons, runStep_some, runBlock_nil, exec, + readSrc, State.load64, ea_at, destRead, workRead, passRead, execAlu, + RegUpd.gpr_setReg, RegUpd.mem_setReg, RegUpd.rd_setReg, RegUpd.wr_setReg, + RegUpd.gpr_arithFlags, RegUpd.mem_arithFlags, RegUpd.rd_arithFlags, + RegUpd.wr_arithFlags, reduceCtorEq, ite_true, ite_false, + Option.map_some, Option.bind_some, Option.some.injEq, exists_eq_left', + show BitVec.signExtend 64 (4096 : BitVec 32) = (4096 : Addr) from rfl] + refine ⟨trivial, trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr.1, hr.2.1, hr.2.2, ite_false] + all_goals rfl + +end VG.Proof.Argon2.X86_64.FillCompress diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressCT.lean new file mode 100644 index 000000000..dc9965321 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressCT.lean @@ -0,0 +1,54 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompress +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressOperationCT + +/-! Compose the setup trace with compression and the full block write. -/ + +namespace VG.Proof.Argon2.X86_64.FillCompress + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FillCompress + +structure CodeRelated (s t : State) : Prop where + left : Ready s + right : Ready t + args : ∀ r ∈ [Reg.rdi, .rsi, .r10, .rsp, .rbp], s.gpr r = t.gpr r + scratch : work s = work t + counter : pass s = pass t + +theorem setup_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + setup (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +theorem prepared_public {s t a b : State} (h : CodeRelated s t) + (ha : Prepared s a) (hb : Prepared t b) : Related a b := by + refine ⟨ha.ready, hb.ready, ?_, ha.dest.trans ((h.args .r10 (by simp)).trans hb.dest.symm), + ha.counter.trans (h.counter.trans hb.counter.symm)⟩ + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl + · exact ha.left.trans ((h.args .rdi (by simp)).trans hb.left.symm) + · exact ha.right.trans ((h.args .rsi (by simp)).trans hb.right.symm) + · exact ha.output.trans ((congrArg (· + (4096 : Addr)) h.scratch).trans hb.output.symm) + · exact ha.scratch.trans (h.scratch.trans hb.scratch.symm) + · exact (ha.regs .rsp (by simp [calleeSaved])).trans + ((h.args .rsp (by simp)).trans (hb.regs .rsp (by simp [calleeSaved])).symm) + · exact (ha.regs .rbp (by simp [calleeSaved])).trans + ((h.args .rbp (by simp)).trans (hb.regs .rbp (by simp [calleeSaved])).symm) + +theorem setup_public_rel : RelCT isa CodeRelated setup Related := by + have trace := setup_rel.mono (P' := CodeRelated) + (fun _ _ h => h.args .rbp (by simp)) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => ⟨setup_ok s h.left, setup_ok t h.right⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + exact prepared_public hp ha hb + +theorem code_rel : RelCT isa CodeRelated code (fun _ _ => True) := + setup_public_rel.seq operation_rel + +end VG.Proof.Argon2.X86_64.FillCompress diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressCall.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressCall.lean new file mode 100644 index 000000000..72355e261 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressCall.lean @@ -0,0 +1,101 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.Compress +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! Invoke the verified compression primitive with narrowed permissions, +retaining the surrounding matrix and derivation frame. -/ + +namespace VG.Proof.Argon2.X86_64.FillCompress + +open VG VG.X86_64 + +structure CallReady (s : State) : Prop where + left : Covers [⟨s.gpr .rdi, 1024⟩] (s.rd ++ s.wr) + right : Covers [⟨s.gpr .rsi, 1024⟩] (s.rd ++ s.wr) + output : Covers [⟨s.gpr .rdx, 1024⟩] s.wr + scratch : Covers [⟨s.gpr .rcx, 4096⟩] s.wr + leftScratch : (⟨s.gpr .rdi, 1024⟩ : Region).Disjoint ⟨s.gpr .rcx, 4096⟩ + rightScratch : (⟨s.gpr .rsi, 1024⟩ : Region).Disjoint ⟨s.gpr .rcx, 4096⟩ + outputScratch : (⟨s.gpr .rdx, 1024⟩ : Region).Disjoint ⟨s.gpr .rcx, 4096⟩ + stackLeft : (below (s.gpr .rsp) 8).Disjoint ⟨s.gpr .rdi, 1024⟩ + stackRight : (below (s.gpr .rsp) 8).Disjoint ⟨s.gpr .rsi, 1024⟩ + stackOutput : (below (s.gpr .rsp) 8).Disjoint ⟨s.gpr .rdx, 1024⟩ + stackScratch : (below (s.gpr .rsp) 8).Disjoint ⟨s.gpr .rcx, 4096⟩ + +structure Called (s t : State) : Prop where + result : Spec.Argon2.blockAt t.mem (s.gpr .rdx) = Spec.Argon2.compress + (Spec.Argon2.blockAt s.mem (s.gpr .rdi)) (Spec.Argon2.blockAt s.mem (s.gpr .rsi)) + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨s.gpr .rdx, 1024⟩, ⟨s.gpr .rcx, 4096⟩, below (s.gpr .rsp) 8] s.mem t.mem + +theorem noSp : NoSp Impl.Argon2.X86_64.compress := by + have h : Impl.Argon2.X86_64.compress.allInstrs (fun i => !Taint.clobbers i .rsp) = true := + by lit_decide + rw [Code.allInstrs_eq, List.all_eq_true] at h + intro i hi + simpa only [Bool.not_eq_true'] using h i hi + +theorem depth : Impl.Argon2.X86_64.compress.depth = 0 := by lit_decide + +theorem call_hyps (s : State) (h : CallReady s) : + compressLocal.pre (s.callEntry.withRegions [⟨s.gpr .rdi, 1024⟩, ⟨s.gpr .rsi, 1024⟩] + [⟨s.gpr .rdx, 1024⟩, ⟨s.gpr .rcx, 4096⟩]) ∧ + Covers [⟨s.gpr .rdi, 1024⟩, ⟨s.gpr .rsi, 1024⟩, + ⟨s.gpr .rdx, 1024⟩, ⟨s.gpr .rcx, 4096⟩] (s.rd ++ s.wr) ∧ + Covers [⟨s.gpr .rdx, 1024⟩, ⟨s.gpr .rcx, 4096⟩] s.wr := by + have g : ∀ r, r ≠ .rsp → s.callEntry.gpr r = s.gpr r := fun _ hr => State.callEntry_gpr s hr + refine ⟨?_, ?_, ?_⟩ + · simp only [compressLocal, State.withRegions_gpr, State.withRegions_rd, + State.withRegions_wr, g _ (by decide : Reg.rdi ≠ .rsp), + g _ (by decide : Reg.rsi ≠ .rsp), g _ (by decide : Reg.rdx ≠ .rsp), + g _ (by decide : Reg.rcx ≠ .rsp), State.callEntry_rsp] + exact ⟨trivial, trivial, h.outputScratch, h.leftScratch, h.rightScratch, + h.stackOutput, h.stackScratch⟩ + · intro p n ⟨r, hr, hc⟩ + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl + · exact h.left p n ⟨_, List.mem_singleton_self _, hc⟩ + · exact h.right p n ⟨_, List.mem_singleton_self _, hc⟩ + · obtain ⟨r, hr, hc⟩ := h.output p n ⟨_, List.mem_singleton_self _, hc⟩ + exact ⟨r, List.mem_append_right _ hr, hc⟩ + · obtain ⟨r, hr, hc⟩ := h.scratch p n ⟨_, List.mem_singleton_self _, hc⟩ + exact ⟨r, List.mem_append_right _ hr, hc⟩ + · intro p n ⟨r, hr, hc⟩ + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact h.output p n ⟨_, List.mem_singleton_self _, hc⟩ + · exact h.scratch p n ⟨_, List.mem_singleton_self _, hc⟩ + +theorem callEntry_block (s : State) (p : Addr) + (h : (below (s.gpr .rsp) 8).Disjoint ⟨p, 1024⟩) : + Spec.Argon2.blockAt s.callEntry.mem p = Spec.Argon2.blockAt s.mem p := by + have frame : Frame [below (s.gpr .rsp) 8] s.mem s.callEntry.mem := by + rw [State.callEntry_mem] + exact (Frame.refl _ _).writeW (r := below (s.gpr .rsp) 8) (by simp) _ + (below_call _ (by decide) (by decide)) + apply Vector.ext + intro i hi + have read := frame.readW (r := ⟨p, 1024⟩) (a := off p (8 * i)) (w := 64) + (Offset.contains_base p (d := 8 * i) (n := 8) (k := 1024) (by omega) (by omega)) + (by intro r hr; simp only [List.mem_singleton] at hr; subst r; exact h.symm) (by decide) + rw [← blockAt_get s.callEntry.mem p ⟨i, hi⟩, ← blockAt_get s.mem p ⟨i, hi⟩] at read + exact read + +theorem call_ok (name : String) (s : State) (h : CallReady s) : + WP isa (.call name Impl.Argon2.X86_64.compress) s (Called s) := by + obtain ⟨pre, cover, writes⟩ := call_hyps s h + refine WP.call (k := compressLocal) compress_correct noSp (by rw [depth]; decide) + pre cover writes ?_ + intro t rd wr regs frame _ ⟨u, memU, regsU, result⟩ + change Spec.Argon2.blockAt u.mem (s.callEntry.gpr .rdx) = Spec.Argon2.compress + (Spec.Argon2.blockAt s.callEntry.mem (s.callEntry.gpr .rdi)) + (Spec.Argon2.blockAt s.callEntry.mem (s.callEntry.gpr .rsi)) at result + rw [State.callEntry_gpr _ (by decide : Reg.rdx ≠ .rsp), + State.callEntry_gpr _ (by decide : Reg.rdi ≠ .rsp), + State.callEntry_gpr _ (by decide : Reg.rsi ≠ .rsp), memU, + callEntry_block s _ h.stackLeft, callEntry_block s _ h.stackRight] at result + rw [depth] at frame + exact ⟨result, regs, rd, wr, frame⟩ + +end VG.Proof.Argon2.X86_64.FillCompress diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressCallCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressCallCT.lean new file mode 100644 index 000000000..62d0b93ec --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressCallCT.lean @@ -0,0 +1,30 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressCall + +/-! Compression calls reveal only their argument addresses and stack pointer. -/ + +namespace VG.Proof.Argon2.X86_64.FillCompress + +open VG VG.X86_64 + +theorem call_rel (name : String) {P : State → State → Prop} + (pre : ∀ s t, P s t → CallReady s ∧ CallReady t ∧ + s.gpr .rdi = t.gpr .rdi ∧ s.gpr .rsi = t.gpr .rsi ∧ + s.gpr .rdx = t.gpr .rdx ∧ s.gpr .rcx = t.gpr .rcx ∧ s.gpr .rsp = t.gpr .rsp) : + RelCT isa P (.call name Impl.Argon2.X86_64.compress) (fun _ _ => True) := by + apply RelCT.callEx (k := compressLocal) compress_correct compress_ct + intro s t hp + obtain ⟨hs, ht, di, si, dx, cx, sp⟩ := pre s t hp + obtain ⟨ps, cs, ws⟩ := call_hyps s hs + obtain ⟨pt, ct, wt⟩ := call_hyps t ht + refine ⟨_, _, _, _, ps, pt, ?_, cs, ws, ct, wt, sp⟩ + change s.callEntry.gpr .rdi = t.callEntry.gpr .rdi ∧ + s.callEntry.gpr .rsi = t.callEntry.gpr .rsi ∧ + s.callEntry.gpr .rdx = t.callEntry.gpr .rdx ∧ + s.callEntry.gpr .rcx = t.callEntry.gpr .rcx + simp only [State.callEntry_gpr _ (by decide : Reg.rdi ≠ .rsp), + State.callEntry_gpr _ (by decide : Reg.rsi ≠ .rsp), + State.callEntry_gpr _ (by decide : Reg.rdx ≠ .rsp), + State.callEntry_gpr _ (by decide : Reg.rcx ≠ .rsp)] + exact ⟨di, si, dx, cx⟩ + +end VG.Proof.Argon2.X86_64.FillCompress diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressLit.lean new file mode 100644 index 000000000..64e99d851 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressLit.lean @@ -0,0 +1,11 @@ +import VerifiedGarbage.Proof.Framework.X86_64.Lit +import VerifiedGarbage.Impl.Argon2.X86_64.FillCompress + +/-! Checked literals for compression and the enclosing argument setup. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.FillCompress.operation +materialize_code Impl.Argon2.X86_64.FillCompress.code + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressOperation.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressOperation.lean new file mode 100644 index 000000000..5bc25e566 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressOperation.lean @@ -0,0 +1,101 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressArgs +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressCall +import VerifiedGarbage.Proof.Argon2.X86_64.FillWriteCover + +/-! Compression followed by first/later-pass writing, preserving the frame +slots and the old destination cell across the compression call. -/ + +namespace VG.Proof.Argon2.X86_64.FillCompress + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillCompress + +def callWrites (s : State) : List Region := + [⟨s.gpr .rdx, 1024⟩, ⟨s.gpr .rcx, 4096⟩, below (s.gpr .rsp) 8] + +def destination (s : State) : Addr := s.mem.readW (off (s.gpr .rbp) 16) 64 + +def pass (s : State) : Addr := s.mem.readW (off (s.gpr .rbp) 0) 64 + +structure OperationReady (s : State) : Prop where + call : CallReady s + frameRead : ∀ d ∈ [0, 16, 248], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8 + workWord : s.mem.readW (off (s.gpr .rbp) 248) 64 = s.gpr .rcx + outputPointer : s.gpr .rcx + 4096 = s.gpr .rdx + destinationWrite : Covers [⟨destination s, 1024⟩] s.wr + frameSafe : ∀ r ∈ callWrites s, (⟨s.gpr .rbp, 272⟩ : Region).Disjoint r + destinationSafe : ∀ r ∈ callWrites s, (⟨destination s, 1024⟩ : Region).Disjoint r + +structure OperationDone (s t : State) : Prop where + block : blockAt t.mem (destination s) = + let next := Spec.Argon2.compress (blockAt s.mem (s.gpr .rdi)) (blockAt s.mem (s.gpr .rsi)) + if pass s = 0 then next else xorBlock next (blockAt s.mem (destination s)) + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (⟨destination s, 1024⟩ :: callWrites s) s.mem t.mem + +theorem frame_word {s t : State} (h : OperationReady s) (called : Called s t) + (d : Nat) (hd : d + 8 ≤ 272) : + t.mem.readW (off (s.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := + called.frame.readW (r := ⟨s.gpr .rbp, 272⟩) + (Offset.contains_base _ hd (by omega)) h.frameSafe (by decide) + +theorem destination_unchanged {s t : State} (h : OperationReady s) (called : Called s t) : + blockAt t.mem (destination s) = blockAt s.mem (destination s) := by + apply Vector.ext + intro i hi + have read : t.mem.readW (off (destination s) (8 * i)) 64 = + s.mem.readW (off (destination s) (8 * i)) 64 := + called.frame.readW (r := ⟨destination s, 1024⟩) + (Offset.contains_base _ (by omega) (by omega)) h.destinationSafe (by decide) + rw [← blockAt_get t.mem (destination s) ⟨i, hi⟩, + ← blockAt_get s.mem (destination s) ⟨i, hi⟩] at read + exact read + +theorem operation_ok (s : State) (h : OperationReady s) : + WP isa operation s (OperationDone s) := by + unfold operation + refine WP.seq ((call_ok _ s h.call).mono ?_) + intro a called + have bp : a.gpr .rbp = s.gpr .rbp := called.regs .rbp (by simp [calleeSaved]) + have reads (d : Nat) (hd : d ∈ [0, 16, 248]) : + InRegions (a.rd ++ a.wr) (off (a.gpr .rbp) d) 8 := by + rw [called.rd, called.wr, bp]; exact h.frameRead d hd + refine WP.seq ((writeArgs_ok a (reads 16 (by simp)) (reads 248 (by simp)) + (reads 0 (by simp))).mono ?_) + rintro b ⟨dest, src, counter, keeps⟩ + have dest' : b.gpr .rdi = destination s := by + rw [dest, bp, frame_word h called 16 (by decide), destination] + have src' : b.gpr .rsi = s.gpr .rdx := by + rw [src, bp, frame_word h called 248 (by decide), h.workWord, h.outputPointer] + have counter' : b.gpr .r9 = pass s := by + rw [counter, bp, frame_word h called 0 (by decide), pass] + have readable : Covers [⟨b.gpr .rsi, 1024⟩] (b.rd ++ b.wr) := by + rw [src', keeps.rd, keeps.wr, called.rd, called.wr] + intro p n hp + obtain ⟨r, hr, hc⟩ := h.call.output p n hp + exact ⟨r, List.mem_append_right _ hr, hc⟩ + have writable : Covers [⟨b.gpr .rdi, 1024⟩] b.wr := by + rw [dest', keeps.wr, called.wr]; exact h.destinationWrite + have sep : (⟨b.gpr .rsi, 1024⟩ : Region).Disjoint ⟨b.gpr .rdi, 1024⟩ := by + rw [src', dest']; exact (h.destinationSafe _ (by simp [callWrites])).symm + refine (FillWrite.code_cover_ok b readable writable sep).mono ?_ + rintro t ⟨value, frame, tk, _⟩ + refine ⟨?_, ?_, tk.2.1.trans (keeps.rd.trans called.rd), + tk.2.2.trans (keeps.wr.trans called.wr), ?_⟩ + · rw [dest', src', counter', keeps.mem, called.result, destination_unchanged h called] at value + exact value + · intro r hr + have ne : r ≠ .rax := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + have nk : r ∉ [Reg.rdi, .rsi, .r9] := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + exact (tk.1 r ne).trans ((keeps.regs r nk).trans (called.regs r hr)) + · rw [dest'] at frame + rw [keeps.mem] at frame + exact (called.frame.mono (by intro r hr; exact List.mem_cons_of_mem _ hr)).trans + (frame.mono (by simp)) + +end VG.Proof.Argon2.X86_64.FillCompress diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressOperationCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressOperationCT.lean new file mode 100644 index 000000000..28cbeeb1d --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressOperationCT.lean @@ -0,0 +1,87 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressOperation +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressCallCT +import VerifiedGarbage.Proof.Argon2.X86_64.FillWriteCT + +/-! Only the compression argument addresses, public frame words and stack +pointer determine the compression-and-write trace. -/ + +namespace VG.Proof.Argon2.X86_64.FillCompress + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FillCompress + +structure Related (s t : State) : Prop where + left : OperationReady s + right : OperationReady t + args : ∀ r ∈ [Reg.rdi, .rsi, .rdx, .rcx, .rsp, .rbp], s.gpr r = t.gpr r + dest : destination s = destination t + counter : pass s = pass t + +structure BeforeWrite (s t : State) : Prop where + leftRead : ∀ d ∈ [0, 16, 248], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8 + rightRead : ∀ d ∈ [0, 16, 248], InRegions (t.rd ++ t.wr) (off (t.gpr .rbp) d) 8 + bases : s.gpr .rbp = t.gpr .rbp + words : ∀ d ∈ [0, 16, 248], + s.mem.readW (off (s.gpr .rbp) d) 64 = t.mem.readW (off (t.gpr .rbp) d) 64 + +theorem called_public {s t a b : State} (hp : Related s t) + (ha : Called s a) (hb : Called t b) : BeforeWrite a b := by + have abp : a.gpr .rbp = s.gpr .rbp := ha.regs .rbp (by simp [calleeSaved]) + have bbp : b.gpr .rbp = t.gpr .rbp := hb.regs .rbp (by simp [calleeSaved]) + refine ⟨?_, ?_, abp.trans ((hp.args .rbp (by simp)).trans bbp.symm), ?_⟩ + · intro d hd + rw [ha.rd, ha.wr, abp]; exact hp.left.frameRead d hd + · intro d hd + rw [hb.rd, hb.wr, bbp]; exact hp.right.frameRead d hd + · intro d hd + rw [abp, bbp] + simp only [List.mem_cons, List.not_mem_nil, or_false] at hd + rcases hd with rfl | rfl | rfl + · rw [frame_word hp.left ha 0 (by decide), frame_word hp.right hb 0 (by decide)] + exact hp.counter + · rw [frame_word hp.left ha 16 (by decide), frame_word hp.right hb 16 (by decide)] + exact hp.dest + · rw [frame_word hp.left ha 248 (by decide), frame_word hp.right hb 248 (by decide), + hp.left.workWord, hp.right.workWord] + exact hp.args .rcx (by simp) + +theorem call_public_rel : RelCT isa Related + (.call Spec.Argon2.compressApi.name VG.Impl.Argon2.X86_64.compress) BeforeWrite := by + have trace := call_rel Spec.Argon2.compressApi.name (P := Related) (fun _ _ hp => + ⟨hp.left.call, hp.right.call, hp.args .rdi (by simp), hp.args .rsi (by simp), + hp.args .rdx (by simp), hp.args .rcx (by simp), hp.args .rsp (by simp)⟩) + have full := trace.wpDep (fun s t hp => + ⟨call_ok _ s hp.left.call, call_ok _ t hp.right.call⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + exact called_public hp ha hb + +theorem writeArgs_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block writeArgs) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +theorem writeArgs_public_rel : RelCT isa BeforeWrite (.block writeArgs) + (fun s t => ∀ r ∈ [Reg.r9, .rdi, .rsi], s.gpr r = t.gpr r) := by + have trace := writeArgs_rel.mono (P' := BeforeWrite) (fun _ _ h => h.bases) (fun _ _ h => h) + have full := trace.wpDep (fun s t hp => + ⟨writeArgs_ok s (hp.leftRead 16 (by simp)) (hp.leftRead 248 (by simp)) (hp.leftRead 0 (by simp)), + writeArgs_ok t (hp.rightRead 16 (by simp)) (hp.rightRead 248 (by simp)) (hp.rightRead 0 (by simp))⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · exact ha.2.2.1.trans ((hp.words 0 (by simp)).trans hb.2.2.1.symm) + · exact ha.1.trans ((hp.words 16 (by simp)).trans hb.1.symm) + · exact ha.2.1.trans ((congrArg (· + (4096 : Addr)) (hp.words 248 (by simp))).trans hb.2.1.symm) + +theorem operation_rel : RelCT isa Related operation (fun _ _ => True) := + call_public_rel.seq (writeArgs_public_rel.seq FillWrite.code_rel) + +end VG.Proof.Argon2.X86_64.FillCompress diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressOperationMx.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressOperationMx.lean new file mode 100644 index 000000000..cc6069fef --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressOperationMx.lean @@ -0,0 +1,14 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressOperation +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressLit + +/-! Baseline compression and the block write preserve all of MXCSR. -/ + +namespace VG.Proof.Argon2.X86_64.FillCompress + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FillCompress + +theorem operation_mx_ok (s : State) (h : OperationReady s) : + WP isa operation s fun t => OperationDone s t ∧ t.mxcsr = s.mxcsr := + WP.mono_mx (by lit_decide) (operation_ok s h) (fun _ done mx => ⟨done, mx⟩) + +end VG.Proof.Argon2.X86_64.FillCompress diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressSetup.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressSetup.lean new file mode 100644 index 000000000..6e3dce766 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillCompressSetup.lean @@ -0,0 +1,162 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressOperation + +/-! Establish compression-and-write invariants from the frame and allocations. -/ + +namespace VG.Proof.Argon2.X86_64.FillCompress + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillCompress + +def work (s : State) : Addr := s.mem.readW (off (s.gpr .rbp) 248) 64 + +def prefixWrites (s : State) : List Region := [⟨off (s.gpr .rbp) 16, 8⟩] + +structure Ready (s : State) : Prop where + frameRead : ∀ d ∈ [0, 16, 248], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8 + frameWrite : InRegions s.wr (off (s.gpr .rbp) 16) 8 + leftRead : Covers [⟨s.gpr .rdi, 1024⟩] (s.rd ++ s.wr) + rightRead : Covers [⟨s.gpr .rsi, 1024⟩] (s.rd ++ s.wr) + destinationWrite : Covers [⟨s.gpr .r10, 1024⟩] s.wr + workWrite : Covers [⟨work s, 5120⟩] s.wr + leftWork : (⟨s.gpr .rdi, 1024⟩ : Region).Disjoint ⟨work s, 5120⟩ + rightWork : (⟨s.gpr .rsi, 1024⟩ : Region).Disjoint ⟨work s, 5120⟩ + destinationWork : (⟨s.gpr .r10, 1024⟩ : Region).Disjoint ⟨work s, 5120⟩ + frameWork : (⟨s.gpr .rbp, 272⟩ : Region).Disjoint ⟨work s, 5120⟩ + leftFrame : (⟨s.gpr .rdi, 1024⟩ : Region).Disjoint ⟨s.gpr .rbp, 272⟩ + rightFrame : (⟨s.gpr .rsi, 1024⟩ : Region).Disjoint ⟨s.gpr .rbp, 272⟩ + destinationFrame : (⟨s.gpr .r10, 1024⟩ : Region).Disjoint ⟨s.gpr .rbp, 272⟩ + stackLeft : (below (s.gpr .rsp) 8).Disjoint ⟨s.gpr .rdi, 1024⟩ + stackRight : (below (s.gpr .rsp) 8).Disjoint ⟨s.gpr .rsi, 1024⟩ + stackWork : (below (s.gpr .rsp) 8).Disjoint ⟨work s, 5120⟩ + destinationStack : (⟨s.gpr .r10, 1024⟩ : Region).Disjoint (below (s.gpr .rsp) 8) + frameStack : (⟨s.gpr .rbp, 272⟩ : Region).Disjoint (below (s.gpr .rsp) 8) + +structure Prepared (s t : State) : Prop where + ready : OperationReady t + dest : destination t = s.gpr .r10 + counter : pass t = pass s + scratch : t.gpr .rcx = work s + output : t.gpr .rdx = work s + 4096 + left : t.gpr .rdi = s.gpr .rdi + right : t.gpr .rsi = s.gpr .rsi + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (prefixWrites s) s.mem t.mem + leftBlock : blockAt t.mem (t.gpr .rdi) = blockAt s.mem (s.gpr .rdi) + rightBlock : blockAt t.mem (t.gpr .rsi) = blockAt s.mem (s.gpr .rsi) + oldBlock : blockAt t.mem (destination t) = blockAt s.mem (s.gpr .r10) + +theorem block_frame {m m' : Mem} {rs : List Region} (hf : Frame rs m m') + (p : Addr) (sep : ∀ r ∈ rs, (⟨p, 1024⟩ : Region).Disjoint r) : + blockAt m' p = blockAt m p := by + apply Vector.ext + intro i hi + have read : m'.readW (off p (8 * i)) 64 = m.readW (off p (8 * i)) 64 := + hf.readW (r := ⟨p, 1024⟩) (Offset.contains_base p (by omega) (by omega)) sep (by decide) + rw [← blockAt_get m' p ⟨i, hi⟩, ← blockAt_get m p ⟨i, hi⟩] at read + exact read + +theorem work_cover (s : State) (h : Ready s) (d n : Nat) (hd : d + n ≤ 5120) : + Covers [⟨off (work s) d, n⟩] s.wr := by + have sub : Covers [⟨off (work s) d, n⟩] [⟨work s, 5120⟩] := by + apply Covers.of_sub + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact ⟨⟨work s, 5120⟩, by simp, d, rfl, hd⟩ + exact fun p n hp => h.workWrite p n (sub p n hp) + +theorem prepared_of_setup (s a b : State) (h : Ready s) + (mem : a.mem = s.mem.writeW (off (s.gpr .rbp) 16) (s.gpr .r10)) + (regs : a.gpr = s.gpr) (rd : a.rd = s.rd) (wr : a.wr = s.wr) + (scratch : b.gpr .rcx = a.mem.readW (off (a.gpr .rbp) 248) 64) + (output : b.gpr .rdx = a.mem.readW (off (a.gpr .rbp) 248) 64 + 4096) + (keeps : Divide.Keeps [.rcx, .rdx] a b) : Prepared s b := by + have g (r : Reg) (hr : r ∉ [Reg.rcx, .rdx]) : b.gpr r = s.gpr r := + (keeps.regs r hr).trans (congrFun regs r) + have brd : b.rd = s.rd := keeps.rd.trans rd + have bwr : b.wr = s.wr := keeps.wr.trans wr + have bm : b.mem = s.mem.writeW (off (s.gpr .rbp) 16) (s.gpr .r10) := keeps.mem.trans mem + have unchanged (d : Nat) (sep : d + 8 ≤ 16 ∨ 24 ≤ d) (bound : d + 8 ≤ 272) : + b.mem.readW (off (b.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [bm, g .rbp (by decide)] + exact Mem.readW_writeW_sep (Offset.sep _ sep (by omega) (by decide)) (by decide) + have work' : b.gpr .rcx = work s := by + rw [scratch, regs, mem] + exact Mem.readW_writeW_sep (Offset.sep _ (by decide) (by decide) (by decide)) (by decide) + have out' : b.gpr .rdx = work s + 4096 := by + rw [output, regs, mem, + Mem.readW_writeW_sep (Offset.sep _ (by decide) (by decide) (by decide)) (by decide), work] + have dest : destination b = s.gpr .r10 := by + unfold destination + rw [bm, g .rbp (by decide), Mem.readW_writeW_self64] + have counter : pass b = pass s := unchanged 0 (by decide) (by decide) + have frame : Frame (prefixWrites s) s.mem b.mem := by + rw [bm] + exact (Frame.refl _ _).writeW (r := ⟨off (s.gpr .rbp) 16, 8⟩) (by simp [prefixWrites]) _ + (Region.contains_self _ _) + have cellFrame (p : Addr) (sep : (⟨p, 1024⟩ : Region).Disjoint ⟨s.gpr .rbp, 272⟩) : + blockAt b.mem p = blockAt s.mem p := + block_frame frame p (by + intro r hr + simp only [prefixWrites, List.mem_singleton] at hr + subst r + exact sep.sub_right (Offset.sub_base _ (by decide))) + have tempSub : Region.Sub ⟨work s + 4096, 1024⟩ ⟨work s, 5120⟩ := + Offset.sub_base _ (by decide) + have scratchSub : Region.Sub ⟨work s, 4096⟩ ⟨work s, 5120⟩ := Region.sub_prefix (by decide) + refine ⟨?_, dest, counter, work', out', g .rdi (by decide), g .rsi (by decide), ?_, brd, bwr, + frame, ?_, ?_, ?_⟩ + · refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · rw [g .rdi (by decide), brd, bwr]; exact h.leftRead + · rw [g .rsi (by decide), brd, bwr]; exact h.rightRead + · rw [out', bwr]; exact work_cover s h 4096 1024 (by decide) + · rw [work', bwr] + simpa only [off, show BitVec.ofNat 64 0 = 0#64 from rfl, BitVec.add_zero] + using work_cover s h 0 4096 (by decide) + · rw [g .rdi (by decide), work']; exact h.leftWork.sub_right scratchSub + · rw [g .rsi (by decide), work']; exact h.rightWork.sub_right scratchSub + · rw [out', work']; exact Offset.disjoint_base _ (by decide) (by decide) + · rw [g .rsp (by decide), g .rdi (by decide)]; exact h.stackLeft + · rw [g .rsp (by decide), g .rsi (by decide)]; exact h.stackRight + · rw [g .rsp (by decide), out']; exact h.stackWork.sub_right tempSub + · rw [g .rsp (by decide), work']; exact h.stackWork.sub_right scratchSub + · intro d hd + rw [brd, bwr, g .rbp (by decide)]; exact h.frameRead d hd + · rw [unchanged 248 (by decide) (by decide), work']; rfl + · rw [work', out'] + · rw [dest, bwr]; exact h.destinationWrite + · intro r hr + simp only [callWrites, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · rw [g .rbp (by decide), out']; exact h.frameWork.sub_right tempSub + · rw [g .rbp (by decide), work']; exact h.frameWork.sub_right scratchSub + · rw [g .rbp (by decide), g .rsp (by decide)]; exact h.frameStack + · intro r hr + rw [dest] + simp only [callWrites, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · rw [out']; exact h.destinationWork.sub_right tempSub + · rw [work']; exact h.destinationWork.sub_right scratchSub + · rw [g .rsp (by decide)]; exact h.destinationStack + · intro r hr + apply g r + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + · rw [g .rdi (by decide)]; exact cellFrame _ h.leftFrame + · rw [g .rsi (by decide)]; exact cellFrame _ h.rightFrame + · rw [dest]; exact cellFrame _ h.destinationFrame + +theorem setup_ok (s : State) (h : Ready s) : + WP isa setup s (Prepared s) := by + unfold setup + refine WP.seq ((saveCurrent_ok s h.frameWrite).mono ?_) + rintro a ⟨mem, regs, rd, wr, _⟩ + have read : InRegions (a.rd ++ a.wr) (off (a.gpr .rbp) 248) 8 := by + rw [rd, wr, regs]; exact h.frameRead 248 (by simp) + refine (compressArgs_ok a read).mono ?_ + rintro b ⟨scratch, output, keeps⟩ + exact prepared_of_setup s a b h mem regs rd wr scratch output keeps + +end VG.Proof.Argon2.X86_64.FillCompress diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillContext.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillContext.lean new file mode 100644 index 000000000..2bbb6335d --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillContext.lean @@ -0,0 +1,61 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSegment + +/-! A segment context allows its starting and final indices, including an empty suffix. -/ + +namespace VG.Proof.Argon2.X86_64.FillContext + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Parameters (p : Params) (pass lane slice : Nat) : Prop where + lanesPositive : 0 < p.lanes + lanesBound : p.lanes < 2 ^ 32 + memoryMinimum : 8 * p.lanes ≤ p.memory + memoryBound : p.memory < 2 ^ 32 + passBound : pass < 2 ^ 32 + laneBound : lane < p.lanes + sliceBound : slice < 4 + +structure Ready (p : Params) (pass lane slice index old : Nat) (s : State) : Prop where + parameters : Parameters p pass lane slice + layout : FillKernel.Layout p s + cache : AddressCache.Invariant p pass lane slice old s + matrixWork : (⟨FillKernel.matrix s, p.blocks * 1024⟩ : Region).Disjoint ⟨AddressCalls.work s, 8192⟩ + position : ReferenceMap.Position p lane slice index s + lanesWord : s.mem.readW (off (s.gpr .rbp) 184) 64 = BitVec.ofNat 64 p.lanes + +theorem Ready.activate {p : Params} {pass lane slice index old : Nat} {s : State} + (h : Ready p pass lane slice index old s) (bound : index < p.segmentLen) + (active : pass ≠ 0 ∨ slice ≠ 0 ∨ 2 ≤ index) : RandomSource.Ready p pass lane slice index old s := + ⟨⟨h.layout, ⟨h.parameters.lanesPositive, h.parameters.lanesBound, h.parameters.memoryMinimum, + h.parameters.memoryBound, h.parameters.passBound, h.parameters.laneBound, h.parameters.sliceBound, + bound, active⟩, h.position, h.cache.words.passWord, h.lanesWord⟩, h.cache, h.matrixWork⟩ + +theorem Parameters.segment_bound {p : Params} {pass lane slice : Nat} (h : Parameters p pass lane slice) : + 2 ≤ p.segmentLen ∧ p.segmentLen < 2 ^ 64 := by + have minimum := Proof.Argon2.segmentLen_ge_two p h.lanesPositive h.memoryMinimum + have le : p.segmentLen ≤ p.blocks := by + have blocks := Proof.Argon2.blocks_lanes p h.lanesPositive + have segments := Proof.Argon2.laneLen_segments p h.lanesPositive + have laneLe : p.laneLen ≤ p.blocks := by rw [blocks]; exact Nat.le_mul_of_pos_left _ h.lanesPositive + omega + exact ⟨minimum, Nat.lt_of_le_of_lt le + (Nat.lt_trans (Nat.lt_of_le_of_lt (Proof.Argon2.blocks_le_memory p) h.memoryBound) (by decide))⟩ + +theorem Ready.of_keeps {p : Params} {pass lane slice index old : Nat} {s t : State} + (h : Ready p pass lane slice index old s) (k : Divide.Keeps ReferenceMap.changed s t) : + Ready p pass lane slice index old t := by + have bp := k.regs .rbp (by decide) + have base : FillKernel.matrix t = FillKernel.matrix s := by unfold FillKernel.matrix; rw [k.mem, bp] + have work : AddressCalls.work t = AddressCalls.work s := by unfold AddressCalls.work; rw [k.mem, bp] + refine ⟨h.parameters, h.layout.of_preserved bp (k.regs .rsp (by decide)) base work k.rd k.wr, + h.cache.of_keeps k, ?_, h.position.of_keeps k, ?_⟩ + · rw [base, work]; exact h.matrixWork + · rw [k.mem, bp]; exact h.lanesWord + +theorem finished_context {p : Params} {pass lane slice : Nat} {s t : State} {state : FillState} + (parameters : Parameters p pass lane slice) (h : FillSegment.Finished s t p pass lane slice state) : + ∃ old, Ready p pass lane slice p.segmentLen old t := by + obtain ⟨old, cache⟩ := h.cache + exact ⟨old, parameters, h.layout, cache, h.matrixWork, h.position, h.lanesWord⟩ + +end VG.Proof.Argon2.X86_64.FillContext diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillFinish.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillFinish.lean new file mode 100644 index 000000000..02b4dcc16 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillFinish.lean @@ -0,0 +1,54 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillFinish +import VerifiedGarbage.Proof.Argon2.X86_64.FillFinishReady +import VerifiedGarbage.Proof.Argon2.X86_64.FinishStage + +/-! The complete filling and finalization stages produce the reviewed final tag. -/ + +namespace VG.Proof.Argon2.X86_64.FillFinish + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState +open VG.Spec.Blake2 (bytesAt) + +structure Ready (p : Params) (s : State) : Prop where + filling : FillIterations.Ready p 0 s + finish : Finish.Ready p s + positive : 0 < p.passes + +def writes (s : State) (p : Params) : List Region := FillIterations.writes s p ++ Finish.writes s p + +structure Done (s t : State) (p : Params) (state : FillState) : Prop where + digest : bytesAt t.mem (FinalOutput.output s) p.tagLen = + Spec.Argon2.finish p (Proof.Argon2.iterations p 0 p.passes state).memory + regs : ∀ r ∈ calleeSaved, r ≠ .rbx → r ≠ .r14 → r ≠ .r15 → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (writes s p) s.mem t.mem + +theorem code_ok (v : Proof.Blake2.X86_64.Backend) (name : String) (s : State) (p : Params) + (h : Ready p s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (matrix s) p.blocks state.memory) : + WP isa (Impl.Argon2.X86_64.FillFinish.code name (HPrime.hash v)) s (Done s · p state) := by + unfold Impl.Argon2.X86_64.FillFinish.code + refine WP.seq ((FillIterations.loop_ok p.passes s p 0 h.filling state represented h.positive (Nat.zero_add _)).mono ?_) + intro a filled + refine (Finish.code_ok v name a p (finish_ready h.filling h.finish filled) _ filled.represented).mono ?_ + intro t finished + have output : FinalOutput.output a = FinalOutput.output s := filled.frame_word h.filling 256 (by decide) (by decide) + have work : FinalOutput.work a = FinalOutput.work s := filled.frame_word h.filling 248 (by decide) (by decide) + have base : matrix a = matrix s := filled.matrix + refine ⟨?_, fun r hr bx sl ix => (finished.regs r hr bx).trans (filled.regs r hr bx sl ix), + finished.rd.trans filled.rd, finished.wr.trans filled.wr, ?_⟩ + · have digest := finished.digest + rw [output] at digest; exact digest + · have firstFrame : Frame (writes s p) s.mem a.mem := filled.frame.sub (by + intro r hr + exact ⟨r, List.mem_append_left _ hr, fun _ h => h⟩) + have lastFrame := finished.frame + rw [Finish.writes, base, output, work, + filled.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide)] at lastFrame + apply firstFrame.trans + apply lastFrame.sub + intro r hr + exact ⟨r, List.mem_append_right _ hr, fun _ h => h⟩ + +end VG.Proof.Argon2.X86_64.FillFinish diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillFinishCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillFinishCT.lean new file mode 100644 index 000000000..63afa599a --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillFinishCT.lean @@ -0,0 +1,60 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillFinish +import VerifiedGarbage.Proof.Argon2.X86_64.FillIterationsCT +import VerifiedGarbage.Proof.Argon2.X86_64.FinishStageCT + +/-! Filling and finalization expose only the reviewed filling reference log. -/ + +namespace VG.Proof.Argon2.X86_64.FillFinish + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState + +structure Related (p : Params) (leftState rightState : FillState) (s t : State) : Prop where + left : Ready p s + right : Ready p t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + matrices : matrix s = matrix t + outputs : FinalOutput.output s = FinalOutput.output t + works : FinalOutput.work s = FinalOutput.work t + leftMatrix : Proof.Argon2.Represents s.mem (matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (matrix t) p.blocks rightState.memory + indices : (Proof.Argon2.iterations p 0 p.passes leftState).indices = + (Proof.Argon2.iterations p 0 p.passes rightState).indices + +theorem code_rel (v : Proof.Blake2.X86_64.Backend) (name : String) (p : Params) + (leftState rightState : FillState) : + RelCT isa (Related p leftState rightState) (Impl.Argon2.X86_64.FillFinish.code name (HPrime.hash v)) + (fun _ _ => True) := by + intro s t ts tt a b hp ea eb + cases ea with + | seq fillA finishA => + cases eb with + | seq fillB finishB => + have related : FillIterations.Related p 0 p.passes leftState rightState s t := + ⟨⟨hp.left.filling, hp.right.filling, hp.bases, hp.stacks, hp.matrices, hp.works, hp.leftMatrix, hp.rightMatrix⟩, + hp.leftMatrix, hp.rightMatrix, hp.indices⟩ + obtain ⟨fillTrace, _⟩ := FillIterations.loop_rel p 0 p.passes leftState rightState hp.left.positive + (Nat.zero_add _) _ _ _ _ _ _ related fillA fillB + obtain ⟨_, sa, runA, doneA⟩ := FillIterations.loop_ok p.passes s p 0 hp.left.filling leftState + hp.leftMatrix hp.left.positive (Nat.zero_add _) + obtain ⟨_, sb, runB, doneB⟩ := FillIterations.loop_ok p.passes t p 0 hp.right.filling rightState + hp.rightMatrix hp.right.positive (Nat.zero_add _) + obtain ⟨_, rfl⟩ := Exec.det fillA runA + obtain ⟨_, rfl⟩ := Exec.det fillB runB + have finalRelated : Finish.Related p (Proof.Argon2.iterations p 0 p.passes leftState).memory + (Proof.Argon2.iterations p 0 p.passes rightState).memory _ _ := + ⟨finish_ready hp.left.filling hp.left.finish doneA, finish_ready hp.right.filling hp.right.finish doneB, + (doneA.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).trans + (hp.bases.trans (doneB.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).symm), + (doneA.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).trans + (hp.stacks.trans (doneB.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).symm), + doneA.matrix.trans (hp.matrices.trans doneB.matrix.symm), + (doneA.frame_word hp.left.filling 256 (by decide) (by decide)).trans + (hp.outputs.trans (doneB.frame_word hp.right.filling 256 (by decide) (by decide)).symm), + (doneA.frame_word hp.left.filling 248 (by decide) (by decide)).trans + (hp.works.trans (doneB.frame_word hp.right.filling 248 (by decide) (by decide)).symm), + doneA.represented, doneB.represented⟩ + obtain ⟨finishTrace, _⟩ := Finish.code_rel v name p _ _ _ _ _ _ _ _ finalRelated finishA finishB + exact ⟨by rw [fillTrace, finishTrace], trivial⟩ + +end VG.Proof.Argon2.X86_64.FillFinish diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillFinishReady.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillFinishReady.lean new file mode 100644 index 000000000..b6dc2b481 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillFinishReady.lean @@ -0,0 +1,39 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillIterations +import VerifiedGarbage.Proof.Argon2.X86_64.FinishReady + +/-! The complete filling loop retains the original final-call allocations and public metadata. -/ + +namespace VG.Proof.Argon2.X86_64.FillFinish + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState + +theorem finish_ready {s t : State} {p : Params} {state : FillState} + (filling : FillIterations.Ready p 0 s) (ready : Finish.Ready p s) + (done : FillIterations.Finished s t p state) : Finish.Ready p t := by + have bp := done.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide) + have sp := done.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide) + have base : matrix t = matrix s := done.matrix + have output : FinalOutput.output t = FinalOutput.output s := done.frame_word filling 256 (by decide) (by decide) + have work : FinalOutput.work t = FinalOutput.work s := done.frame_word filling 248 (by decide) (by decide) + constructor + · have a := ready.reduction.allocation + refine ⟨⟨a.positive, a.minimum, a.bound, ?_, ?_, ?_, ?_⟩, ready.reduction.lanesBound, ?_, ?_⟩ + · rw [done.rd, done.wr, bp]; exact a.read + · rw [base, done.wr]; exact a.write + · rw [base, bp]; exact a.frame + · exact (done.regs .r12 (by simp [calleeSaved]) (by decide) (by decide) (by decide)).trans a.length + · rw [done.rd, done.wr, bp]; exact ready.reduction.lanesRead + · exact (done.frame_word filling 184 (by decide) (by decide)).trans ready.reduction.lanesWord + · refine ⟨ready.output.positive, ready.output.bound, ?_, + (done.frame_word filling 264 (by decide) (by decide)).trans ready.output.tagWord, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · rw [done.rd, done.wr, bp]; exact ready.output.reads + · rw [base, done.rd, done.wr]; exact ready.output.input + · rw [output, done.wr]; exact ready.output.outputWrite + · rw [work, done.wr]; exact ready.output.workWrite + · rw [base, work]; exact ready.output.inputWork + · rw [output, work]; exact ready.output.outputWork + · rw [sp, base]; exact ready.output.stackInput + · rw [sp, output]; exact ready.output.stackOutput + · rw [sp, work]; exact ready.output.stackWork + +end VG.Proof.Argon2.X86_64.FillFinish diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillHeader.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillHeader.lean new file mode 100644 index 000000000..531a38abe --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillHeader.lean @@ -0,0 +1,71 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillLanes + +/-! Allocation and normalized header across lane, slice and pass boundaries. -/ + +namespace VG.Proof.Argon2.X86_64.FillHeader + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Ready (p : Params) (pass lane slice : Nat) (s : State) : Prop where + layout : FillKernel.Layout p s + addressLayout : AddressCalls.Ready s + reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8 + write : InRegions s.wr (off (s.gpr .rbp) 8) 8 + words : ∃ old, AddressHeader.Words p pass lane slice old s + matrixWork : (⟨FillKernel.matrix s, p.blocks * 1024⟩ : Region).Disjoint ⟨AddressCalls.work s, 8192⟩ + laneLength : s.gpr .r12 = BitVec.ofNat 64 p.laneLen + segmentLength : s.gpr .r13 = BitVec.ofNat 64 p.segmentLen + lanesWord : s.mem.readW (off (s.gpr .rbp) 184) 64 = BitVec.ofNat 64 p.lanes + +theorem of_segment_ready {p : Params} {pass lane slice : Nat} {s : State} + (h : SegmentSetup.Ready p pass lane slice s) : Ready p pass lane slice s := + ⟨h.layout, h.addressLayout, h.reads, h.write, h.words, h.matrixWork, h.laneLength, h.segmentLength, h.lanesWord⟩ + +theorem Ready.segment {p : Params} {pass lane slice : Nat} {s : State} + (h : Ready p pass lane slice s) (parameters : FillContext.Parameters p pass lane slice) : + SegmentSetup.Ready p pass lane slice s := + ⟨parameters, h.layout, h.addressLayout, h.reads, h.write, h.words, + h.matrixWork, h.laneLength, h.segmentLength, h.lanesWord⟩ + +theorem Ready.of_state {p : Params} {pass lane slice newLane newSlice : Nat} {s t : State} + (h : Ready p pass lane slice s) + (regs : ∀ r ∈ [Reg.rbp, .rsp, .r12, .r13], t.gpr r = s.gpr r) + (mem : t.mem = s.mem) (rd : t.rd = s.rd) (wr : t.wr = s.wr) + (laneWord : t.gpr .rbx = BitVec.ofNat 64 newLane) (sliceWord : t.gpr .r14 = BitVec.ofNat 64 newSlice) : + Ready p pass newLane newSlice t := by + have bp := regs .rbp (by simp) + have sp := regs .rsp (by simp) + have base : FillKernel.matrix t = FillKernel.matrix s := by unfold FillKernel.matrix; rw [mem, bp] + have work : AddressCalls.work t = AddressCalls.work s := by unfold AddressCalls.work; rw [mem, bp] + refine ⟨h.layout.of_preserved bp sp base work rd wr, ?_, ?_, ?_, ?_, ?_, + (regs .r12 (by simp)).trans h.laneLength, (regs .r13 (by simp)).trans h.segmentLength, ?_⟩ + · constructor + · rw [rd, wr, bp]; exact h.addressLayout.frameRead + · rw [wr, work]; exact h.addressLayout.workWrite + · rw [bp, work]; exact h.addressLayout.frameWork + · rw [bp, sp]; exact h.addressLayout.frameStack + · rw [sp, work]; exact h.addressLayout.stackWork + · rw [rd, wr, bp]; exact h.reads + · rw [wr, bp]; exact h.write + · obtain ⟨old, words⟩ := h.words + refine ⟨old, ?_, laneWord, sliceWord, ?_, ?_, ?_, ?_⟩ + all_goals rw [mem, bp] + · exact words.passWord + · exact words.blocksWord + · exact words.passesWord + · exact words.variantWord + · exact words.counterWord + · rw [base, work]; exact h.matrixWork + · rw [mem, bp]; exact h.lanesWord + +theorem of_lanes_finished {p : Params} {pass slice : Nat} {s t : State} {state : FillState} + (h : FillLanes.Finished s t p pass slice state) : Ready p pass p.lanes slice t := by + obtain ⟨lane, a, _, ready, keeps⟩ := h.header + obtain ⟨old, words⟩ := ready.words + apply (of_segment_ready ready).of_state _ keeps.mem keeps.rd keeps.wr h.laneWord + ((keeps.regs .r14 (by decide)).trans words.sliceWord) + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl <;> exact keeps.regs _ (by decide) + +end VG.Proof.Argon2.X86_64.FillHeader diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIndex.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIndex.lean new file mode 100644 index 000000000..8d4c39437 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIndex.lean @@ -0,0 +1,81 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillSegment +import VerifiedGarbage.Proof.Argon2.X86_64.FillBlock + +/-! Public segment-index advancement retains the filling and cache allocations. -/ + +namespace VG.Proof.Argon2.X86_64.FillSegment + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillSegment + +theorem advance_ok (s : State) : WP isa (.block advance) s fun t => + t.gpr .r15 = s.gpr .r15 + 1 ∧ + t.cf = decide ((s.gpr .r15 + 1).toNat < (s.gpr .r13).toNat) ∧ + Divide.Keeps [.r15] s t := by + apply WP.of_runBlock + simp only [advance, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, execAlu, + RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, RegUpd.cf_arithFlags, + show BitVec.signExtend 64 (1 : BitVec 32) = (1 : Addr) from rfl, + reduceCtorEq, ite_true, ite_false, Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr, ite_false] + all_goals rfl + +theorem advance_nat_ok (s : State) (p : Params) (pass lane slice index : Nat) + (h : FillKernel.Ready p pass lane slice index s) : WP isa (.block advance) s fun t => + t.gpr .r15 = BitVec.ofNat 64 (index + 1) ∧ + t.cf = decide (index + 1 < p.segmentLen) ∧ Divide.Keeps [.r15] s t := by + refine (advance_ok s).mono ?_ + rintro t ⟨value, flag, keeps⟩ + have added : s.gpr .r15 + 1 = BitVec.ofNat 64 (index + 1) := by + rw [h.position.index, BitVec.ofNat_add]; rfl + have endBound : p.segmentLen < 2 ^ 64 := Nat.lt_of_le_of_lt h.bounds.segment_le_lane + (Nat.lt_trans h.bounds.laneLength_bound (by decide)) + have indexBound := h.bounds.indexBound + refine ⟨value.trans added, ?_, keeps⟩ + rw [flag, added, h.position.segmentLength, + ReferenceMap.word_nat (index + 1) (by omega), ReferenceMap.word_nat p.segmentLen endBound] + +theorem next_ready {p : Params} {pass lane slice index old : Nat} {s t : State} + (h : RandomSource.Ready p pass lane slice index old s) + (k : Divide.Keeps [.r15] s t) (value : t.gpr .r15 = BitVec.ofNat 64 (index + 1)) + (active : index + 1 < p.segmentLen) : RandomSource.Ready p pass lane slice (index + 1) old t := by + have bp := k.regs .rbp (by decide) + have sp := k.regs .rsp (by decide) + have base : FillKernel.matrix t = FillKernel.matrix s := by unfold FillKernel.matrix; rw [k.mem, bp] + have work : AddressCalls.work t = AddressCalls.work s := by unfold AddressCalls.work; rw [k.mem, bp] + have kernelWork : FillKernel.work t = FillKernel.work s := work + refine ⟨?_, h.cache.of_state (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl <;> exact k.regs _ (by decide)) k.mem k.rd k.wr, ?_⟩ + · refine ⟨?_, ?_, ?_, ?_, ?_⟩ + · constructor + · rw [k.rd, k.wr, bp]; exact h.filling.layout.frameRead + · rw [k.wr, bp]; exact h.filling.layout.frameWrite + · rw [base, k.wr]; exact h.filling.layout.matrixWrite + · rw [kernelWork, k.wr]; exact h.filling.layout.workWrite + · rw [base, kernelWork]; exact h.filling.layout.matrixWork + · rw [base, bp]; exact h.filling.layout.matrixFrame + · rw [base, sp]; exact h.filling.layout.matrixStack + · rw [bp, kernelWork]; exact h.filling.layout.frameWork + · rw [bp, sp]; exact h.filling.layout.frameStack + · rw [sp, kernelWork]; exact h.filling.layout.stackWork + · have bounds := h.filling.bounds + refine ⟨bounds.lanesPositive, bounds.lanesBound, bounds.memoryMinimum, bounds.memoryBound, + bounds.passBound, bounds.laneBound, bounds.sliceBound, active, ?_⟩ + rcases bounds.active with hp | hs | hi + · exact Or.inl hp + · exact Or.inr (Or.inl hs) + · exact Or.inr (Or.inr (by omega)) + · exact ⟨(k.regs .rbx (by decide)).trans h.filling.position.current, + (k.regs .r12 (by decide)).trans h.filling.position.laneLength, + (k.regs .r13 (by decide)).trans h.filling.position.segmentLength, + (k.regs .r14 (by decide)).trans h.filling.position.slice, value⟩ + · rw [k.mem, bp]; exact h.filling.passWord + · rw [k.mem, bp]; exact h.filling.lanesWord + · rw [base, work]; exact h.matrixWork + +end VG.Proof.Argon2.X86_64.FillSegment diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIteration.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIteration.lean new file mode 100644 index 000000000..bb145ae96 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIteration.lean @@ -0,0 +1,32 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillIterationPrepare + +/-! One complete filling pass against the reviewed specification. -/ + +namespace VG.Proof.Argon2.X86_64.FillIteration + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem code_ok (s : State) (p : Params) (pass : Nat) (h : Ready p pass s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) : + WP isa Impl.Argon2.X86_64.FillIteration.code s (FillSlices.Finished s · p pass (fillPass p state pass)) := by + unfold Impl.Argon2.X86_64.FillIteration.code + refine WP.seq ((setup_ok s p pass h).mono ?_) + intro a prepared + have bp := prepared.keeps.regs .rbp (by decide) + have base : FillKernel.matrix a = FillKernel.matrix s := by unfold FillKernel.matrix; rw [prepared.keeps.mem, bp] + have work : AddressCalls.work a = AddressCalls.work s := by unfold AddressCalls.work; rw [prepared.keeps.mem, bp] + have representedA : Proof.Argon2.Represents a.mem (FillKernel.matrix a) p.blocks state.memory := by + rw [prepared.keeps.mem, base]; exact represented + refine (FillSlices.pass_ok a p pass prepared.ready state representedA).mono ?_ + intro t finished + refine ⟨finished.represented, finished.matrix.trans base, finished.work.trans work, finished.header, + finished.rd.trans prepared.keeps.rd, finished.wr.trans prepared.keeps.wr, ?_, + finished.mxcsr.trans prepared.keeps.mxcsr, ?_⟩ + · have frame := finished.frame + rw [FillBlock.writes, base, work, prepared.keeps.regs .rsp (by decide), bp, prepared.keeps.mem] at frame + exact frame + · intro r hr bx sl ix + have ne : r ∉ [Reg.r14] := by simp only [List.mem_cons, List.not_mem_nil, or_false]; exact sl + exact (finished.regs r hr bx sl ix).trans (prepared.keeps.regs r ne) + +end VG.Proof.Argon2.X86_64.FillIteration diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationCT.lean new file mode 100644 index 000000000..10abf5107 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationCT.lean @@ -0,0 +1,49 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillIteration +import VerifiedGarbage.Proof.Argon2.X86_64.FillSlicesCT + +/-! Pass setup retains public pointers and exposes only the reviewed pass log. -/ + +namespace VG.Proof.Argon2.X86_64.FillIteration + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Related (p : Params) (pass : Nat) (leftState rightState : FillState) (s t : State) : Prop where + left : Ready p pass s + right : Ready p pass t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + matrices : FillKernel.matrix s = FillKernel.matrix t + work : AddressCalls.work s = AddressCalls.work t + leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory + indices : (fillPass p leftState pass).indices = (fillPass p rightState pass).indices + +theorem setup_trace : RelCT isa (fun _ _ : State => True) (.block Impl.Argon2.X86_64.FillIteration.setup) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by intro r hr; simp at hr)) (by taint_decide) + +theorem setup_public_rel (p : Params) (pass : Nat) (leftState rightState : FillState) : + RelCT isa (Related p pass leftState rightState) (.block Impl.Argon2.X86_64.FillIteration.setup) + (fun s t => FillSlices.NextRelated p pass 0 leftState rightState s t ∧ + (fillPass p leftState pass).indices = (fillPass p rightState pass).indices) := by + have trace := setup_trace.mono (P' := Related p pass leftState rightState) + (fun _ _ _ => trivial) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => ⟨setup_ok s p pass h.left, setup_ok t p pass h.right⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + refine ⟨⟨ha.ready, hb.ready, ?_, ?_, ?_, ?_, ?_, ?_⟩, hp.indices⟩ + · rw [ha.keeps.regs .rbp (by decide), hb.keeps.regs .rbp (by decide)]; exact hp.bases + · rw [ha.keeps.regs .rsp (by decide), hb.keeps.regs .rsp (by decide)]; exact hp.stacks + · unfold FillKernel.matrix + rw [ha.keeps.mem, hb.keeps.mem, ha.keeps.regs .rbp (by decide), hb.keeps.regs .rbp (by decide)]; exact hp.matrices + · unfold AddressCalls.work + rw [ha.keeps.mem, hb.keeps.mem, ha.keeps.regs .rbp (by decide), hb.keeps.regs .rbp (by decide)]; exact hp.work + · unfold FillKernel.matrix; rw [ha.keeps.mem, ha.keeps.regs .rbp (by decide)]; exact hp.leftMatrix + · unfold FillKernel.matrix; rw [hb.keeps.mem, hb.keeps.regs .rbp (by decide)]; exact hp.rightMatrix + +theorem code_rel (p : Params) (pass : Nat) (leftState rightState : FillState) : + RelCT isa (Related p pass leftState rightState) Impl.Argon2.X86_64.FillIteration.code (fun _ _ => True) := + (setup_public_rel p pass leftState rightState).seq (FillSlices.pass_rel p pass leftState rightState) + +end VG.Proof.Argon2.X86_64.FillIteration diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationPrepare.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationPrepare.lean new file mode 100644 index 000000000..f7c1378f6 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationPrepare.lean @@ -0,0 +1,31 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillIteration +import VerifiedGarbage.Proof.Argon2.X86_64.FillSlices + +/-! Start a pass at slice zero regardless of its incoming lane and slice coordinates. -/ + +namespace VG.Proof.Argon2.X86_64.FillIteration + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Ready (p : Params) (pass : Nat) (s : State) : Prop where + parameters : FillContext.Parameters p pass 0 0 + header : ∃ lane slice, FillHeader.Ready p pass lane slice s + +structure Prepared (s t : State) (p : Params) (pass : Nat) : Prop where + ready : FillSlice.Ready p pass 0 t + keeps : Divide.Keeps [.r14] s t + +theorem setup_ok (s : State) (p : Params) (pass : Nat) (h : Ready p pass s) : + WP isa (.block Impl.Argon2.X86_64.FillIteration.setup) s (Prepared s · p pass) := by + refine (SegmentSetup.register_ok s .r14 0).mono ?_ + rintro t ⟨sliceWord, keeps⟩ + obtain ⟨lane, slice, header⟩ := h.header + obtain ⟨old, words⟩ := header.words + have next : FillHeader.Ready p pass lane 0 t := header.of_state (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl <;> exact keeps.regs _ (by decide)) + keeps.mem keeps.rd keeps.wr ((keeps.regs .rbx (by decide)).trans words.laneWord) sliceWord + exact ⟨⟨h.parameters, lane, next⟩, keeps⟩ + +end VG.Proof.Argon2.X86_64.FillIteration diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterations.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterations.lean new file mode 100644 index 000000000..a9b81309b --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterations.lean @@ -0,0 +1,73 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillIterationsBody +import VerifiedGarbage.Proof.Argon2.Iterations + +/-! Termination and correctness of every requested filling pass. -/ + +namespace VG.Proof.Argon2.X86_64.FillIterations + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Finished (s t : State) (p : Params) (state : FillState) : Prop where + represented : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks state.memory + matrix : FillKernel.matrix t = FillKernel.matrix s + work : AddressCalls.work t = AddressCalls.work s + header : FillHeader.Ready p p.passes p.lanes 4 t + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (writes s p) s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + regs : ∀ r ∈ calleeSaved, r ≠ .rbx → r ≠ .r14 → r ≠ .r15 → t.gpr r = s.gpr r + +theorem Done.finished {s t : State} {p : Params} {pass : Nat} {state : FillState} + (h : Done s t p pass state) (last : pass + 1 = p.passes) : Finished s t p (fillPass p state pass) := + ⟨h.represented, h.matrix, h.work, last ▸ h.header, h.rd, h.wr, h.frame, h.mxcsr, h.regs⟩ + +theorem Finished.prepend {s a t : State} {p : Params} {pass : Nat} {state finalState : FillState} + (first : Done s a p pass state) (rest : Finished a t p finalState) : Finished s t p finalState := by + refine ⟨rest.represented, rest.matrix.trans first.matrix, rest.work.trans first.work, rest.header, + rest.rd.trans first.rd, rest.wr.trans first.wr, ?_, rest.mxcsr.trans first.mxcsr, ?_⟩ + · have frame := rest.frame + rw [writes, first.matrix, first.work, + first.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide), + first.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)] at frame + exact first.frame.trans frame + · intro r hr bx sl ix; exact (rest.regs r hr bx sl ix).trans (first.regs r hr bx sl ix) + +theorem loop_ok (count : Nat) (s : State) (p : Params) (pass : Nat) (h : Ready p pass s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) + (positive : 0 < count) (endPass : pass + count = p.passes) : + WP isa Impl.Argon2.X86_64.FillIterations.loop s (Finished s · p (Proof.Argon2.iterations p pass count state)) := by + induction count generalizing s pass state with + | zero => omega + | succ n ih => + obtain ⟨trace, a, run, done⟩ := body_ok s p pass h state represented + rw [Proof.Argon2.iterations_succ] + cases n with + | zero => + have last : pass + 1 = p.passes := endPass + refine ⟨_, a, .loopExit run ?_, done.finished last⟩ + simp only [eval, done.cf, last, Nat.lt_irrefl, decide_false] + | succ n => + have active : pass + 1 < p.passes := by omega + obtain ⟨restTrace, t, restRun, finished⟩ := ih a (pass + 1) (done.next active) + (fillPass p state pass) done.represented (by omega) (by omega) + refine ⟨_, t, .loopNext run ?_ restRun, finished.prepend done⟩ + simp only [eval, done.cf, active, decide_true] + +theorem Finished.frame_word {s t : State} {p : Params} {pass : Nat} {state : FillState} + (ready : Ready p pass s) (done : Finished s t p state) + (d : Nat) (bound : d + 8 ≤ 272) (separate : 24 ≤ d) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [done.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)] + obtain ⟨lane, slice, header⟩ := ready.filling.header + have sub : Region.Sub ⟨off (s.gpr .rbp) d, 8⟩ ⟨s.gpr .rbp, 272⟩ := Offset.sub_base _ bound + exact done.frame.readW (r := ⟨off (s.gpr .rbp) d, 8⟩) (Region.contains_self _ _) (by + intro r hr + simp only [writes, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl + · exact header.layout.matrixFrame.symm.sub_left sub + · exact header.addressLayout.frameWork.sub_left sub + · exact header.addressLayout.frameStack.sub_left sub + · simpa only [off, BitVec.add_zero] using Offset.disjoint (d := d) (n := 8) (e := 0) (k := 24) (s.gpr .rbp) (Or.inr (by omega)) (by omega) (by decide)) (by decide) + +end VG.Proof.Argon2.X86_64.FillIterations diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsBody.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsBody.lean new file mode 100644 index 000000000..85450bb9b --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsBody.lean @@ -0,0 +1,65 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillIterationsFrame + +/-! A complete pass retains the matrix and advances its public iteration counter. -/ + +namespace VG.Proof.Argon2.X86_64.FillIterations + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Ready (p : Params) (pass : Nat) (s : State) : Prop where + filling : FillIteration.Ready p pass s + passesBound : p.passes < 2 ^ 32 + passWrite : InRegions s.wr (off (s.gpr .rbp) 0) 8 + +structure Done (s t : State) (p : Params) (pass : Nat) (state : FillState) : Prop where + represented : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks (fillPass p state pass).memory + matrix : FillKernel.matrix t = FillKernel.matrix s + work : AddressCalls.work t = AddressCalls.work s + header : FillHeader.Ready p (pass + 1) p.lanes 4 t + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (writes s p) s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + regs : ∀ r ∈ calleeSaved, r ≠ .rbx → r ≠ .r14 → r ≠ .r15 → t.gpr r = s.gpr r + cf : t.cf = decide (pass + 1 < p.passes) + next : pass + 1 < p.passes → Ready p (pass + 1) t + +theorem body_ok (s : State) (p : Params) (pass : Nat) (h : Ready p pass s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) : + WP isa Impl.Argon2.X86_64.FillIterations.body s (Done s · p pass state) := by + unfold Impl.Argon2.X86_64.FillIterations.body + refine WP.seq ((FillIteration.code_ok s p pass h.filling state represented).mono ?_) + intro a filled + have write : InRegions a.wr (off (a.gpr .rbp) 0) 8 := by + rw [filled.wr, filled.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)] + exact h.passWrite + refine (advance_ok a (filled.header.reads 0 (by simp)) write (filled.header.reads 72 (by simp))).mono ?_ + intro t saved + have header := saved.header filled.header + obtain ⟨old, words⟩ := filled.header.words + have base : FillKernel.matrix t = FillKernel.matrix a := saved.read 232 (by decide) (by decide) + have work : AddressCalls.work t = AddressCalls.work a := saved.read 248 (by decide) (by decide) + have passBound := h.filling.parameters.passBound + refine ⟨saved.represents filled.header _ filled.represented, base.trans filled.matrix, + work.trans filled.work, header, saved.rd.trans filled.rd, saved.wr.trans filled.wr, + ?_, saved.mxcsr.trans filled.mxcsr, ?_, ?_, ?_⟩ + · have lastFrame := saved.outer_frame (p := p) + rw [writes, filled.matrix, filled.work, + filled.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide), + filled.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)] at lastFrame + exact (filling_frame filled.frame).trans lastFrame + · intro r hr bx sl ix + have ne : r ≠ .rax := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + exact (saved.regs r ne).trans (filled.regs r hr bx sl ix) + · have added : (BitVec.ofNat 64 pass + 1 : Addr) = BitVec.ofNat 64 (pass + 1) := by + rw [BitVec.ofNat_add]; rfl + rw [saved.cf, words.passWord, words.passesWord, added, ReferenceMap.word_nat (pass + 1) (by omega), + ReferenceMap.word_nat p.passes (Nat.lt_trans h.passesBound (by decide))] + · intro active + refine ⟨⟨{ h.filling.parameters with passBound := Nat.lt_trans active h.passesBound }, p.lanes, 4, header⟩, + h.passesBound, ?_⟩ + rw [saved.wr, saved.regs .rbp (by decide)]; exact write + +end VG.Proof.Argon2.X86_64.FillIterations diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsBodyCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsBodyCT.lean new file mode 100644 index 000000000..1c9d6454d --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsBodyCT.lean @@ -0,0 +1,62 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillIterationsBody +import VerifiedGarbage.Proof.Argon2.X86_64.FillIterationCT + +/-! Iteration advances its public pass counter and retains the reviewed filling log. -/ + +namespace VG.Proof.Argon2.X86_64.FillIterations + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillIterations + +theorem advance_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) advance (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr; simp only [List.mem_cons, List.not_mem_nil, or_false] at hr; subst r; exact h)) (by taint_decide) + +structure NextRelated (p : Params) (pass : Nat) (leftState rightState : FillState) (s t : State) : Prop where + left : Ready p pass s + right : Ready p pass t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + matrices : FillKernel.matrix s = FillKernel.matrix t + work : AddressCalls.work s = AddressCalls.work t + leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory + +theorem body_rel (p : Params) (pass : Nat) (leftState rightState : FillState) : + RelCT isa (fun s t => NextRelated p pass leftState rightState s t ∧ + (fillPass p leftState pass).indices = (fillPass p rightState pass).indices) body + (fun s t => s.cf = t.cf ∧ (pass + 1 < p.passes → NextRelated p (pass + 1) + (fillPass p leftState pass) + (fillPass p rightState pass) s t)) := by + intro s t ts tt a b hp ea eb + obtain ⟨hp, indices⟩ := hp + have related : FillIteration.Related p pass leftState rightState s t := + ⟨hp.left.filling, hp.right.filling, hp.bases, hp.stacks, hp.matrices, hp.work, hp.leftMatrix, hp.rightMatrix, indices⟩ + cases ea with + | seq segmentA advanceA => + cases eb with + | seq segmentB advanceB => + obtain ⟨segmentTrace, _⟩ := FillIteration.code_rel p pass leftState rightState + _ _ _ _ _ _ related segmentA segmentB + obtain ⟨_, sa, runA, filledA⟩ := FillIteration.code_ok s p pass hp.left.filling leftState hp.leftMatrix + obtain ⟨_, sb, runB, filledB⟩ := FillIteration.code_ok t p pass hp.right.filling rightState hp.rightMatrix + obtain ⟨_, rfl⟩ := Exec.det segmentA runA + obtain ⟨_, rfl⟩ := Exec.det segmentB runB + have bases := (filledA.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).trans + (hp.bases.trans (filledB.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).symm) + obtain ⟨advancedTrace, _⟩ := advance_rel _ _ _ _ _ _ bases advanceA advanceB + obtain ⟨_, a', runA, doneA⟩ := body_ok s p pass hp.left leftState hp.leftMatrix + obtain ⟨_, b', runB, doneB⟩ := body_ok t p pass hp.right rightState hp.rightMatrix + obtain ⟨_, rfl⟩ := Exec.det (.seq segmentA advanceA) runA + obtain ⟨_, rfl⟩ := Exec.det (.seq segmentB advanceB) runB + refine ⟨by rw [segmentTrace, advancedTrace], doneA.cf.trans doneB.cf.symm, ?_⟩ + intro active + refine ⟨doneA.next active, doneB.next active, ?_, ?_, + doneA.matrix.trans (hp.matrices.trans doneB.matrix.symm), + doneA.work.trans (hp.work.trans doneB.work.symm), doneA.represented, doneB.represented⟩ + · exact (doneA.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).trans + (hp.bases.trans (doneB.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).symm) + · exact (doneA.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).trans + (hp.stacks.trans (doneB.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).symm) + +end VG.Proof.Argon2.X86_64.FillIterations diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsCT.lean new file mode 100644 index 000000000..448a7f9ba --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsCT.lean @@ -0,0 +1,51 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillIterations +import VerifiedGarbage.Proof.Argon2.X86_64.FillIterationsBodyCT +import VerifiedGarbage.Proof.Argon2.IterationsIndices + +/-! The pass loop exposes only the complete filling reference log. -/ + +namespace VG.Proof.Argon2.X86_64.FillIterations + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Related (p : Params) (pass count : Nat) (leftState rightState : FillState) + (s t : State) : Prop where + ready : NextRelated p pass leftState rightState s t + leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory + indices : (Proof.Argon2.iterations p pass count leftState).indices = + (Proof.Argon2.iterations p pass count rightState).indices + +theorem loop_rel (p : Params) (pass count : Nat) (leftState rightState : FillState) + (positive : 0 < count) (endPass : pass + count = p.passes) : + RelCT isa (Related p pass count leftState rightState) Impl.Argon2.X86_64.FillIterations.loop (fun _ _ => True) := by + let I := fun n s t => ∃ (pass : Nat) (leftState rightState : FillState), + pass + n = p.passes ∧ 0 < n ∧ Related p pass n leftState rightState s t + have steps : ∀ n, RelCT isa (I n) Impl.Argon2.X86_64.FillIterations.body fun s t => + isa.eval .b s = isa.eval .b t ∧ (isa.eval .b s = some false → True) ∧ + (isa.eval .b s = some true → ∃ m < n, I m s t) := by + intro n s t ts tt a b hp ea eb + obtain ⟨j, ls, rs, endPass, positive, hp⟩ := hp + cases n with + | zero => omega + | succ n => + have passIndices := Proof.Argon2.iterations_first_pass p j n ls rs + hp.ready.left.filling.parameters.segment_bound.1 hp.indices + obtain ⟨trace, flags, next⟩ := body_rel p j ls rs _ _ _ _ _ _ ⟨hp.ready, passIndices⟩ ea eb + obtain ⟨_, a', runA, done⟩ := body_ok s p j hp.ready.left ls hp.leftMatrix + obtain ⟨_, rfl⟩ := Exec.det ea runA + refine ⟨trace, ?_, fun _ => trivial, ?_⟩ + · simp only [eval, flags] + · intro taken + have active : j + 1 < p.passes := by + simp only [eval, done.cf, Option.some.injEq, decide_eq_true_eq] at taken + exact taken + have ready := next active + have indices := hp.indices + rw [Proof.Argon2.iterations_succ, Proof.Argon2.iterations_succ] at indices + exact ⟨n, by omega, j + 1, fillPass p ls j, + fillPass p rs j, by omega, by omega, ready, ready.leftMatrix, ready.rightMatrix, indices⟩ + exact (RelCT.loop I steps count).mono + (fun _ _ h => ⟨pass, leftState, rightState, endPass, positive, h⟩) (fun _ _ h => h) + +end VG.Proof.Argon2.X86_64.FillIterations diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsFrame.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsFrame.lean new file mode 100644 index 000000000..59ec3e8f4 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillIterationsFrame.lean @@ -0,0 +1,47 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillPassSave + +/-! The outer pass loop also writes the public pass word at frame offset zero. -/ + +namespace VG.Proof.Argon2.X86_64.FillIterations + +open VG VG.X86_64 VG.Spec.Argon2 + +def writes (s : State) (p : Params) : List Region := + [⟨FillKernel.matrix s, p.blocks * 1024⟩, ⟨AddressCalls.work s, 8192⟩, + below (s.gpr .rsp) 8, ⟨s.gpr .rbp, 24⟩] + +theorem filling_frame {s t : State} {p : Params} (h : Frame (FillBlock.writes s p) s.mem t.mem) : + Frame (writes s p) s.mem t.mem := by + apply h.sub + intro r hr + simp only [FillBlock.writes, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl + · exact ⟨_, by simp [writes], fun _ h => h⟩ + · exact ⟨_, by simp [writes], fun _ h => h⟩ + · exact ⟨_, by simp [writes], fun _ h => h⟩ + · exact ⟨⟨s.gpr .rbp, 24⟩, by simp [writes], Offset.sub_base _ (by decide)⟩ + +theorem Saved.outer_frame {s t : State} {p : Params} (h : Saved s t) : Frame (writes s p) s.mem t.mem := by + apply h.frame.sub + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact ⟨⟨s.gpr .rbp, 24⟩, by simp [writes], Offset.sub_base _ (by decide)⟩ + +theorem Saved.represents {s t : State} {p : Params} {pass lane slice : Nat} + (h : Saved s t) (header : FillHeader.Ready p pass lane slice s) (blocks : Array Block) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks blocks) : + Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks blocks := by + have base : FillKernel.matrix t = FillKernel.matrix s := h.read 232 (by decide) (by decide) + rw [base] + refine ⟨represented.size, ?_⟩ + intro k hk + apply Eq.trans _ (represented.block k hk) + apply FillCompress.block_frame h.frame + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact (header.layout.matrixFrame.sub_left (Proof.Argon2.matrixCell_sub _ _ _ hk)).sub_right + (Offset.sub_base _ (by decide)) + +end VG.Proof.Argon2.X86_64.FillIterations diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernel.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernel.lean new file mode 100644 index 000000000..8f271cd07 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernel.lean @@ -0,0 +1,67 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelPrepare + +/-! Complete active-cell update from a random word and the matrix allocation. -/ + +namespace VG.Proof.Argon2.X86_64.FillKernel + +open VG VG.X86_64 VG.Spec.Argon2 + +def writes (s : State) (p : Params) (lane slice index : Nat) : List Region := + [⟨current s p lane slice index, 1024⟩, ⟨work s, 5120⟩, + below (s.gpr .rsp) 8, ⟨off (s.gpr .rbp) 16, 8⟩] + +structure Done (s t : State) (p : Params) (pass lane slice index : Nat) : Prop where + block : blockAt t.mem (current s p lane slice index) = + let next := Spec.Argon2.compress (blockAt s.mem (previous s p lane slice index)) + (blockAt s.mem (referenced s p pass lane slice index)) + if pass = 0 then next else xorBlock next (blockAt s.mem (current s p lane slice index)) + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (writes s p lane slice index) s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + +theorem code_ok (s : State) (p : Params) (pass lane slice index : Nat) + (h : Ready p pass lane slice index s) : + WP isa Impl.Argon2.X86_64.FillKernel.code s (Done s · p pass lane slice index) := by + unfold Impl.Argon2.X86_64.FillKernel.code + refine WP.seq ((prepare_ok s p pass lane slice index h).mono ?_) + intro b prepared + have keeps := prepared.keeps + have cur := prepared.currentPtr + have prev := prepared.previousPtr + have other := prepared.referencePtr + refine (FillCompress.code_mx_ok b prepared.ready).mono ?_ + rintro t ⟨done, mx⟩ + have counter : FillCompress.pass b = BitVec.ofNat 64 pass := by + unfold FillCompress.pass + rw [keeps.mem, keeps.regs .rbp (by decide)] + exact h.passWord + refine ⟨?_, ?_, done.rd.trans keeps.rd, done.wr.trans keeps.wr, ?_, mx.trans keeps.mxcsr⟩ + · have block := done.block + rw [cur, prev, other, keeps.mem, counter] at block + simp only [ReferenceMap.word_zero pass (Nat.lt_trans h.bounds.passBound (by decide))] at block + exact block + · intro r hr + have ne : r ∉ ReferenceMap.changed := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + exact (done.regs r hr).trans (keeps.regs r ne) + · have workB : FillCompress.work b = work s := by + unfold FillCompress.work work + rw [keeps.regs .rbp (by decide), keeps.mem] + have frame := done.frame + rw [FillCompress.writes, workB, cur, keeps.regs .rsp (by decide), keeps.regs .rbp (by decide), keeps.mem] at frame + change Frame [⟨current s p lane slice index, 1024⟩, ⟨work s + 4096, 1024⟩, + ⟨work s, 4096⟩, below (s.gpr .rsp) 8, ⟨off (s.gpr .rbp) 16, 8⟩] s.mem t.mem at frame + apply frame.sub + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl + · exact ⟨_, by simp [writes], fun _ h => h⟩ + · exact ⟨⟨work s, 5120⟩, by simp [writes], Offset.sub_base _ (by decide)⟩ + · exact ⟨⟨work s, 5120⟩, by simp [writes], Region.sub_prefix (by decide)⟩ + · exact ⟨_, by simp [writes], fun _ h => h⟩ + · exact ⟨_, by simp [writes], fun _ h => h⟩ + +end VG.Proof.Argon2.X86_64.FillKernel diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelArgs.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelArgs.lean new file mode 100644 index 000000000..b2afb6895 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelArgs.lean @@ -0,0 +1,80 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelLayout +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMap + +/-! Reload frame arguments and compose reference mapping with matrix addresses. -/ + +namespace VG.Proof.Argon2.X86_64.FillKernel + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem load_ok (s : State) (r : Reg) (d : Nat) + (read : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8) : + WP isa (.block [.mov r (.mem (Impl.Argon2.X86_64.at_ .rbp d))]) s fun t => + t.gpr r = s.mem.readW (off (s.gpr .rbp) d) 64 ∧ Divide.Keeps [r] s t := by + apply WP.of_runBlock + simp only [runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, State.load64, + ea_at, read, Option.map_some, Option.some.injEq, exists_eq_left', RegUpd.gpr_setReg, ite_true] + refine ⟨trivial, ?_⟩ + constructor + · intro q hq + simp only [List.mem_cons, List.not_mem_nil, or_false] at hq + exact ite_eq_right hq + all_goals rfl + +structure Ready (p : Params) (pass lane slice index : Nat) (s : State) : Prop where + layout : Layout p s + bounds : ReferenceMap.Bounds p pass lane slice index + position : ReferenceMap.Position p lane slice index s + passWord : s.mem.readW (off (s.gpr .rbp) 0) 64 = BitVec.ofNat 64 pass + lanesWord : s.mem.readW (off (s.gpr .rbp) 184) 64 = BitVec.ofNat 64 p.lanes + +structure Mapped (s t : State) (p : Params) (pass lane slice index : Nat) : Prop where + selected : t.gpr .r9 = BitVec.ofNat 64 (Spec.Argon2.reference p pass lane slice index (s.gpr .rdi)).1 + column : t.gpr .rdi = BitVec.ofNat 64 (Spec.Argon2.reference p pass lane slice index (s.gpr .rdi)).2 + original : t.gpr .r11 = s.gpr .rdi + keeps : Divide.Keeps ReferenceMap.changed s t + +theorem mapping_ok (s : State) (p : Params) (pass lane slice index : Nat) + (h : Ready p pass lane slice index s) : + WP isa Impl.Argon2.X86_64.FillKernel.mapping s (Mapped s · p pass lane slice index) := by + unfold Impl.Argon2.X86_64.FillKernel.mapping + refine WP.seq ((load_ok s .rsi 184 (h.layout.frameRead 184 (by simp))).mono ?_) + rintro a ⟨lanes, keeps⟩ + have k : Divide.Keeps ReferenceMap.changed s a := keeps.mono (by decide) + have ready : ReferenceMap.Ready p pass lane slice index a := by + refine ⟨h.bounds, h.position.of_keeps k, lanes.trans h.lanesWord, ?_, ?_⟩ + · rw [k.rd, k.wr, k.regs .rbp (by decide)] + simpa only [off, show BitVec.ofNat 64 0 = 0#64 from rfl, BitVec.add_zero] + using h.layout.frameRead 0 (by simp) + · rw [k.mem, k.regs .rbp (by decide)] + simpa only [off, show BitVec.ofNat 64 0 = 0#64 from rfl, BitVec.add_zero] using h.passWord + refine (ReferenceMap.code_spec_ok a p pass lane slice index ready).mono ?_ + rintro t ⟨lane, column, original, tail⟩ + rw [keeps.regs .rdi (by decide)] at lane column original + exact ⟨lane, column, original, k.trans tail⟩ + +structure Pointers (s t : State) (p : Params) (lane slice index refLane refColumn : Nat) : Prop where + current : t.gpr .r10 = FillPointers.cell (matrix s) p lane (slice * p.segmentLen + index) + previous : t.gpr .rdi = FillPointers.cell (matrix s) p lane + ((slice * p.segmentLen + index + p.laneLen - 1) % p.laneLen) + reference : t.gpr .rsi = FillPointers.cell (matrix s) p refLane refColumn + keeps : Divide.Keeps ReferenceMap.changed s t + +theorem pointers_ok (s : State) (p : Params) (pass lane slice index refLane refColumn : Nat) + (layout : Layout p s) (bounds : ReferenceMap.Bounds p pass lane slice index) + (position : ReferenceMap.Position p lane slice index s) + (laneWord : s.gpr .r9 = BitVec.ofNat 64 refLane) (columnWord : s.gpr .rdi = BitVec.ofNat 64 refColumn) : + WP isa Impl.Argon2.X86_64.FillKernel.pointers s (Pointers s · p lane slice index refLane refColumn) := by + unfold Impl.Argon2.X86_64.FillKernel.pointers + refine WP.seq ((load_ok s .r8 232 (layout.frameRead 232 (by simp))).mono ?_) + rintro a ⟨base, keeps⟩ + have k : Divide.Keeps ReferenceMap.changed s a := keeps.mono (by decide) + have lane' : a.gpr .r9 = BitVec.ofNat 64 refLane := (keeps.regs .r9 (by decide)).trans laneWord + have col' : a.gpr .rdi = BitVec.ofNat 64 refColumn := (keeps.regs .rdi (by decide)).trans columnWord + refine (FillPointers.code_nat_ok a p pass lane slice index refLane refColumn bounds + (position.of_keeps k) lane' col').mono ?_ + rintro t ⟨current, previous, reference, tail⟩ + rw [base] at current previous reference + exact ⟨current, previous, reference, k.trans (tail.mono (by decide))⟩ + +end VG.Proof.Argon2.X86_64.FillKernel diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelCT.lean new file mode 100644 index 000000000..e5b41c53d --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelCT.lean @@ -0,0 +1,57 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelMappingCT +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressCT + +/-! Equal permitted references give equal compression and block-update traces. -/ + +namespace VG.Proof.Argon2.X86_64.FillKernel + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem prepared_public {p : Params} {pass lane slice index : Nat} {s t a b : State} + (h : Related p pass lane slice index s t) + (ha : Prepared s a p pass lane slice index) (hb : Prepared t b p pass lane slice index) : + FillCompress.CodeRelated a b := by + have currentEq : current s p lane slice index = current t p lane slice index := by + unfold current; rw [h.matrices] + have previousEq : previous s p lane slice index = previous t p lane slice index := by + unfold previous; rw [h.matrices] + have referenceEq : referenced s p pass lane slice index = referenced t p pass lane slice index := by + unfold referenced; rw [h.references, h.matrices] + have workA : FillCompress.work a = work s := by + unfold FillCompress.work work; rw [ha.keeps.regs .rbp (by decide), ha.keeps.mem] + have workB : FillCompress.work b = work t := by + unfold FillCompress.work work; rw [hb.keeps.regs .rbp (by decide), hb.keeps.mem] + have passA : FillCompress.pass a = BitVec.ofNat 64 pass := by + unfold FillCompress.pass + rw [ha.keeps.regs .rbp (by decide), ha.keeps.mem] + exact h.left.passWord + have passB : FillCompress.pass b = BitVec.ofNat 64 pass := by + unfold FillCompress.pass + rw [hb.keeps.regs .rbp (by decide), hb.keeps.mem] + exact h.right.passWord + refine ⟨ha.ready, hb.ready, ?_, workA.trans (h.scratch.trans workB.symm), passA.trans passB.symm⟩ + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl + · exact ha.previousPtr.trans (previousEq.trans hb.previousPtr.symm) + · exact ha.referencePtr.trans (referenceEq.trans hb.referencePtr.symm) + · exact ha.currentPtr.trans (currentEq.trans hb.currentPtr.symm) + · exact (ha.keeps.regs .rsp (by decide)).trans (h.stacks.trans (hb.keeps.regs .rsp (by decide)).symm) + · exact (ha.keeps.regs .rbp (by decide)).trans (h.bases.trans (hb.keeps.regs .rbp (by decide)).symm) + +theorem prepare_public_rel (p : Params) (pass lane slice index : Nat) : + RelCT isa (Related p pass lane slice index) Impl.Argon2.X86_64.FillKernel.prepare + FillCompress.CodeRelated := by + have trace := (mapping_public_rel p pass lane slice index).seq (pointers_trace p lane slice index) + have full := trace.wpDep (fun s t h => + ⟨prepare_ok s p pass lane slice index h.left, prepare_ok t p pass lane slice index h.right⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + exact prepared_public hp ha hb + +theorem code_rel (p : Params) (pass lane slice index : Nat) : + RelCT isa (Related p pass lane slice index) Impl.Argon2.X86_64.FillKernel.code + (fun _ _ => True) := (prepare_public_rel p pass lane slice index).seq FillCompress.code_rel + +end VG.Proof.Argon2.X86_64.FillKernel diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelInvariant.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelInvariant.lean new file mode 100644 index 000000000..fe0abd23b --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelInvariant.lean @@ -0,0 +1,53 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernel + +/-! Each active-cell update retains the frame and matrix allocation invariant. -/ + +namespace VG.Proof.Argon2.X86_64.FillKernel + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem Done.frame_word {s t : State} {p : Params} {pass lane slice index : Nat} + (h : Ready p pass lane slice index s) (done : Done s t p pass lane slice index) + (d : Nat) (bound : d + 8 ≤ 272) (separate : d + 8 ≤ 16 ∨ 24 ≤ d) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [done.regs .rbp (by simp [calleeSaved])] + have sub : Region.Sub ⟨off (s.gpr .rbp) d, 8⟩ ⟨s.gpr .rbp, 272⟩ := Offset.sub_base _ bound + have currentSub : Region.Sub ⟨current s p lane slice index, 1024⟩ ⟨matrix s, p.blocks * 1024⟩ := + cell_sub p _ h.bounds.lanesPositive h.bounds.laneBound + (Proof.Argon2.column_lt p h.bounds.lanesPositive h.bounds.sliceBound h.bounds.indexBound) + exact done.frame.readW (r := ⟨off (s.gpr .rbp) d, 8⟩) (Region.contains_self _ _) (by + intro r hr + simp only [writes, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl + · exact ((h.layout.matrixFrame.sub_left currentSub).symm).sub_left sub + · exact h.layout.frameWork.sub_left sub + · exact h.layout.frameStack.sub_left sub + · exact Offset.disjoint _ separate (by omega) (by decide)) (by decide) + +theorem Done.retains {s t : State} {p : Params} {pass lane slice index : Nat} + (h : Ready p pass lane slice index s) (done : Done s t p pass lane slice index) : + Ready p pass lane slice index t := by + have bp := done.regs .rbp (by simp [calleeSaved]) + have sp := done.regs .rsp (by simp [calleeSaved]) + have matrix' : matrix t = matrix s := done.frame_word h 232 (by decide) (by decide) + have work' : work t = work s := done.frame_word h 248 (by decide) (by decide) + refine ⟨?_, h.bounds, ?_, (done.frame_word h 0 (by decide) (by decide)).trans h.passWord, + (done.frame_word h 184 (by decide) (by decide)).trans h.lanesWord⟩ + · constructor + · rw [done.rd, done.wr, bp]; exact h.layout.frameRead + · rw [done.wr, bp]; exact h.layout.frameWrite + · rw [matrix', done.wr]; exact h.layout.matrixWrite + · rw [work', done.wr]; exact h.layout.workWrite + · rw [matrix', work']; exact h.layout.matrixWork + · rw [matrix', bp]; exact h.layout.matrixFrame + · rw [matrix', sp]; exact h.layout.matrixStack + · rw [bp, work']; exact h.layout.frameWork + · rw [bp, sp]; exact h.layout.frameStack + · rw [sp, work']; exact h.layout.stackWork + · exact ⟨(done.regs .rbx (by simp [calleeSaved])).trans h.position.current, + (done.regs .r12 (by simp [calleeSaved])).trans h.position.laneLength, + (done.regs .r13 (by simp [calleeSaved])).trans h.position.segmentLength, + (done.regs .r14 (by simp [calleeSaved])).trans h.position.slice, + (done.regs .r15 (by simp [calleeSaved])).trans h.position.index⟩ + +end VG.Proof.Argon2.X86_64.FillKernel diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelLayout.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelLayout.lean new file mode 100644 index 000000000..f822a6935 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelLayout.lean @@ -0,0 +1,101 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillPointersNat +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompress +import VerifiedGarbage.Impl.Argon2.X86_64.FillKernel + +/-! One allocation invariant covers all matrix cells used by the filling step. -/ + +namespace VG.Proof.Argon2.X86_64.FillKernel + +open VG VG.X86_64 VG.Spec.Argon2 + +def matrix (s : State) : Addr := s.mem.readW (off (s.gpr .rbp) 232) 64 + +def work (s : State) : Addr := s.mem.readW (off (s.gpr .rbp) 248) 64 + +structure Layout (p : Params) (s : State) : Prop where + frameRead : ∀ d ∈ [0, 16, 184, 232, 248], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8 + frameWrite : InRegions s.wr (off (s.gpr .rbp) 16) 8 + matrixWrite : Covers [⟨matrix s, p.blocks * 1024⟩] s.wr + workWrite : Covers [⟨work s, 5120⟩] s.wr + matrixWork : (⟨matrix s, p.blocks * 1024⟩ : Region).Disjoint ⟨work s, 5120⟩ + matrixFrame : (⟨matrix s, p.blocks * 1024⟩ : Region).Disjoint ⟨s.gpr .rbp, 272⟩ + matrixStack : (⟨matrix s, p.blocks * 1024⟩ : Region).Disjoint (below (s.gpr .rsp) 8) + frameWork : (⟨s.gpr .rbp, 272⟩ : Region).Disjoint ⟨work s, 5120⟩ + frameStack : (⟨s.gpr .rbp, 272⟩ : Region).Disjoint (below (s.gpr .rsp) 8) + stackWork : (below (s.gpr .rsp) 8).Disjoint ⟨work s, 5120⟩ + +theorem Layout.of_keeps {p : Params} {s t : State} (h : Layout p s) + (k : Divide.Keeps ReferenceMap.changed s t) : Layout p t := by + have bp := k.regs .rbp (by decide) + have sp := k.regs .rsp (by decide) + have matrix' : matrix t = matrix s := by unfold matrix; rw [bp, k.mem] + have work' : work t = work s := by unfold work; rw [bp, k.mem] + constructor + · rw [k.rd, k.wr, bp]; exact h.frameRead + · rw [k.wr, bp]; exact h.frameWrite + · rw [matrix', k.wr]; exact h.matrixWrite + · rw [work', k.wr]; exact h.workWrite + · rw [matrix', work']; exact h.matrixWork + · rw [matrix', bp]; exact h.matrixFrame + · rw [matrix', sp]; exact h.matrixStack + · rw [bp, work']; exact h.frameWork + · rw [bp, sp]; exact h.frameStack + · rw [sp, work']; exact h.stackWork + +theorem cell_sub (p : Params) (base : Addr) (positive : 0 < p.lanes) {lane column : Nat} + (hl : lane < p.lanes) (hc : column < p.laneLen) : + Region.Sub ⟨FillPointers.cell base p lane column, 1024⟩ ⟨base, p.blocks * 1024⟩ := + Offset.sub_base base (Proof.Argon2.cell_bytes p positive hl hc) + +theorem Layout.cell_cover {p : Params} {s : State} (h : Layout p s) (positive : 0 < p.lanes) + {lane column : Nat} (hl : lane < p.lanes) (hc : column < p.laneLen) : + Covers [⟨FillPointers.cell (matrix s) p lane column, 1024⟩] s.wr := by + have sub : Covers [⟨FillPointers.cell (matrix s) p lane column, 1024⟩] + [⟨matrix s, p.blocks * 1024⟩] := Covers.of_sub (by + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact ⟨⟨matrix s, p.blocks * 1024⟩, by simp, (lane * p.laneLen + column) * 1024, + rfl, Proof.Argon2.cell_bytes p positive hl hc⟩) + exact fun a n ha => h.matrixWrite a n (sub a n ha) + +theorem compress_ready (p : Params) (s : State) (layout : Layout p s) + (positive : 0 < p.lanes) (leftLane leftColumn rightLane rightColumn destLane destColumn : Nat) + (ll : leftLane < p.lanes) (lc : leftColumn < p.laneLen) + (rl : rightLane < p.lanes) (rc : rightColumn < p.laneLen) + (dl : destLane < p.lanes) (dc : destColumn < p.laneLen) + (left : s.gpr .rdi = FillPointers.cell (matrix s) p leftLane leftColumn) + (right : s.gpr .rsi = FillPointers.cell (matrix s) p rightLane rightColumn) + (dest : s.gpr .r10 = FillPointers.cell (matrix s) p destLane destColumn) : FillCompress.Ready s := by + have leftSub := cell_sub p (matrix s) positive ll lc + have rightSub := cell_sub p (matrix s) positive rl rc + have destSub := cell_sub p (matrix s) positive dl dc + have read (lane column : Nat) (hl : lane < p.lanes) (hc : column < p.laneLen) : + Covers [⟨FillPointers.cell (matrix s) p lane column, 1024⟩] (s.rd ++ s.wr) := by + intro a n ha + obtain ⟨r, hr, hc⟩ := layout.cell_cover positive hl hc a n ha + exact ⟨r, List.mem_append_right _ hr, hc⟩ + constructor + · intro d hd + exact layout.frameRead d (by + simp only [List.mem_cons, List.not_mem_nil, or_false] at hd + rcases hd with rfl | rfl | rfl <;> simp) + · exact layout.frameWrite + · rw [left]; exact read leftLane leftColumn ll lc + · rw [right]; exact read rightLane rightColumn rl rc + · rw [dest]; exact layout.cell_cover positive dl dc + · exact layout.workWrite + · rw [left]; exact layout.matrixWork.sub_left leftSub + · rw [right]; exact layout.matrixWork.sub_left rightSub + · rw [dest]; exact layout.matrixWork.sub_left destSub + · exact layout.frameWork + · rw [left]; exact layout.matrixFrame.sub_left leftSub + · rw [right]; exact layout.matrixFrame.sub_left rightSub + · rw [dest]; exact layout.matrixFrame.sub_left destSub + · rw [left]; exact (layout.matrixStack.sub_left leftSub).symm + · rw [right]; exact (layout.matrixStack.sub_left rightSub).symm + · exact layout.stackWork + · rw [dest]; exact layout.matrixStack.sub_left destSub + · exact layout.frameStack + +end VG.Proof.Argon2.X86_64.FillKernel diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelMappingCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelMappingCT.lean new file mode 100644 index 000000000..2d48c8db4 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelMappingCT.lean @@ -0,0 +1,114 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelPrepare +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapCT +import VerifiedGarbage.Proof.Argon2.X86_64.FillPointersCT + +/-! Reference mapping exposes no more than the permitted reference coordinates. -/ + +namespace VG.Proof.Argon2.X86_64.FillKernel + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Related (p : Params) (pass lane slice index : Nat) (s t : State) : Prop where + left : Ready p pass lane slice index s + right : Ready p pass lane slice index t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + matrices : matrix s = matrix t + scratch : work s = work t + references : Spec.Argon2.reference p pass lane slice index (s.gpr .rdi) = + Spec.Argon2.reference p pass lane slice index (t.gpr .rdi) + +structure PointerRelated (p : Params) (lane slice index : Nat) (s t : State) : Prop where + left : Layout p s + right : Layout p t + leftPosition : ReferenceMap.Position p lane slice index s + rightPosition : ReferenceMap.Position p lane slice index t + bases : s.gpr .rbp = t.gpr .rbp + matrices : matrix s = matrix t + +theorem lanes_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block Impl.Argon2.X86_64.FillKernel.lanes) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +theorem lanes_ready (s : State) (p : Params) (pass lane slice index : Nat) + (h : Ready p pass lane slice index s) : + WP isa (.block Impl.Argon2.X86_64.FillKernel.lanes) s fun t => + ReferenceMap.Ready p pass lane slice index t ∧ Divide.Keeps ReferenceMap.changed s t := by + refine (load_ok s .rsi 184 (h.layout.frameRead 184 (by simp))).mono ?_ + rintro t ⟨lanes, keeps⟩ + have k : Divide.Keeps ReferenceMap.changed s t := keeps.mono (by decide) + refine ⟨⟨h.bounds, h.position.of_keeps k, lanes.trans h.lanesWord, ?_, ?_⟩, k⟩ + · rw [k.rd, k.wr, k.regs .rbp (by decide)] + simpa only [off, show BitVec.ofNat 64 0 = 0#64 from rfl, BitVec.add_zero] + using h.layout.frameRead 0 (by simp) + · rw [k.mem, k.regs .rbp (by decide)] + simpa only [off, show BitVec.ofNat 64 0 = 0#64 from rfl, BitVec.add_zero] using h.passWord + +theorem lanes_public_rel (p : Params) (pass lane slice index : Nat) : + RelCT isa (Related p pass lane slice index) (.block Impl.Argon2.X86_64.FillKernel.lanes) + (ReferenceMap.Related p pass lane slice index) := by + have trace := lanes_rel.mono (P' := Related p pass lane slice index) + (fun _ _ h => h.bases) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => + ⟨lanes_ready s p pass lane slice index h.left, lanes_ready t p pass lane slice index h.right⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + exact ⟨ha.1, hb.1, (ha.2.regs .rbp (by decide)).trans + (hp.bases.trans (hb.2.regs .rbp (by decide)).symm)⟩ + +theorem mapping_public_rel (p : Params) (pass lane slice index : Nat) : + RelCT isa (Related p pass lane slice index) Impl.Argon2.X86_64.FillKernel.mapping + (PointerRelated p lane slice index) := by + have trace := (lanes_public_rel p pass lane slice index).seq (ReferenceMap.code_rel p pass lane slice index) + have full := trace.wpDep (fun s t h => + ⟨mapping_ok s p pass lane slice index h.left, mapping_ok t p pass lane slice index h.right⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + refine ⟨hp.left.layout.of_keeps ha.keeps, hp.right.layout.of_keeps hb.keeps, + hp.left.position.of_keeps ha.keeps, hp.right.position.of_keeps hb.keeps, ?_, ?_⟩ + · exact (ha.keeps.regs .rbp (by decide)).trans (hp.bases.trans (hb.keeps.regs .rbp (by decide)).symm) + · unfold matrix + rw [ha.keeps.mem, hb.keeps.mem, ha.keeps.regs .rbp (by decide), hb.keeps.regs .rbp (by decide)] + exact hp.matrices + +theorem matrix_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block Impl.Argon2.X86_64.FillKernel.matrix) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +theorem pointers_trace (p : Params) (lane slice index : Nat) : + RelCT isa (PointerRelated p lane slice index) Impl.Argon2.X86_64.FillKernel.pointers + (fun _ _ => True) := by + have trace := matrix_rel.mono (P' := PointerRelated p lane slice index) + (fun _ _ h => h.bases) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => + ⟨load_ok s .r8 232 (h.left.frameRead 232 (by simp)), + load_ok t .r8 232 (h.right.frameRead 232 (by simp))⟩) + have args : RelCT isa (PointerRelated p lane slice index) (.block Impl.Argon2.X86_64.FillKernel.matrix) + (fun s t => ∀ r ∈ [Reg.r8, .rbx, .r12, .r13, .r14, .r15], s.gpr r = t.gpr r) := + full.mono (fun _ _ h => h) (by + intro a b h r hr + obtain ⟨_, s, t, hp, ⟨va, ka⟩, ⟨vb, kb⟩⟩ := h + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl + · exact va.trans (hp.matrices.trans vb.symm) + all_goals rw [ka.regs _ (by decide), kb.regs _ (by decide)] + · exact hp.leftPosition.current.trans hp.rightPosition.current.symm + · exact hp.leftPosition.laneLength.trans hp.rightPosition.laneLength.symm + · exact hp.leftPosition.segmentLength.trans hp.rightPosition.segmentLength.symm + · exact hp.leftPosition.slice.trans hp.rightPosition.slice.symm + · exact hp.leftPosition.index.trans hp.rightPosition.index.symm) + exact (args.seq FillPointers.code_rel).mono (fun _ _ h => h) (fun _ _ _ => trivial) + +end VG.Proof.Argon2.X86_64.FillKernel diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelMatrix.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelMatrix.lean new file mode 100644 index 000000000..002908e3f --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelMatrix.lean @@ -0,0 +1,60 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernel +import VerifiedGarbage.Proof.Argon2.Matrix + +/-! The filling step updates exactly one cell of the specification's block array. -/ + +namespace VG.Proof.Argon2.X86_64.FillKernel + +open VG VG.X86_64 VG.Spec.Argon2 + +def currentIndex (p : Params) (lane slice index : Nat) : Nat := + lane * p.laneLen + currentColumn p slice index + +def previousIndex (p : Params) (lane slice index : Nat) : Nat := + lane * p.laneLen + previousColumn p slice index + +def referenceIndex (s : State) (p : Params) (pass lane slice index : Nat) : Nat := + let ref := Spec.Argon2.reference p pass lane slice index (s.gpr .rdi) + ref.1 * p.laneLen + ref.2 + +def nextBlock (s : State) (p : Params) (pass lane slice index : Nat) (blocks : Array Block) : Block := + let next := Spec.Argon2.compress (blocks[previousIndex p lane slice index]?.getD zeroBlock) + (blocks[referenceIndex s p pass lane slice index]?.getD zeroBlock) + if pass = 0 then next else xorBlock next (blocks[currentIndex p lane slice index]?.getD zeroBlock) + +theorem Done.represents {s t : State} {p : Params} {pass lane slice index : Nat} + (ready : Ready p pass lane slice index s) (done : Done s t p pass lane slice index) + (blocks : Array Block) (represented : Proof.Argon2.Represents s.mem (matrix s) p.blocks blocks) : + Proof.Argon2.Represents t.mem (matrix s) p.blocks + (blocks.set! (currentIndex p lane slice index) (nextBlock s p pass lane slice index blocks)) := by + have currentBound := Proof.Argon2.current_cell_lt p ready.bounds.lanesPositive + ready.bounds.laneBound ready.bounds.sliceBound ready.bounds.indexBound + have previousBound := Proof.Argon2.previous_cell_lt p ready.bounds.lanesPositive + ready.bounds.memoryMinimum ready.bounds.laneBound (column := currentColumn p slice index) + have referenceBound := Proof.Argon2.reference_cell_lt p ready.bounds.lanesPositive + ready.bounds.memoryMinimum pass lane slice index (s.gpr .rdi) ready.bounds.laneBound + apply represented.update (currentIndex p lane slice index) currentBound (nextBlock s p pass lane slice index blocks) + · have block := done.block + change blockAt t.mem (Proof.Argon2.matrixCell (matrix s) (currentIndex p lane slice index)) = _ at block + have prev := represented.block (previousIndex p lane slice index) previousBound + have other := represented.block (referenceIndex s p pass lane slice index) referenceBound + have old := represented.block (currentIndex p lane slice index) currentBound + change blockAt s.mem (previous s p lane slice index) = _ at prev + change blockAt s.mem (referenced s p pass lane slice index) = _ at other + change blockAt s.mem (current s p lane slice index) = _ at old + rw [prev, other, old] at block + exact block + · intro j hj different + apply FillCompress.block_frame done.frame + intro r hr + simp only [writes, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl + · have blocksBound := Nat.lt_of_le_of_lt (Proof.Argon2.blocks_le_memory p) ready.bounds.memoryBound + exact Proof.Argon2.matrixCell_disjoint _ p.blocks j (currentIndex p lane slice index) + (Nat.lt_trans (Nat.mul_lt_mul_of_pos_right blocksBound (by decide)) (by decide)) hj currentBound different + · exact ready.layout.matrixWork.sub_left (Proof.Argon2.matrixCell_sub _ _ _ hj) + · exact ready.layout.matrixStack.sub_left (Proof.Argon2.matrixCell_sub _ _ _ hj) + · exact (ready.layout.matrixFrame.sub_left (Proof.Argon2.matrixCell_sub _ _ _ hj)).sub_right + (Offset.sub_base _ (by decide)) + +end VG.Proof.Argon2.X86_64.FillKernel diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelPrepare.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelPrepare.lean new file mode 100644 index 000000000..2285bbed3 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelPrepare.lean @@ -0,0 +1,67 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelArgs + +/-! Complete active-cell update from a random word and the matrix allocation. -/ + +namespace VG.Proof.Argon2.X86_64.FillKernel + +open VG VG.X86_64 VG.Spec.Argon2 + +def currentColumn (p : Params) (slice index : Nat) : Nat := slice * p.segmentLen + index + +def previousColumn (p : Params) (slice index : Nat) : Nat := + (currentColumn p slice index + p.laneLen - 1) % p.laneLen + +def current (s : State) (p : Params) (lane slice index : Nat) : Addr := + FillPointers.cell (matrix s) p lane (currentColumn p slice index) + +def previous (s : State) (p : Params) (lane slice index : Nat) : Addr := + FillPointers.cell (matrix s) p lane (previousColumn p slice index) + +def referenced (s : State) (p : Params) (pass lane slice index : Nat) : Addr := + let ref := Spec.Argon2.reference p pass lane slice index (s.gpr .rdi) + FillPointers.cell (matrix s) p ref.1 ref.2 + +structure Prepared (s t : State) (p : Params) (pass lane slice index : Nat) : Prop where + ready : FillCompress.Ready t + currentPtr : t.gpr .r10 = current s p lane slice index + previousPtr : t.gpr .rdi = previous s p lane slice index + referencePtr : t.gpr .rsi = referenced s p pass lane slice index + keeps : Divide.Keeps ReferenceMap.changed s t + +theorem prepare_ok (s : State) (p : Params) (pass lane slice index : Nat) + (h : Ready p pass lane slice index s) : + WP isa Impl.Argon2.X86_64.FillKernel.prepare s (Prepared s · p pass lane slice index) := by + unfold Impl.Argon2.X86_64.FillKernel.prepare + refine WP.seq ((mapping_ok s p pass lane slice index h).mono ?_) + intro a mapped + let ref := Spec.Argon2.reference p pass lane slice index (s.gpr .rdi) + refine ((pointers_ok a p pass lane slice index ref.1 ref.2 + (h.layout.of_keeps mapped.keeps) h.bounds (h.position.of_keeps mapped.keeps) + mapped.selected mapped.column).mono ?_) + intro b pointers + have keeps := mapped.keeps.trans pointers.keeps + have matrixA : matrix a = matrix s := by + unfold matrix; rw [mapped.keeps.mem, mapped.keeps.regs .rbp (by decide)] + have matrixB : matrix b = matrix a := by + unfold matrix; rw [pointers.keeps.mem, pointers.keeps.regs .rbp (by decide)] + have cur : b.gpr .r10 = current s p lane slice index := by + rw [pointers.current, matrixA]; rfl + have prev : b.gpr .rdi = previous s p lane slice index := by + rw [pointers.previous, matrixA]; rfl + have other : b.gpr .rsi = referenced s p pass lane slice index := by + rw [pointers.reference, matrixA]; rfl + have columnBound := Proof.Argon2.column_lt p h.bounds.lanesPositive h.bounds.sliceBound h.bounds.indexBound + have previousBound := Proof.Argon2.previous_column_lt p h.bounds.lanesPositive h.bounds.memoryMinimum + (slice * p.segmentLen + index) + obtain ⟨refLane, refColumn⟩ := Proof.Argon2.reference_bounds p h.bounds.lanesPositive + h.bounds.memoryMinimum pass lane slice index (s.gpr .rdi) h.bounds.laneBound + have compressReady : FillCompress.Ready b := by + apply compress_ready p b (h.layout.of_keeps keeps) h.bounds.lanesPositive + lane (previousColumn p slice index) ref.1 ref.2 lane (currentColumn p slice index) + h.bounds.laneBound previousBound refLane refColumn h.bounds.laneBound columnBound + · rw [pointers.previous, matrixB]; rfl + · rw [pointers.reference, matrixB] + · rw [pointers.current, matrixB]; rfl + exact ⟨compressReady, cur, prev, other, keeps⟩ + +end VG.Proof.Argon2.X86_64.FillKernel diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelSpec.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelSpec.lean new file mode 100644 index 000000000..09daeddab --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelSpec.lean @@ -0,0 +1,32 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelMatrix +import VerifiedGarbage.Proof.Argon2.FillStep + +/-! Relate the complete assembly step to the reviewed filling-state transition. -/ + +namespace VG.Proof.Argon2.X86_64.FillKernel + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem update_spec (s : State) (p : Params) (pass lane slice index : Nat) (state : FillState) + (active : pass ≠ 0 ∨ slice ≠ 0 ∨ 2 ≤ index) + (random : s.gpr .rdi = Proof.Argon2.FillStep.random p pass lane slice index state.memory) : + state.memory.set! (currentIndex p lane slice index) (nextBlock s p pass lane slice index state.memory) = + (fillBlock p pass slice lane index state).memory := by + rw [Proof.Argon2.FillStep.memory p pass lane slice index state active] + unfold nextBlock referenceIndex + rw [random] + rfl + +theorem code_spec_ok (s : State) (p : Params) (pass lane slice index : Nat) + (ready : Ready p pass lane slice index s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (matrix s) p.blocks state.memory) + (random : s.gpr .rdi = Proof.Argon2.FillStep.random p pass lane slice index state.memory) : + WP isa Impl.Argon2.X86_64.FillKernel.code s fun t => Done s t p pass lane slice index ∧ + Proof.Argon2.Represents t.mem (matrix s) p.blocks (fillBlock p pass slice lane index state).memory := by + refine (code_ok s p pass lane slice index ready).mono ?_ + intro t done + have represented' := done.represents ready state.memory represented + rw [update_spec s p pass lane slice index state ready.bounds.active random] at represented' + exact ⟨done, represented'⟩ + +end VG.Proof.Argon2.X86_64.FillKernel diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelStable.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelStable.lean new file mode 100644 index 000000000..007196a70 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillKernelStable.lean @@ -0,0 +1,16 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillKernelArgs + +/-! Register-only helpers retain the filling allocation and position invariants. -/ + +namespace VG.Proof.Argon2.X86_64.FillKernel + +open VG VG.X86_64 + +theorem Ready.of_keeps {p : Spec.Argon2.Params} {pass lane slice index : Nat} {s t : State} + (h : Ready p pass lane slice index s) (k : Divide.Keeps ReferenceMap.changed s t) : + Ready p pass lane slice index t := by + refine ⟨h.layout.of_keeps k, h.bounds, h.position.of_keeps k, ?_, ?_⟩ + · rw [k.mem, k.regs .rbp (by decide)]; exact h.passWord + · rw [k.mem, k.regs .rbp (by decide)]; exact h.lanesWord + +end VG.Proof.Argon2.X86_64.FillKernel diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLaneAdvance.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLaneAdvance.lean new file mode 100644 index 000000000..b145987f7 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLaneAdvance.lean @@ -0,0 +1,69 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillLanes +import VerifiedGarbage.Proof.Argon2.X86_64.SegmentSetup + +/-! Lane advancement retains the allocation and public segment parameters. -/ + +namespace VG.Proof.Argon2.X86_64.FillLanes + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillLanes + +theorem advance_ok (s : State) (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 184) 8) : + WP isa (.block advance) s fun t => t.gpr .rbx = s.gpr .rbx + 1 ∧ + t.cf = decide ((s.gpr .rbx + 1).toNat < (s.mem.readW (off (s.gpr .rbp) 184) 64).toNat) ∧ + Divide.Keeps [.rbx] s t := by + apply WP.of_runBlock + simp only [advance, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, execAlu, + State.load64, ea_at, RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, + RegUpd.mem_setReg, RegUpd.mem_arithFlags, RegUpd.rd_setReg, RegUpd.rd_arithFlags, + RegUpd.wr_setReg, RegUpd.wr_arithFlags, RegUpd.cf_arithFlags, hr, + show BitVec.signExtend 64 (1 : BitVec 32) = (1 : Addr) from rfl, + reduceCtorEq, ite_true, ite_false, Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr, ite_false] + all_goals rfl + +theorem context_ready {p : Params} {pass lane slice index old : Nat} {s : State} + (h : FillContext.Ready p pass lane slice index old s) : SegmentSetup.Ready p pass lane slice s := + ⟨h.parameters, h.layout, h.cache.layout, h.cache.reads, h.cache.write, ⟨old, h.cache.words⟩, + h.matrixWork, h.position.laneLength, h.position.segmentLength, h.lanesWord⟩ + +theorem finished_ready {p : Params} {pass lane slice : Nat} {s t : State} {state : FillState} + (parameters : FillContext.Parameters p pass lane slice) (h : FillSegment.Finished s t p pass lane slice state) : + SegmentSetup.Ready p pass lane slice t := by + obtain ⟨old, context⟩ := FillContext.finished_context parameters h + exact context_ready context + +theorem change_lane_ready {p : Params} {pass lane slice newLane : Nat} {s t : State} + (h : SegmentSetup.Ready p pass lane slice s) (k : Divide.Keeps [.rbx] s t) + (value : t.gpr .rbx = BitVec.ofNat 64 newLane) (active : newLane < p.lanes) : + SegmentSetup.Ready p pass newLane slice t := by + have bp := k.regs .rbp (by decide) + have sp := k.regs .rsp (by decide) + have base : FillKernel.matrix t = FillKernel.matrix s := by unfold FillKernel.matrix; rw [k.mem, bp] + have work : AddressCalls.work t = AddressCalls.work s := by unfold AddressCalls.work; rw [k.mem, bp] + refine ⟨{ h.parameters with laneBound := active }, h.layout.of_preserved bp sp base work k.rd k.wr, + ?_, ?_, ?_, ?_, ?_, (k.regs .r12 (by decide)).trans h.laneLength, + (k.regs .r13 (by decide)).trans h.segmentLength, ?_⟩ + · constructor + · rw [k.rd, k.wr, bp]; exact h.addressLayout.frameRead + · rw [k.wr, work]; exact h.addressLayout.workWrite + · rw [bp, work]; exact h.addressLayout.frameWork + · rw [bp, sp]; exact h.addressLayout.frameStack + · rw [sp, work]; exact h.addressLayout.stackWork + · rw [k.rd, k.wr, bp]; exact h.reads + · rw [k.wr, bp]; exact h.write + · obtain ⟨old, words⟩ := h.words + refine ⟨old, ?_, value, (k.regs .r14 (by decide)).trans words.sliceWord, ?_, ?_, ?_, ?_⟩ + all_goals rw [k.mem, bp] + · exact words.passWord + · exact words.blocksWord + · exact words.passesWord + · exact words.variantWord + · exact words.counterWord + · rw [base, work]; exact h.matrixWork + · rw [k.mem, bp]; exact h.lanesWord + +end VG.Proof.Argon2.X86_64.FillLanes diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanes.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanes.lean new file mode 100644 index 000000000..a396f0f1b --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanes.lean @@ -0,0 +1,64 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillLanesBody +import VerifiedGarbage.Proof.Argon2.Lanes + +/-! Termination and correctness of all remaining lanes in one slice. -/ + +namespace VG.Proof.Argon2.X86_64.FillLanes + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Finished (s t : State) (p : Params) (pass slice : Nat) (state : FillState) : Prop where + represented : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks state.memory + matrix : FillKernel.matrix t = FillKernel.matrix s + work : AddressCalls.work t = AddressCalls.work s + laneWord : t.gpr .rbx = BitVec.ofNat 64 p.lanes + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (FillBlock.writes s p) s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + regs : ∀ r ∈ calleeSaved, r ≠ .rbx → r ≠ .r15 → t.gpr r = s.gpr r + header : ∃ lane a, lane + 1 = p.lanes ∧ SegmentSetup.Ready p pass lane slice a ∧ Divide.Keeps [.rbx] a t + +theorem Done.finished {s t : State} {p : Params} {pass lane slice : Nat} {state : FillState} + (h : Done s t p pass lane slice state) (last : lane + 1 = p.lanes) : + Finished s t p pass slice (Proof.Argon2.segment p pass lane slice 0 p.segmentLen state) := by + obtain ⟨a, ready, keeps⟩ := h.header + exact ⟨h.represented, h.matrix, h.work, last ▸ h.laneWord, h.rd, h.wr, h.frame, h.mxcsr, + h.regs, lane, a, last, ready, keeps⟩ + +theorem Finished.prepend {s a t : State} {p : Params} {pass lane slice : Nat} {state finalState : FillState} + (first : Done s a p pass lane slice state) (rest : Finished a t p pass slice finalState) : + Finished s t p pass slice finalState := by + refine ⟨rest.represented, rest.matrix.trans first.matrix, rest.work.trans first.work, rest.laneWord, + rest.rd.trans first.rd, rest.wr.trans first.wr, ?_, rest.mxcsr.trans first.mxcsr, ?_, rest.header⟩ + · have frame := rest.frame + rw [FillBlock.writes, first.matrix, first.work, + first.regs .rsp (by simp [calleeSaved]) (by decide) (by decide), + first.regs .rbp (by simp [calleeSaved]) (by decide) (by decide)] at frame + exact first.frame.trans frame + · intro r hr bx ix; exact (rest.regs r hr bx ix).trans (first.regs r hr bx ix) + +theorem loop_ok (count : Nat) (s : State) (p : Params) (pass lane slice : Nat) + (h : SegmentSetup.Ready p pass lane slice s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) + (positive : 0 < count) (endLane : lane + count = p.lanes) : + WP isa Impl.Argon2.X86_64.FillLanes.loop s + (Finished s · p pass slice (Proof.Argon2.lanes p pass slice lane count state)) := by + induction count generalizing s lane state with + | zero => omega + | succ n ih => + obtain ⟨trace, a, run, done⟩ := body_ok s p pass lane slice h state represented + rw [Proof.Argon2.lanes_succ] + cases n with + | zero => + have last : lane + 1 = p.lanes := endLane + refine ⟨_, a, .loopExit run ?_, done.finished last⟩ + simp only [eval, done.cf, last, Nat.lt_irrefl, decide_false] + | succ n => + have active : lane + 1 < p.lanes := by omega + obtain ⟨restTrace, t, restRun, finished⟩ := ih a (lane + 1) (done.next active) + (Proof.Argon2.segment p pass lane slice 0 p.segmentLen state) done.represented (by omega) (by omega) + refine ⟨_, t, .loopNext run ?_ restRun, finished.prepend done⟩ + simp only [eval, done.cf, active, decide_true] + +end VG.Proof.Argon2.X86_64.FillLanes diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanesBody.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanesBody.lean new file mode 100644 index 000000000..94f71d2fd --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanesBody.lean @@ -0,0 +1,54 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillLaneAdvance + +/-! One lane iteration fills its segment and advances the public lane. -/ + +namespace VG.Proof.Argon2.X86_64.FillLanes + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Done (s t : State) (p : Params) (pass lane slice : Nat) (state : FillState) : Prop where + represented : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks + (Proof.Argon2.segment p pass lane slice 0 p.segmentLen state).memory + matrix : FillKernel.matrix t = FillKernel.matrix s + work : AddressCalls.work t = AddressCalls.work s + laneWord : t.gpr .rbx = BitVec.ofNat 64 (lane + 1) + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (FillBlock.writes s p) s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + regs : ∀ r ∈ calleeSaved, r ≠ .rbx → r ≠ .r15 → t.gpr r = s.gpr r + header : ∃ a, SegmentSetup.Ready p pass lane slice a ∧ Divide.Keeps [.rbx] a t + cf : t.cf = decide (lane + 1 < p.lanes) + next : lane + 1 < p.lanes → SegmentSetup.Ready p pass (lane + 1) slice t + +theorem body_ok (s : State) (p : Params) (pass lane slice : Nat) + (h : SegmentSetup.Ready p pass lane slice s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) : + WP isa Impl.Argon2.X86_64.FillLanes.body s (Done s · p pass lane slice state) := by + unfold Impl.Argon2.X86_64.FillLanes.body + refine WP.seq ((SegmentSetup.code_ok s p pass lane slice h state represented).mono ?_) + intro a filled + have ready := finished_ready h.parameters filled + refine (advance_ok a (ready.layout.frameRead 184 (by simp))).mono ?_ + rintro t ⟨value, flag, keeps⟩ + obtain ⟨old, words⟩ := ready.words + have bp := keeps.regs .rbp (by decide) + have base : FillKernel.matrix t = FillKernel.matrix a := by unfold FillKernel.matrix; rw [keeps.mem, bp] + have work : AddressCalls.work t = AddressCalls.work a := by unfold AddressCalls.work; rw [keeps.mem, bp] + have added : a.gpr .rbx + 1 = BitVec.ofNat 64 (lane + 1) := by + rw [words.laneWord, BitVec.ofNat_add]; rfl + have nextWord := value.trans added + have lanesBound : p.lanes < 2 ^ 64 := Nat.lt_trans h.parameters.lanesBound (by decide) + have laneBound := h.parameters.laneBound + refine ⟨?_, base.trans filled.matrix, work.trans filled.work, nextWord, keeps.rd.trans filled.rd, + keeps.wr.trans filled.wr, ?_, keeps.mxcsr.trans filled.mxcsr, ?_, ⟨a, ready, keeps⟩, ?_, ?_⟩ + · rw [keeps.mem, base]; exact filled.represented + · rw [keeps.mem]; exact filled.frame + · intro r hr bx ix + have outside : r ∉ [Reg.rbx] := by simp only [List.mem_cons, List.not_mem_nil, or_false]; exact bx + exact (keeps.regs r outside).trans (filled.regs r hr ix) + · rw [flag, added, ready.lanesWord, ReferenceMap.word_nat (lane + 1) (by omega), + ReferenceMap.word_nat p.lanes lanesBound] + · intro active; exact change_lane_ready ready keeps nextWord active + +end VG.Proof.Argon2.X86_64.FillLanes diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanesBodyCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanesBodyCT.lean new file mode 100644 index 000000000..615877731 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanesBodyCT.lean @@ -0,0 +1,53 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillLanesBody +import VerifiedGarbage.Proof.Argon2.X86_64.SegmentSetupCT + +/-! Lane iteration preserves public allocations and loops on the public lane count. -/ + +namespace VG.Proof.Argon2.X86_64.FillLanes + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillLanes + +theorem advance_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) (.block advance) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr; simp only [List.mem_cons, List.not_mem_nil, or_false] at hr; subst r; exact h)) (by taint_decide) + +structure NextRelated (p : Params) (pass lane slice : Nat) (leftState rightState : FillState) (s t : State) : Prop where + ready : SegmentSetup.RelatedReady p pass lane slice s t + leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory + +theorem body_rel (p : Params) (pass lane slice : Nat) (leftState rightState : FillState) : + RelCT isa (SegmentSetup.Related p pass lane slice leftState rightState) body + (fun s t => s.cf = t.cf ∧ (lane + 1 < p.lanes → NextRelated p pass (lane + 1) slice + (Proof.Argon2.segment p pass lane slice 0 p.segmentLen leftState) + (Proof.Argon2.segment p pass lane slice 0 p.segmentLen rightState) s t)) := by + intro s t ts tt a b hp ea eb + cases ea with + | seq segmentA advanceA => + cases eb with + | seq segmentB advanceB => + obtain ⟨segmentTrace, _⟩ := SegmentSetup.code_rel p pass lane slice leftState rightState + _ _ _ _ _ _ hp segmentA segmentB + obtain ⟨_, sa, runA, filledA⟩ := SegmentSetup.code_ok s p pass lane slice hp.ready.left leftState hp.leftMatrix + obtain ⟨_, sb, runB, filledB⟩ := SegmentSetup.code_ok t p pass lane slice hp.ready.right rightState hp.rightMatrix + obtain ⟨_, rfl⟩ := Exec.det segmentA runA + obtain ⟨_, rfl⟩ := Exec.det segmentB runB + have bases := (filledA.regs .rbp (by simp [calleeSaved]) (by decide)).trans + (hp.ready.bases.trans (filledB.regs .rbp (by simp [calleeSaved]) (by decide)).symm) + obtain ⟨advancedTrace, _⟩ := advance_rel _ _ _ _ _ _ bases advanceA advanceB + obtain ⟨_, a', runA, doneA⟩ := body_ok s p pass lane slice hp.ready.left leftState hp.leftMatrix + obtain ⟨_, b', runB, doneB⟩ := body_ok t p pass lane slice hp.ready.right rightState hp.rightMatrix + obtain ⟨_, rfl⟩ := Exec.det (.seq segmentA advanceA) runA + obtain ⟨_, rfl⟩ := Exec.det (.seq segmentB advanceB) runB + refine ⟨by rw [segmentTrace, advancedTrace], doneA.cf.trans doneB.cf.symm, ?_⟩ + intro active + refine ⟨⟨doneA.next active, doneB.next active, ?_, ?_, + doneA.matrix.trans (hp.ready.matrices.trans doneB.matrix.symm), + doneA.work.trans (hp.ready.work.trans doneB.work.symm)⟩, doneA.represented, doneB.represented⟩ + · exact (doneA.regs .rbp (by simp [calleeSaved]) (by decide) (by decide)).trans + (hp.ready.bases.trans (doneB.regs .rbp (by simp [calleeSaved]) (by decide) (by decide)).symm) + · exact (doneA.regs .rsp (by simp [calleeSaved]) (by decide) (by decide)).trans + (hp.ready.stacks.trans (doneB.regs .rsp (by simp [calleeSaved]) (by decide) (by decide)).symm) + +end VG.Proof.Argon2.X86_64.FillLanes diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanesCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanesCT.lean new file mode 100644 index 000000000..0b33492ef --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillLanesCT.lean @@ -0,0 +1,52 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillLanes +import VerifiedGarbage.Proof.Argon2.X86_64.FillLanesBodyCT +import VerifiedGarbage.Proof.Argon2.LanesIndices + +/-! The lane loop exposes only the slice's specified reference log. -/ + +namespace VG.Proof.Argon2.X86_64.FillLanes + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Related (p : Params) (pass lane slice count : Nat) (leftState rightState : FillState) + (s t : State) : Prop where + ready : SegmentSetup.RelatedReady p pass lane slice s t + leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory + indices : (Proof.Argon2.lanes p pass slice lane count leftState).indices = + (Proof.Argon2.lanes p pass slice lane count rightState).indices + +theorem loop_rel (p : Params) (pass lane slice count : Nat) (leftState rightState : FillState) + (positive : 0 < count) (endLane : lane + count = p.lanes) : + RelCT isa (Related p pass lane slice count leftState rightState) Impl.Argon2.X86_64.FillLanes.loop (fun _ _ => True) := by + let I := fun n s t => ∃ (lane : Nat) (leftState rightState : FillState), + lane + n = p.lanes ∧ 0 < n ∧ Related p pass lane slice n leftState rightState s t + have steps : ∀ n, RelCT isa (I n) Impl.Argon2.X86_64.FillLanes.body fun s t => + isa.eval .b s = isa.eval .b t ∧ (isa.eval .b s = some false → True) ∧ + (isa.eval .b s = some true → ∃ m < n, I m s t) := by + intro n s t ts tt a b hp ea eb + obtain ⟨j, ls, rs, endLane, positive, hp⟩ := hp + cases n with + | zero => omega + | succ n => + have segmentRelated : SegmentSetup.Related p pass j slice ls rs s t := + ⟨hp.ready, hp.leftMatrix, hp.rightMatrix, Proof.Argon2.lanes_first_segment p pass slice j n ls rs + hp.ready.left.parameters.segment_bound.1 hp.indices⟩ + obtain ⟨trace, flags, next⟩ := body_rel p pass j slice ls rs _ _ _ _ _ _ segmentRelated ea eb + obtain ⟨_, a', runA, done⟩ := body_ok s p pass j slice hp.ready.left ls hp.leftMatrix + obtain ⟨_, rfl⟩ := Exec.det ea runA + refine ⟨trace, ?_, fun _ => trivial, ?_⟩ + · simp only [eval, flags] + · intro taken + have active : j + 1 < p.lanes := by + simp only [eval, done.cf, Option.some.injEq, decide_eq_true_eq] at taken + exact taken + obtain ⟨ready, matrixA, matrixB⟩ := next active + have indices := hp.indices + rw [Proof.Argon2.lanes_succ, Proof.Argon2.lanes_succ] at indices + exact ⟨n, by omega, j + 1, Proof.Argon2.segment p pass j slice 0 p.segmentLen ls, + Proof.Argon2.segment p pass j slice 0 p.segmentLen rs, by omega, by omega, ready, matrixA, matrixB, indices⟩ + exact (RelCT.loop I steps count).mono + (fun _ _ h => ⟨lane, leftState, rightState, endLane, positive, h⟩) (fun _ _ h => h) + +end VG.Proof.Argon2.X86_64.FillLanes diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPassCounter.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPassCounter.lean new file mode 100644 index 000000000..3a9765a34 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPassCounter.lean @@ -0,0 +1,42 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillIterations +import VerifiedGarbage.Proof.Argon2.X86_64.FillIteration + +/-! Increment, save and compare the public pass counter. -/ + +namespace VG.Proof.Argon2.X86_64.FillIterations + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillIterations + +theorem increment_ok (s : State) (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 0) 8) : + WP isa (.block increment) s fun t => + t.gpr .rax = s.mem.readW (off (s.gpr .rbp) 0) 64 + 1 ∧ Divide.Keeps [.rax] s t := by + apply WP.of_runBlock + simp only [increment, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, State.load64, + ea_at, hr, execAlu, RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, + show BitVec.signExtend 64 (1 : BitVec 32) = (1 : Addr) from rfl, + ite_true, Option.map_some, Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr, ite_false] + all_goals rfl + +theorem saveCheck_ok (s : State) (hw : InRegions s.wr (off (s.gpr .rbp) 0) 8) + (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 72) 8) : + WP isa (.block saveCheck) s fun t => + t.mem = s.mem.writeW (off (s.gpr .rbp) 0) (s.gpr .rax) ∧ + t.gpr = s.gpr ∧ t.rd = s.rd ∧ t.wr = s.wr ∧ t.mxcsr = s.mxcsr ∧ + t.cf = decide ((s.gpr .rax).toNat < (s.mem.readW (off (s.gpr .rbp) 72) 64).toNat) := by + apply WP.of_runBlock + have sep : Mem.Sep (off (s.gpr .rbp) 72) 8 (off (s.gpr .rbp) 0) 8 := + Offset.sep _ (by decide) (by decide) (by decide) + simp only [saveCheck, runBlock_cons, runStep_some, runBlock_nil, exec, State.store64, + State.load64, ea_at, hw, hr, readSrc, execAlu, ite_true, + Mem.readW_writeW_sep (w := 64) (w' := 64) sep (by decide), RegUpd.cf_arithFlags, + RegUpd.mem_arithFlags, RegUpd.gpr_arithFlags, RegUpd.rd_arithFlags, RegUpd.wr_arithFlags, + Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, trivial, trivial, trivial, ?_, trivial⟩ + rfl + +end VG.Proof.Argon2.X86_64.FillIterations diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPassSave.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPassSave.lean new file mode 100644 index 000000000..2c52b6633 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPassSave.lean @@ -0,0 +1,79 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillPassCounter + +/-! A saved pass counter changes only its eight-byte header word. -/ + +namespace VG.Proof.Argon2.X86_64.FillIterations + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillIterations + +structure Saved (s t : State) : Prop where + mem : t.mem = s.mem.writeW (off (s.gpr .rbp) 0) (s.mem.readW (off (s.gpr .rbp) 0) 64 + 1) + regs : ∀ r, r ≠ .rax → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + cf : t.cf = decide ((s.mem.readW (off (s.gpr .rbp) 0) 64 + 1).toNat < + (s.mem.readW (off (s.gpr .rbp) 72) 64).toNat) + frame : Frame [⟨off (s.gpr .rbp) 0, 8⟩] s.mem t.mem + +theorem advance_ok (s : State) (read : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 0) 8) + (write : InRegions s.wr (off (s.gpr .rbp) 0) 8) + (passesRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 72) 8) : WP isa advance s (Saved s) := by + unfold advance + refine WP.seq ((increment_ok s read).mono ?_) + rintro a ⟨value, keeps⟩ + have bp := keeps.regs .rbp (by decide) + have readA : InRegions (a.rd ++ a.wr) (off (a.gpr .rbp) 72) 8 := by + rw [keeps.rd, keeps.wr, bp]; exact passesRead + have writeA : InRegions a.wr (off (a.gpr .rbp) 0) 8 := by rw [keeps.wr, bp]; exact write + refine (saveCheck_ok a writeA readA).mono ?_ + rintro t ⟨mem, regs, rd, wr, mx, cf⟩ + have finalMem : t.mem = s.mem.writeW (off (s.gpr .rbp) 0) (s.mem.readW (off (s.gpr .rbp) 0) 64 + 1) := by + rw [mem, keeps.mem, bp, value] + refine ⟨finalMem, ?_, rd.trans keeps.rd, wr.trans keeps.wr, mx.trans keeps.mxcsr, ?_, ?_⟩ + · intro r ne + have outside : r ∉ [Reg.rax] := by simp only [List.mem_cons, List.not_mem_nil, or_false]; exact ne + exact (congrFun regs r).trans (keeps.regs r outside) + · rw [cf, value, keeps.mem, bp] + · rw [finalMem] + exact (Frame.refl _ _).writeW (r := ⟨off (s.gpr .rbp) 0, 8⟩) (by simp) _ (Region.contains_self _ _) + +theorem Saved.read {s t : State} (h : Saved s t) (d : Nat) (separate : 8 ≤ d) (bound : d + 8 ≤ 272) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [h.regs .rbp (by decide), h.mem] + exact Mem.readW_writeW_sep (Offset.sep _ (d := d) (n := 8) (e := 0) (k := 8) + (by omega) (by omega) (by decide)) (by decide) + +theorem Saved.words {s t : State} {p : Params} {pass lane slice old : Nat} + (h : Saved s t) (words : AddressHeader.Words p pass lane slice old s) : + AddressHeader.Words p (pass + 1) lane slice old t := by + refine ⟨?_, (h.regs .rbx (by decide)).trans words.laneWord, + (h.regs .r14 (by decide)).trans words.sliceWord, + (h.read 240 (by decide) (by decide)).trans words.blocksWord, + (h.read 72 (by decide) (by decide)).trans words.passesWord, + (h.read 112 (by decide) (by decide)).trans words.variantWord, + (h.read 8 (by decide) (by decide)).trans words.counterWord⟩ + rw [h.regs .rbp (by decide), h.mem, Mem.readW_writeW_self64, words.passWord, BitVec.ofNat_add] + rfl + +theorem Saved.header {s t : State} {p : Params} {pass lane slice : Nat} + (h : Saved s t) (header : FillHeader.Ready p pass lane slice s) : FillHeader.Ready p (pass + 1) lane slice t := by + have bp := h.regs .rbp (by decide) + have sp := h.regs .rsp (by decide) + have base : FillKernel.matrix t = FillKernel.matrix s := h.read 232 (by decide) (by decide) + have work : AddressCalls.work t = AddressCalls.work s := h.read 248 (by decide) (by decide) + refine ⟨header.layout.of_preserved bp sp base work h.rd h.wr, ?_, ?_, ?_, ?_, ?_, + (h.regs .r12 (by decide)).trans header.laneLength, (h.regs .r13 (by decide)).trans header.segmentLength, ?_⟩ + · constructor + · rw [h.rd, h.wr, bp]; exact header.addressLayout.frameRead + · rw [h.wr, work]; exact header.addressLayout.workWrite + · rw [bp, work]; exact header.addressLayout.frameWork + · rw [bp, sp]; exact header.addressLayout.frameStack + · rw [sp, work]; exact header.addressLayout.stackWork + · rw [h.rd, h.wr, bp]; exact header.reads + · rw [h.wr, bp]; exact header.write + · obtain ⟨old, words⟩ := header.words; exact ⟨old, h.words words⟩ + · rw [base, work]; exact header.matrixWork + · exact (h.read 184 (by decide) (by decide)).trans header.lanesWord + +end VG.Proof.Argon2.X86_64.FillIterations diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointers.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointers.lean new file mode 100644 index 000000000..346b12d82 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointers.lean @@ -0,0 +1,99 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillPointersArgs +import VerifiedGarbage.Proof.Argon2.X86_64.FillColumn +import VerifiedGarbage.Proof.Argon2.X86_64.BlockAddress + +/-! Compose the matrix addresses while retaining the enclosing loop position. -/ + +namespace VG.Proof.Argon2.X86_64.FillPointers + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FillPointers + +def address (base lane column q : Addr) : Addr := (lane * q + column) * 1024 + base + +def column (s : State) : Addr := s.gpr .r14 * s.gpr .r13 + s.gpr .r15 + +def predecessor (s : State) : Addr := + (if column s = 0 then s.gpr .r12 else column s) - 1 + +def changed : List Reg := [.rax, .rdx, .rcx, .rdi, .rsi, .r10, .r11] + +theorem current_ok (s : State) : WP isa current s fun t => + t.gpr .rax = address (s.gpr .r8) (s.gpr .rbx) (s.gpr .rcx) (s.gpr .r12) ∧ + Divide.Keeps [.rax, .rdx] s t := by + unfold current + refine WP.seq ((currentArgs_ok s).mono ?_) + rintro a ⟨lane, ka⟩ + refine (BlockAddress.code_ok a).mono ?_ + rintro t ⟨pointer, kt⟩ + refine ⟨?_, (ka.mono (by simp)).trans kt⟩ + rw [pointer, lane, ka.regs .r12 (by decide), ka.regs .rcx (by decide), ka.regs .r8 (by decide), address] + +theorem previous_ok (s : State) : WP isa previous s fun t => + t.gpr .r10 = s.gpr .rax ∧ + t.gpr .rax = address (s.gpr .r8) (s.gpr .rbx) (s.gpr .rdi) (s.gpr .r12) ∧ + Divide.Keeps [.rax, .rdx, .rcx, .r10] s t := by + unfold previous + refine WP.seq ((previousArgs_ok s).mono ?_) + rintro a ⟨saved, col, lane, ka⟩ + refine (BlockAddress.code_ok a).mono ?_ + rintro t ⟨pointer, kt⟩ + refine ⟨(kt.regs .r10 (by decide)).trans saved, ?_, + (ka.mono (by simp)).trans (kt.mono (by simp))⟩ + rw [pointer, lane, col, ka.regs .r12 (by decide), ka.regs .r8 (by decide), address] + +theorem reference_ok (s : State) : WP isa reference s fun t => + t.gpr .r11 = s.gpr .rax ∧ + t.gpr .rax = address (s.gpr .r8) (s.gpr .r9) (s.gpr .rsi) (s.gpr .r12) ∧ + Divide.Keeps [.rax, .rdx, .rcx, .r11] s t := by + unfold reference + refine WP.seq ((referenceArgs_ok s).mono ?_) + rintro a ⟨saved, col, lane, ka⟩ + refine (BlockAddress.code_ok a).mono ?_ + rintro t ⟨pointer, kt⟩ + refine ⟨(kt.regs .r11 (by decide)).trans saved, ?_, + (ka.mono (by simp)).trans (kt.mono (by simp))⟩ + rw [pointer, lane, col, ka.regs .r12 (by decide), ka.regs .r8 (by decide), address] + +theorem code_ok (s : State) : WP isa code s fun t => + t.gpr .r10 = address (s.gpr .r8) (s.gpr .rbx) (column s) (s.gpr .r12) ∧ + t.gpr .rdi = address (s.gpr .r8) (s.gpr .rbx) (predecessor s) (s.gpr .r12) ∧ + t.gpr .rsi = address (s.gpr .r8) (s.gpr .r9) (s.gpr .rdi) (s.gpr .r12) ∧ + Divide.Keeps changed s t := by + unfold code + refine WP.seq ((saveReference_ok s).mono ?_) + rintro a ⟨refColumn, ka⟩ + refine WP.seq ((FillColumn.code_ok a).mono ?_) + rintro b ⟨curColumn, prevColumn, kb⟩ + refine WP.seq ((current_ok b).mono ?_) + rintro c ⟨curPointer, kc⟩ + refine WP.seq ((previous_ok c).mono ?_) + rintro d ⟨savedCurrent, prevPointer, kd⟩ + refine WP.seq ((reference_ok d).mono ?_) + rintro e ⟨savedPrevious, refPointer, ke⟩ + refine (finishArgs_ok e).mono ?_ + rintro t ⟨referenceResult, previousResult, kt⟩ + have coords : column a = column s := by + unfold column + rw [ka.regs .r14 (by decide), ka.regs .r13 (by decide), ka.regs .r15 (by decide)] + refine ⟨?_, ?_, ?_, ?_⟩ + · rw [kt.regs .r10 (by decide), ke.regs .r10 (by decide), savedCurrent, curPointer, + kb.regs .r8 (by decide), ka.regs .r8 (by decide), kb.regs .rbx (by decide), + ka.regs .rbx (by decide), kb.regs .r12 (by decide), ka.regs .r12 (by decide), curColumn] + exact congrArg (fun col => address (s.gpr .r8) (s.gpr .rbx) col (s.gpr .r12)) coords + · rw [previousResult, savedPrevious, prevPointer, kc.regs .r8 (by decide), + kc.regs .rbx (by decide), kc.regs .rdi (by decide), kc.regs .r12 (by decide), + kb.regs .r8 (by decide), ka.regs .r8 (by decide), kb.regs .rbx (by decide), + ka.regs .rbx (by decide), kb.regs .r12 (by decide), ka.regs .r12 (by decide), prevColumn] + change address _ _ ((if column a = 0 then a.gpr .r12 else column a) - 1) _ = _ + rw [coords, ka.regs .r12 (by decide), predecessor] + · rw [referenceResult, refPointer, kd.regs .r8 (by decide), kd.regs .r9 (by decide), + kd.regs .rsi (by decide), kd.regs .r12 (by decide), kc.regs .r8 (by decide), + kc.regs .r9 (by decide), kc.regs .rsi (by decide), kc.regs .r12 (by decide), + kb.regs .r8 (by decide), kb.regs .r9 (by decide), kb.regs .rsi (by decide), + kb.regs .r12 (by decide), ka.regs .r8 (by decide), ka.regs .r9 (by decide), + ka.regs .r12 (by decide), refColumn] + · exact (((((ka.mono (by simp [changed])).trans (kb.mono (by simp [changed]))).trans + (kc.mono (by simp [changed]))).trans (kd.mono (by simp [changed]))).trans + (ke.mono (by simp [changed]))).trans (kt.mono (by simp [changed])) + +end VG.Proof.Argon2.X86_64.FillPointers diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersArgs.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersArgs.lean new file mode 100644 index 000000000..f55ec4160 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersArgs.lean @@ -0,0 +1,76 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillPointers +import VerifiedGarbage.Proof.Argon2.X86_64.DivideStep + +/-! Short register-setup steps for the filling pointers. -/ + +namespace VG.Proof.Argon2.X86_64.FillPointers + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FillPointers + +theorem saveReference_ok (s : State) : WP isa (.block saveReference) s fun t => + t.gpr .rsi = s.gpr .rdi ∧ Divide.Keeps [.rsi] s t := by + apply WP.of_runBlock + simp only [saveReference, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + Option.map_some, Option.some.injEq, exists_eq_left', RegUpd.gpr_setReg, ite_true] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + exact ite_eq_right hr + all_goals rfl + +theorem currentArgs_ok (s : State) : WP isa (.block currentArgs) s fun t => + t.gpr .rax = s.gpr .rbx ∧ Divide.Keeps [.rax] s t := by + apply WP.of_runBlock + simp only [currentArgs, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + Option.map_some, Option.some.injEq, exists_eq_left', RegUpd.gpr_setReg, ite_true] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + exact ite_eq_right hr + all_goals rfl + +theorem previousArgs_ok (s : State) : WP isa (.block previousArgs) s fun t => + t.gpr .r10 = s.gpr .rax ∧ t.gpr .rcx = s.gpr .rdi ∧ t.gpr .rax = s.gpr .rbx ∧ + Divide.Keeps [.r10, .rcx, .rax] s t := by + apply WP.of_runBlock + simp only [previousArgs, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + Option.map_some, Option.some.injEq, exists_eq_left', RegUpd.gpr_setReg, + reduceCtorEq, ite_true, ite_false] + refine ⟨trivial, trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, hr.1, hr.2.1, hr.2.2, ite_false] + all_goals rfl + +theorem referenceArgs_ok (s : State) : WP isa (.block referenceArgs) s fun t => + t.gpr .r11 = s.gpr .rax ∧ t.gpr .rcx = s.gpr .rsi ∧ t.gpr .rax = s.gpr .r9 ∧ + Divide.Keeps [.r11, .rcx, .rax] s t := by + apply WP.of_runBlock + simp only [referenceArgs, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + Option.map_some, Option.some.injEq, exists_eq_left', RegUpd.gpr_setReg, + reduceCtorEq, ite_true, ite_false] + refine ⟨trivial, trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, hr.1, hr.2.1, hr.2.2, ite_false] + all_goals rfl + +theorem finishArgs_ok (s : State) : WP isa (.block finishArgs) s fun t => + t.gpr .rsi = s.gpr .rax ∧ t.gpr .rdi = s.gpr .r11 ∧ + Divide.Keeps [.rsi, .rdi] s t := by + apply WP.of_runBlock + simp only [finishArgs, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + Option.map_some, Option.some.injEq, exists_eq_left', RegUpd.gpr_setReg, + reduceCtorEq, ite_true, ite_false] + refine ⟨trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, hr.1, hr.2, ite_false] + all_goals rfl + +end VG.Proof.Argon2.X86_64.FillPointers diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersCT.lean new file mode 100644 index 000000000..b531c5cc2 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersCT.lean @@ -0,0 +1,17 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillPointersLit +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! The pointer setup only branches on the public current column. +Reference coordinates may differ without changing its execution trace. -/ + +namespace VG.Proof.Argon2.X86_64.FillPointers + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FillPointers + +theorem code_rel : RelCT isa + (fun s t => ∀ r ∈ [Reg.r8, .rbx, .r12, .r13, .r14, .r15], s.gpr r = t.gpr r) code + (fun s t => ∀ r ∈ [Reg.r10], s.gpr r = t.gpr r) := + RelCT.taintRegs (τ := Taint.ofRegs [.r8, .rbx, .r12, .r13, .r14, .r15]) + (fun _ _ h => Taint.agree_ofRegs h) [Reg.r10] (by taint_decide) + +end VG.Proof.Argon2.X86_64.FillPointers diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersLit.lean new file mode 100644 index 000000000..6b85e3f1f --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersLit.lean @@ -0,0 +1,10 @@ +import VerifiedGarbage.Proof.Framework.X86_64.Lit +import VerifiedGarbage.Impl.Argon2.X86_64.FillPointers + +/-! Checked literal of the complete filling pointer setup. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.FillPointers.code + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersNat.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersNat.lean new file mode 100644 index 000000000..1e4643517 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillPointersNat.lean @@ -0,0 +1,51 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillPointers +import VerifiedGarbage.Proof.Argon2.X86_64.Memory +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapState +import VerifiedGarbage.Proof.Argon2.FillPositions + +/-! Matrix pointers are the natural-number block offsets in the specification. -/ + +namespace VG.Proof.Argon2.X86_64.FillPointers + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FillPointers + +def cell (base : Addr) (p : Spec.Argon2.Params) (lane column : Nat) : Addr := + off base ((lane * p.laneLen + column) * 1024) + +theorem address_nat (base : Addr) (lane column q : Nat) : + address base (BitVec.ofNat 64 lane) (BitVec.ofNat 64 column) (BitVec.ofNat 64 q) = + off base ((lane * q + column) * 1024) := by + unfold address off + change (BitVec.ofNat 64 lane * BitVec.ofNat 64 q + BitVec.ofNat 64 column) * + BitVec.ofNat 64 1024 + base = _ + rw [← BitVec.ofNat_mul, ← BitVec.ofNat_add, ← BitVec.ofNat_mul, BitVec.add_comm] + +theorem code_nat_ok (s : State) (p : Spec.Argon2.Params) (pass lane slice index refLane refColumn : Nat) + (bounds : ReferenceMap.Bounds p pass lane slice index) + (position : ReferenceMap.Position p lane slice index s) + (rl : s.gpr .r9 = BitVec.ofNat 64 refLane) + (rc : s.gpr .rdi = BitVec.ofNat 64 refColumn) : + WP isa code s fun t => + t.gpr .r10 = cell (s.gpr .r8) p lane (slice * p.segmentLen + index) ∧ + t.gpr .rdi = cell (s.gpr .r8) p lane ((slice * p.segmentLen + index + p.laneLen - 1) % p.laneLen) ∧ + t.gpr .rsi = cell (s.gpr .r8) p refLane refColumn ∧ Divide.Keeps changed s t := by + refine (code_ok s).mono ?_ + rintro t ⟨current, previous, reference, keeps⟩ + have col : column s = BitVec.ofNat 64 (slice * p.segmentLen + index) := by + unfold column + rw [position.slice, position.segmentLength, position.index, ← BitVec.ofNat_mul, ← BitVec.ofNat_add] + have prev : predecessor s = BitVec.ofNat 64 + ((slice * p.segmentLen + index + p.laneLen - 1) % p.laneLen) := by + unfold predecessor + rw [col, position.laneLength] + have positive := Proof.Argon2.segmentLen_ge_two p bounds.lanesPositive bounds.memoryMinimum + have q := Proof.Argon2.laneLen_segments p bounds.lanesPositive + exact FillColumn.previous_word_nat _ _ (by omega) + (Nat.lt_trans bounds.laneLength_bound (by decide)) + (Proof.Argon2.column_lt p bounds.lanesPositive bounds.sliceBound bounds.indexBound) + refine ⟨?_, ?_, ?_, keeps⟩ + · rw [current, position.current, col, position.laneLength, address_nat]; rfl + · rw [previous, position.current, prev, position.laneLength, address_nat]; rfl + · rw [reference, rl, rc, position.laneLength, address_nat]; rfl + +end VG.Proof.Argon2.X86_64.FillPointers diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegment.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegment.lean new file mode 100644 index 000000000..877df68e4 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegment.lean @@ -0,0 +1,70 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSegmentBody +import VerifiedGarbage.Proof.Argon2.Segment + +/-! Termination and correctness of the active suffix of one segment. -/ + +namespace VG.Proof.Argon2.X86_64.FillSegment + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Finished (s t : State) (p : Params) (pass lane slice : Nat) (state : FillState) : Prop where + represented : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks state.memory + matrix : FillKernel.matrix t = FillKernel.matrix s + work : AddressCalls.work t = AddressCalls.work s + position : ReferenceMap.Position p lane slice p.segmentLen t + layout : FillKernel.Layout p t + cache : ∃ old, AddressCache.Invariant p pass lane slice old t + matrixWork : (⟨FillKernel.matrix t, p.blocks * 1024⟩ : Region).Disjoint ⟨AddressCalls.work t, 8192⟩ + passWord : t.mem.readW (off (t.gpr .rbp) 0) 64 = BitVec.ofNat 64 pass + lanesWord : t.mem.readW (off (t.gpr .rbp) 184) 64 = BitVec.ofNat 64 p.lanes + regs : ∀ r ∈ calleeSaved, r ≠ .r15 → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (FillBlock.writes s p) s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + +theorem Done.finished {s t : State} {p : Params} {pass lane slice index : Nat} {state : FillState} + (h : Done s t p pass lane slice index state) (last : index + 1 = p.segmentLen) : + Finished s t p pass lane slice (fillBlock p pass slice lane index state) := + ⟨h.represented, h.matrix, h.work, last ▸ h.position, h.layout, h.cache, + h.matrixWork, h.passWord, h.lanesWord, h.regs, h.rd, h.wr, h.frame, h.mxcsr⟩ + +theorem Finished.prepend {s a t : State} {p : Params} {pass lane slice index : Nat} + {state finalState : FillState} (first : Done s a p pass lane slice index state) + (rest : Finished a t p pass lane slice finalState) : Finished s t p pass lane slice finalState := by + refine ⟨rest.represented, rest.matrix.trans first.matrix, rest.work.trans first.work, + rest.position, rest.layout, rest.cache, rest.matrixWork, rest.passWord, rest.lanesWord, + ?_, rest.rd.trans first.rd, rest.wr.trans first.wr, ?_, rest.mxcsr.trans first.mxcsr⟩ + · intro r hr ne; exact (rest.regs r hr ne).trans (first.regs r hr ne) + · have frame := rest.frame + rw [FillBlock.writes, first.matrix, first.work, + first.regs .rsp (by simp [calleeSaved]) (by decide), + first.regs .rbp (by simp [calleeSaved]) (by decide)] at frame + exact first.frame.trans frame + +theorem loop_ok (count : Nat) (s : State) (p : Params) (pass lane slice index old : Nat) + (h : RandomSource.Ready p pass lane slice index old s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) + (positive : 0 < count) (endIndex : index + count = p.segmentLen) : + WP isa Impl.Argon2.X86_64.FillSegment.loop s + (Finished s · p pass lane slice (Proof.Argon2.segment p pass lane slice index count state)) := by + induction count generalizing s index old state with + | zero => omega + | succ n ih => + obtain ⟨trace, a, run, done⟩ := body_ok s p pass lane slice index old h state represented + rw [Proof.Argon2.segment_succ] + cases n with + | zero => + have last : index + 1 = p.segmentLen := endIndex + refine ⟨_, a, .loopExit run ?_, ?_⟩ + · simp only [eval, done.cf, last, Nat.lt_irrefl, decide_false] + · exact done.finished last + | succ n => + have active : index + 1 < p.segmentLen := by omega + obtain ⟨nextCounter, nextReady⟩ := done.next active + obtain ⟨restTrace, t, restRun, finished⟩ := ih a (index + 1) nextCounter nextReady + (fillBlock p pass slice lane index state) done.represented (by omega) (by omega) + refine ⟨_, t, .loopNext run ?_ restRun, finished.prepend done⟩ + simp only [eval, done.cf, active, decide_true] + +end VG.Proof.Argon2.X86_64.FillSegment diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegmentBody.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegmentBody.lean new file mode 100644 index 000000000..9223d2f44 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegmentBody.lean @@ -0,0 +1,63 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillIndex +import VerifiedGarbage.Proof.Argon2.X86_64.FillAllocation + +/-! One segment iteration updates the specified cell and advances its public index. -/ + +namespace VG.Proof.Argon2.X86_64.FillSegment + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Done (s t : State) (p : Params) (pass lane slice index : Nat) (state : FillState) : Prop where + represented : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks + (fillBlock p pass slice lane index state).memory + matrix : FillKernel.matrix t = FillKernel.matrix s + work : AddressCalls.work t = AddressCalls.work s + position : ReferenceMap.Position p lane slice (index + 1) t + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (FillBlock.writes s p) s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + regs : ∀ r ∈ calleeSaved, r ≠ .r15 → t.gpr r = s.gpr r + layout : FillKernel.Layout p t + cache : ∃ old, AddressCache.Invariant p pass lane slice old t + matrixWork : (⟨FillKernel.matrix t, p.blocks * 1024⟩ : Region).Disjoint ⟨AddressCalls.work t, 8192⟩ + passWord : t.mem.readW (off (t.gpr .rbp) 0) 64 = BitVec.ofNat 64 pass + lanesWord : t.mem.readW (off (t.gpr .rbp) 184) 64 = BitVec.ofNat 64 p.lanes + cf : t.cf = decide (index + 1 < p.segmentLen) + next : index + 1 < p.segmentLen → ∃ old, RandomSource.Ready p pass lane slice (index + 1) old t + +theorem body_ok (s : State) (p : Params) (pass lane slice index old : Nat) + (h : RandomSource.Ready p pass lane slice index old s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) : + WP isa Impl.Argon2.X86_64.FillSegment.body s (Done s · p pass lane slice index state) := by + unfold Impl.Argon2.X86_64.FillSegment.body + refine WP.seq ((FillBlock.code_ok s p pass lane slice index old h state represented).mono ?_) + intro a filled + obtain ⟨counter, ready⟩ := filled.ready + refine (advance_nat_ok a p pass lane slice index ready.filling).mono ?_ + rintro t ⟨value, cf, keeps⟩ + have bp := keeps.regs .rbp (by decide) + have base : FillKernel.matrix t = FillKernel.matrix a := by unfold FillKernel.matrix; rw [keeps.mem, bp] + have work : AddressCalls.work t = AddressCalls.work a := by unfold AddressCalls.work; rw [keeps.mem, bp] + refine ⟨?_, base.trans filled.matrix, work.trans filled.work, ?_, keeps.rd.trans filled.rd, + keeps.wr.trans filled.wr, ?_, keeps.mxcsr.trans filled.mxcsr, ?_, ?_, ?_, ?_, ?_, ?_, cf, ?_⟩ + · rw [keeps.mem, base]; exact filled.represented + · exact ⟨(keeps.regs .rbx (by decide)).trans ready.filling.position.current, + (keeps.regs .r12 (by decide)).trans ready.filling.position.laneLength, + (keeps.regs .r13 (by decide)).trans ready.filling.position.segmentLength, + (keeps.regs .r14 (by decide)).trans ready.filling.position.slice, value⟩ + · rw [keeps.mem]; exact filled.frame + · intro r hr ne + have outside : r ∉ [Reg.r15] := by simp only [List.mem_cons, List.not_mem_nil, or_false]; exact ne + exact (keeps.regs r outside).trans (filled.regs r hr) + · exact ready.filling.layout.of_preserved bp (keeps.regs .rsp (by decide)) base work keeps.rd keeps.wr + · exact ⟨counter, ready.cache.of_state (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl <;> exact keeps.regs _ (by decide)) keeps.mem keeps.rd keeps.wr⟩ + · rw [base, work]; exact ready.matrixWork + · rw [keeps.mem, bp]; exact ready.filling.passWord + · rw [keeps.mem, bp]; exact ready.filling.lanesWord + · intro active; exact ⟨counter, next_ready ready keeps value active⟩ + +end VG.Proof.Argon2.X86_64.FillSegment diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegmentBodyCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegmentBodyCT.lean new file mode 100644 index 000000000..61463d80e --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegmentBodyCT.lean @@ -0,0 +1,82 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSegmentBody +import VerifiedGarbage.Proof.Argon2.X86_64.FillBlockCT +import VerifiedGarbage.Proof.Argon2.X86_64.FillBlockCounter + +/-! Public counters and coordinates remain related across a segment iteration. -/ + +namespace VG.Proof.Argon2.X86_64.FillSegment + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillSegment + +theorem advance_rel : RelCT isa + (fun s t => ∀ r ∈ [Reg.r13, .r15], s.gpr r = t.gpr r) + (.block advance) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.r13, .r15]) + (fun _ _ h => Taint.agree_ofRegs h) (by taint_decide) + +structure NextRelated (p : Params) (pass lane slice index : Nat) (leftState rightState : FillState) + (s t : State) : Prop where + ready : ∃ old, RandomSource.Related p pass lane slice index old s t + leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory + +theorem body_rel (p : Params) (pass lane slice index old : Nat) (leftState rightState : FillState) : + RelCT isa (FillBlock.Related p pass lane slice index old leftState rightState) body + (fun s t => s.cf = t.cf ∧ (index + 1 < p.segmentLen → + NextRelated p pass lane slice (index + 1) + (fillBlock p pass slice lane index leftState) (fillBlock p pass slice lane index rightState) s t)) := by + intro s t ts tt a b hp ea eb + cases ea with + | seq fillA advanceA => + cases eb with + | seq fillB advanceB => + obtain ⟨filledTrace, _⟩ := FillBlock.code_rel p pass lane slice index old leftState rightState + _ _ _ _ _ _ hp fillA fillB + obtain ⟨_, sa, runA, filledA⟩ := FillBlock.code_ok s p pass lane slice index old hp.source.left leftState hp.leftMatrix + obtain ⟨_, sb, runB, filledB⟩ := FillBlock.code_ok t p pass lane slice index old hp.source.right rightState hp.rightMatrix + obtain ⟨_, rfl⟩ := Exec.det fillA runA + obtain ⟨_, rfl⟩ := Exec.det fillB runB + obtain ⟨counterA, readyA⟩ := filledA.ready + obtain ⟨counterB, readyB⟩ := filledB.ready + obtain ⟨advancedTrace, _⟩ := advance_rel _ _ _ _ _ _ (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact readyA.filling.position.segmentLength.trans readyB.filling.position.segmentLength.symm + · exact readyA.filling.position.index.trans readyB.filling.position.index.symm) advanceA advanceB + obtain ⟨_, a', advanceRunA, valueA, flagA, keptA⟩ := advance_nat_ok _ p pass lane slice index readyA.filling + obtain ⟨_, b', advanceRunB, valueB, flagB, keptB⟩ := advance_nat_ok _ p pass lane slice index readyB.filling + obtain ⟨_, rfl⟩ := Exec.det advanceA advanceRunA + obtain ⟨_, rfl⟩ := Exec.det advanceB advanceRunB + refine ⟨by rw [filledTrace, advancedTrace], flagA.trans flagB.symm, ?_⟩ + intro active + have nextA := next_ready readyA keptA valueA active + have nextB := next_ready readyB keptB valueB active + have counterWordA := FillBlock.counter_run hp.source.left leftState hp.leftMatrix fillA + have counterWordB := FillBlock.counter_run hp.source.right rightState hp.rightMatrix fillB + have wordEq : BitVec.ofNat 64 counterA = BitVec.ofNat 64 counterB := + readyA.cache.words.counterWord.symm.trans + (counterWordA.trans (counterWordB.symm.trans readyB.cache.words.counterWord)) + have counters := (ReferenceMap.word_eq counterA counterB readyA.cache.bound readyB.cache.bound).mp wordEq + subst counterB + refine ⟨⟨counterA, nextA, nextB, ?_, ?_, ?_, ?_⟩, ?_, ?_⟩ + · rw [keptA.regs .rbp (by decide), keptB.regs .rbp (by decide), + filledA.regs .rbp (by simp [calleeSaved]), filledB.regs .rbp (by simp [calleeSaved])] + exact hp.source.bases + · rw [keptA.regs .rsp (by decide), keptB.regs .rsp (by decide), + filledA.regs .rsp (by simp [calleeSaved]), filledB.regs .rsp (by simp [calleeSaved])] + exact hp.source.stacks + · unfold FillKernel.matrix + rw [keptA.mem, keptB.mem, keptA.regs .rbp (by decide), keptB.regs .rbp (by decide)] + exact filledA.matrix.trans (hp.source.matrices.trans filledB.matrix.symm) + · unfold AddressCalls.work + rw [keptA.mem, keptB.mem, keptA.regs .rbp (by decide), keptB.regs .rbp (by decide)] + exact filledA.work.trans (hp.source.work.trans filledB.work.symm) + · unfold FillKernel.matrix + rw [keptA.mem, keptA.regs .rbp (by decide)] + exact filledA.represented + · unfold FillKernel.matrix + rw [keptB.mem, keptB.regs .rbp (by decide)] + exact filledB.represented + +end VG.Proof.Argon2.X86_64.FillSegment diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegmentCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegmentCT.lean new file mode 100644 index 000000000..c1e1e44f0 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSegmentCT.lean @@ -0,0 +1,54 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSegment +import VerifiedGarbage.Proof.Argon2.X86_64.FillSegmentBodyCT +import VerifiedGarbage.Proof.Argon2.SegmentIndices + +/-! The segment loop exposes only the reviewed segment reference log. -/ + +namespace VG.Proof.Argon2.X86_64.FillSegment + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Related (p : Params) (pass lane slice index count old : Nat) (leftState rightState : FillState) + (s t : State) : Prop where + source : RandomSource.Related p pass lane slice index old s t + leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory + indices : (Proof.Argon2.segment p pass lane slice index count leftState).indices = + (Proof.Argon2.segment p pass lane slice index count rightState).indices + +theorem loop_rel (p : Params) (pass lane slice index count old : Nat) (leftState rightState : FillState) + (positive : 0 < count) (endIndex : index + count = p.segmentLen) : + RelCT isa (Related p pass lane slice index count old leftState rightState) + Impl.Argon2.X86_64.FillSegment.loop (fun _ _ => True) := by + let I := fun n s t => ∃ (index old : Nat) (leftState rightState : FillState), + index + n = p.segmentLen ∧ 0 < n ∧ Related p pass lane slice index n old leftState rightState s t + have steps : ∀ n, RelCT isa (I n) Impl.Argon2.X86_64.FillSegment.body fun s t => + isa.eval .b s = isa.eval .b t ∧ (isa.eval .b s = some false → True) ∧ + (isa.eval .b s = some true → ∃ m < n, I m s t) := by + intro n s t ts tt a b hp ea eb + obtain ⟨j, counter, ls, rs, endIndex, positive, hp⟩ := hp + cases n with + | zero => omega + | succ n => + have blockRelated : FillBlock.Related p pass lane slice j counter ls rs s t := + ⟨hp.source, hp.leftMatrix, hp.rightMatrix, + Proof.Argon2.segment_first_reference p pass lane slice j n ls rs + hp.source.left.filling.bounds.active hp.indices⟩ + obtain ⟨trace, flags, next⟩ := body_rel p pass lane slice j counter ls rs _ _ _ _ _ _ blockRelated ea eb + obtain ⟨_, a', runA, done⟩ := body_ok s p pass lane slice j counter hp.source.left ls hp.leftMatrix + obtain ⟨_, rfl⟩ := Exec.det ea runA + refine ⟨trace, ?_, fun _ => trivial, ?_⟩ + · simp only [eval, flags] + · intro taken + have active : j + 1 < p.segmentLen := by + simp only [eval, done.cf, Option.some.injEq, decide_eq_true_eq] at taken + exact taken + obtain ⟨⟨nextCounter, ready⟩, matrixA, matrixB⟩ := next active + have indices := hp.indices + rw [Proof.Argon2.segment_succ, Proof.Argon2.segment_succ] at indices + exact ⟨n, by omega, j + 1, nextCounter, fillBlock p pass slice lane j ls, + fillBlock p pass slice lane j rs, by omega, by omega, ready, matrixA, matrixB, indices⟩ + exact (RelCT.loop I steps count).mono + (fun _ _ h => ⟨index, old, leftState, rightState, endIndex, positive, h⟩) (fun _ _ h => h) + +end VG.Proof.Argon2.X86_64.FillSegment diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetup.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetup.lean new file mode 100644 index 000000000..5f98138f9 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetup.lean @@ -0,0 +1,75 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSetupEnvironment +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressSetup + +/-! Establish the complete pass-loop invariant from memory initialization's byte stride. -/ + +namespace VG.Proof.Argon2.X86_64.FillSetup + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Ready (p : Params) (s : State) : Prop where + environment : Environment p s + bound : 1024 * p.laneLen < 2 ^ 64 + stride : s.gpr .r13 = BitVec.ofNat 64 (1024 * p.laneLen) + +structure Prepared (s t : State) (p : Params) : Prop where + ready : FillIterations.Ready p 0 t + matrix : FillKernel.matrix t = FillKernel.matrix s + work : AddressCalls.work t = AddressCalls.work s + regs : ∀ r ∈ calleeSaved, r ≠ .rbx → r ≠ .r12 → r ≠ .r13 → r ≠ .r14 → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨s.gpr .rbp, 8⟩] s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + words : ∀ d, 8 ≤ d → d + 8 ≤ 272 → + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 + +theorem code_ok (s : State) (p : Params) (h : Ready p s) : + WP isa Impl.Argon2.X86_64.FillSetup.code s (Prepared s · p) := by + unfold Impl.Argon2.X86_64.FillSetup.code + refine WP.seq ((dimensions_nat_ok s p h.bound h.stride).mono ?_) + rintro a ⟨laneLength, segmentLength, keeps⟩ + have bp := keeps.regs .rbp (by decide) + have sp := keeps.regs .rsp (by decide) + have environment := h.environment.of_state bp sp keeps.mem keeps.rd keeps.wr + refine (reset_ok a environment.passWrite).mono ?_ + intro t reset + have header := reset.header environment ((reset.regs .r12 (by decide)).trans laneLength) + ((reset.regs .r13 (by decide)).trans segmentLength) + have words (d : Nat) (lower : 8 ≤ d) (upper : d + 8 ≤ 272) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [reset.read d lower upper, keeps.mem, bp] + refine ⟨⟨⟨environment.parameters, 0, 0, header⟩, environment.passesBound, ?_⟩, + words 232 (by decide) (by decide), words 248 (by decide) (by decide), ?_, + reset.rd.trans keeps.rd, reset.wr.trans keeps.wr, ?_, reset.mxcsr.trans keeps.mxcsr, words⟩ + · rw [reset.wr, reset.regs .rbp (by decide)]; exact environment.passWrite + · intro r hr bx q g sl + have ne : r ≠ .rax := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + exact (reset.regs r (by simp only [List.mem_cons, List.not_mem_nil, or_false, not_or]; exact ⟨ne, bx, sl⟩)).trans + (keeps.regs r (by simp only [List.mem_cons, List.not_mem_nil, or_false, not_or]; exact ⟨q, g⟩)) + · have frame := reset.frame + rw [bp, keeps.mem] at frame; exact frame + +theorem Prepared.represents {s t : State} {p : Params} (ready : Ready p s) (done : Prepared s t p) + (blocks : Array Block) (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks blocks) : + Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks blocks := by + rw [done.matrix] + refine ⟨represented.size, ?_⟩ + intro k hk + apply Eq.trans _ (represented.block k hk) + apply FillCompress.block_frame done.frame + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact (ready.environment.layout.matrixFrame.sub_left (Proof.Argon2.matrixCell_sub _ _ _ hk)).sub_right + (Region.sub_prefix (by decide)) + +theorem code_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + Impl.Argon2.X86_64.FillSetup.code (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr; simp only [List.mem_cons, List.not_mem_nil, or_false] at hr; subst r; exact h)) (by taint_decide) + +end VG.Proof.Argon2.X86_64.FillSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupDimensions.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupDimensions.lean new file mode 100644 index 000000000..eaf4d8447 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupDimensions.lean @@ -0,0 +1,50 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillSetup +import VerifiedGarbage.Proof.Argon2.X86_64.DivideStep +import VerifiedGarbage.Proof.Argon2.X86_64.Initialize +import VerifiedGarbage.Proof.Argon2.Dimensions + +/-! Initialization's byte stride gives exact block and segment counts without division instructions. -/ + +namespace VG.Proof.Argon2.X86_64.FillSetup + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem dimensions_ok (s : State) : WP isa (.block Impl.Argon2.X86_64.FillSetup.dimensions) s fun t => + t.gpr .r12 = s.gpr .r13 >>> 10 ∧ t.gpr .r13 = s.gpr .r13 >>> 12 ∧ Divide.Keeps [.r12, .r13] s t := by + apply WP.of_runBlock + simp only [Impl.Argon2.X86_64.FillSetup.dimensions, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + execShift, RegUpd.gpr_setReg, RegUpd.gpr_setFlags, + show 1 ≤ (10 : Nat) ∧ (10 : Nat) ≤ 63 from by decide, + show 1 ≤ (12 : Nat) ∧ (12 : Nat) ≤ 63 from by decide, + and_self, reduceCtorEq, ite_true, ite_false, + Option.map_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_setFlags, hr.1, hr.2, ite_false] + all_goals rfl + +theorem stride_shift (q shift : Nat) (bound : 1024 * q < 2 ^ 64) : + BitVec.ofNat 64 (1024 * q) >>> shift = BitVec.ofNat 64 ((1024 * q) / 2 ^ shift) := by + apply BitVec.eq_of_toNat_eq + rw [BitVec.toNat_ushiftRight, BitVec.toNat_ofNat, Nat.mod_eq_of_lt bound, Nat.shiftRight_eq_div_pow, + BitVec.toNat_ofNat, Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt (Nat.div_le_self _ _) bound)] + +theorem dimensions_nat_ok (s : State) (p : Params) (bound : 1024 * p.laneLen < 2 ^ 64) + (stride : s.gpr .r13 = BitVec.ofNat 64 (1024 * p.laneLen)) : + WP isa (.block Impl.Argon2.X86_64.FillSetup.dimensions) s fun t => + t.gpr .r12 = BitVec.ofNat 64 p.laneLen ∧ t.gpr .r13 = BitVec.ofNat 64 p.segmentLen ∧ + Divide.Keeps [.r12, .r13] s t := by + refine (dimensions_ok s).mono ?_ + rintro t ⟨lane, segment, keeps⟩ + refine ⟨?_, ?_, keeps⟩ + · rw [lane, stride, stride_shift _ 10 bound] + simp only [show 2 ^ 10 = 1024 from rfl, Nat.mul_div_cancel_left _ (by decide : 0 < 1024)] + · rw [segment, stride, stride_shift _ 12 bound] + have div : 1024 * p.laneLen / 4096 = p.laneLen / 4 := by + rw [show (4096 : Nat) = 1024 * 4 from rfl, Nat.mul_div_mul_left _ _ (by decide : 0 < 1024)] + rw [show 2 ^ 12 = 4096 from rfl, div] + rfl + +end VG.Proof.Argon2.X86_64.FillSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupEnvironment.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupEnvironment.lean new file mode 100644 index 000000000..fc5c02822 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupEnvironment.lean @@ -0,0 +1,71 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSetupReset +import VerifiedGarbage.Proof.Argon2.X86_64.FillIterationsBody + +/-! Initialization hands filling the reviewed dimensions and stable public frame words. -/ + +namespace VG.Proof.Argon2.X86_64.FillSetup + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Environment (p : Params) (s : State) : Prop where + parameters : FillContext.Parameters p 0 0 0 + passesBound : p.passes < 2 ^ 32 + layout : FillKernel.Layout p s + addressLayout : AddressCalls.Ready s + reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8 + counterWrite : InRegions s.wr (off (s.gpr .rbp) 8) 8 + passWrite : InRegions s.wr (off (s.gpr .rbp) 0) 8 + matrixWork : (⟨FillKernel.matrix s, p.blocks * 1024⟩ : Region).Disjoint ⟨AddressCalls.work s, 8192⟩ + blocksWord : s.mem.readW (off (s.gpr .rbp) 240) 64 = BitVec.ofNat 64 p.blocks + passesWord : s.mem.readW (off (s.gpr .rbp) 72) 64 = BitVec.ofNat 64 p.passes + variantWord : s.mem.readW (off (s.gpr .rbp) 112) 64 = BitVec.ofNat 64 p.variant.code + lanesWord : s.mem.readW (off (s.gpr .rbp) 184) 64 = BitVec.ofNat 64 p.lanes + +theorem Environment.of_state {p : Params} {s t : State} (h : Environment p s) + (bp : t.gpr .rbp = s.gpr .rbp) (sp : t.gpr .rsp = s.gpr .rsp) + (mem : t.mem = s.mem) (rd : t.rd = s.rd) (wr : t.wr = s.wr) : Environment p t := by + have base : FillKernel.matrix t = FillKernel.matrix s := by unfold FillKernel.matrix; rw [mem, bp] + have work : AddressCalls.work t = AddressCalls.work s := by unfold AddressCalls.work; rw [mem, bp] + refine ⟨h.parameters, h.passesBound, h.layout.of_preserved bp sp base work rd wr, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · constructor + · rw [rd, wr, bp]; exact h.addressLayout.frameRead + · rw [wr, work]; exact h.addressLayout.workWrite + · rw [bp, work]; exact h.addressLayout.frameWork + · rw [bp, sp]; exact h.addressLayout.frameStack + · rw [sp, work]; exact h.addressLayout.stackWork + · rw [rd, wr, bp]; exact h.reads + · rw [wr, bp]; exact h.counterWrite + · rw [wr, bp]; exact h.passWrite + · rw [base, work]; exact h.matrixWork + all_goals rw [mem, bp] + · exact h.blocksWord + · exact h.passesWord + · exact h.variantWord + · exact h.lanesWord + +theorem Reset.header {p : Params} {s t : State} (h : Environment p s) (reset : Reset s t) + (laneLength : t.gpr .r12 = BitVec.ofNat 64 p.laneLen) + (segmentLength : t.gpr .r13 = BitVec.ofNat 64 p.segmentLen) : FillHeader.Ready p 0 0 0 t := by + have bp := reset.regs .rbp (by decide) + have sp := reset.regs .rsp (by decide) + have base : FillKernel.matrix t = FillKernel.matrix s := reset.read 232 (by decide) (by decide) + have work : AddressCalls.work t = AddressCalls.work s := reset.read 248 (by decide) (by decide) + refine ⟨h.layout.of_preserved bp sp base work reset.rd reset.wr, ?_, ?_, ?_, ?_, ?_, laneLength, + segmentLength, (reset.read 184 (by decide) (by decide)).trans h.lanesWord⟩ + · constructor + · rw [reset.rd, reset.wr, bp]; exact h.addressLayout.frameRead + · rw [reset.wr, work]; exact h.addressLayout.workWrite + · rw [bp, work]; exact h.addressLayout.frameWork + · rw [bp, sp]; exact h.addressLayout.frameStack + · rw [sp, work]; exact h.addressLayout.stackWork + · rw [reset.rd, reset.wr, bp]; exact h.reads + · rw [reset.wr, bp]; exact h.counterWrite + · refine ⟨(s.mem.readW (off (s.gpr .rbp) 8) 64).toNat, reset.pass, reset.lane, reset.slice, + (reset.read 240 (by decide) (by decide)).trans h.blocksWord, + (reset.read 72 (by decide) (by decide)).trans h.passesWord, + (reset.read 112 (by decide) (by decide)).trans h.variantWord, ?_⟩ + rw [reset.read 8 (by decide) (by decide)] + simp only [BitVec.ofNat_toNat, BitVec.setWidth_eq] + · rw [base, work]; exact h.matrixWork + +end VG.Proof.Argon2.X86_64.FillSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupFinish.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupFinish.lean new file mode 100644 index 000000000..aa0eb418a --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupFinish.lean @@ -0,0 +1,38 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSetup +import VerifiedGarbage.Proof.Argon2.X86_64.FillFinish + +/-! Filling setup retains the final-call layout and establishes the reduction dimensions. -/ + +namespace VG.Proof.Argon2.X86_64.FillSetup + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem Prepared.finish_ready {s t : State} {p : Params} (ready : Ready p s) (done : Prepared s t p) + (outputReady : FinalOutput.Ready p s) (positive : 0 < p.passes) : FillFinish.Ready p t := by + have bp := done.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide) (by decide) + have sp := done.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide) (by decide) + have base : ReductionState.matrix t = ReductionState.matrix s := done.matrix + have output : FinalOutput.output t = FinalOutput.output s := done.words 256 (by decide) (by decide) + have work : FinalOutput.work t = FinalOutput.work s := done.words 248 (by decide) (by decide) + obtain ⟨lane, slice, header⟩ := done.ready.filling.header + have params := ready.environment.parameters + refine ⟨done.ready, ⟨?_, ?_⟩, positive⟩ + · refine ⟨⟨params.lanesPositive, params.segment_bound.1, ?_, header.layout.frameRead 232 (by simp), + header.layout.matrixWrite, header.layout.matrixFrame, header.laneLength⟩, + params.lanesBound, header.layout.frameRead 184 (by simp), header.lanesWord⟩ + have blocks := Proof.Argon2.blocks_le_memory p + have memory := params.memoryBound + omega + · refine ⟨outputReady.positive, outputReady.bound, ?_, + (done.words 264 (by decide) (by decide)).trans outputReady.tagWord, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · rw [done.rd, done.wr, bp]; exact outputReady.reads + · rw [base, done.rd, done.wr]; exact outputReady.input + · rw [output, done.wr]; exact outputReady.outputWrite + · rw [work, done.wr]; exact outputReady.workWrite + · rw [base, work]; exact outputReady.inputWork + · rw [output, work]; exact outputReady.outputWork + · rw [sp, base]; exact outputReady.stackInput + · rw [sp, output]; exact outputReady.stackOutput + · rw [sp, work]; exact outputReady.stackWork + +end VG.Proof.Argon2.X86_64.FillSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupReset.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupReset.lean new file mode 100644 index 000000000..1d1fd9870 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSetupReset.lean @@ -0,0 +1,45 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSetupDimensions +import VerifiedGarbage.Proof.Framework.Mem + +/-! Reset public loop coordinates and only the pass word in the enclosing frame. -/ + +namespace VG.Proof.Argon2.X86_64.FillSetup + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Reset (s t : State) : Prop where + mem : t.mem = s.mem.writeW (off (s.gpr .rbp) 0) (0 : Addr) + lane : t.gpr .rbx = 0 + slice : t.gpr .r14 = 0 + regs : ∀ r, r ∉ [Reg.rax, .rbx, .r14] → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + +theorem reset_ok (s : State) (write : InRegions s.wr (off (s.gpr .rbp) 0) 8) : + WP isa (.block Impl.Argon2.X86_64.FillSetup.reset) s (Reset s) := by + apply WP.of_runBlock + simp only [Impl.Argon2.X86_64.FillSetup.reset, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + State.store64, ea_at, RegUpd.gpr_setReg, RegUpd.mem_setReg, RegUpd.rd_setReg, RegUpd.wr_setReg, + write, reduceCtorEq, ite_true, ite_false, Option.map_some, Option.some.injEq, exists_eq_left'] + refine ⟨rfl, rfl, rfl, ?_, rfl, rfl, rfl⟩ + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, hr.1, hr.2.1, hr.2.2, ite_false] + +theorem Reset.pass {s t : State} (h : Reset s t) : t.mem.readW (off (t.gpr .rbp) 0) 64 = 0 := by + rw [h.mem, h.regs .rbp (by decide)] + exact Mem.readW_writeW_self64 _ _ _ + +theorem Reset.frame {s t : State} (h : Reset s t) : Frame [⟨s.gpr .rbp, 8⟩] s.mem t.mem := by + rw [h.mem] + exact (Frame.refl _ _).writeW (r := ⟨s.gpr .rbp, 8⟩) (by simp) _ + (by simpa only [off, BitVec.add_zero] using Region.contains_self (s.gpr .rbp) 8) + +theorem Reset.read {s t : State} (h : Reset s t) (d : Nat) (separate : 8 ≤ d) (bound : d + 8 ≤ 272) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [h.mem, h.regs .rbp (by decide)] + exact Mem.readW_writeW_sep (w := 64) (w' := 64) + (Offset.sep _ (d := d) (n := 8) (e := 0) (k := 8) (Or.inr (by omega)) (by omega) (by decide)) (by decide) + +end VG.Proof.Argon2.X86_64.FillSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlice.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlice.lean new file mode 100644 index 000000000..d319837c1 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlice.lean @@ -0,0 +1,34 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSlicePrepare + +/-! Correctness of one complete slice, starting at lane zero. -/ + +namespace VG.Proof.Argon2.X86_64.FillSlice + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem code_ok (s : State) (p : Params) (pass slice : Nat) (h : Ready p pass slice s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) : + WP isa Impl.Argon2.X86_64.FillSlice.code s + (FillLanes.Finished s · p pass slice (Proof.Argon2.lanes p pass slice 0 p.lanes state)) := by + unfold Impl.Argon2.X86_64.FillSlice.code + refine WP.seq ((setup_ok s p pass slice h).mono ?_) + intro a prepared + have bp := prepared.keeps.regs .rbp (by decide) + have base : FillKernel.matrix a = FillKernel.matrix s := by unfold FillKernel.matrix; rw [prepared.keeps.mem, bp] + have work : AddressCalls.work a = AddressCalls.work s := by unfold AddressCalls.work; rw [prepared.keeps.mem, bp] + have representedA : Proof.Argon2.Represents a.mem (FillKernel.matrix a) p.blocks state.memory := by + rw [prepared.keeps.mem, base]; exact represented + refine (FillLanes.loop_ok p.lanes a p pass 0 slice prepared.ready state representedA + h.parameters.lanesPositive (by omega)).mono ?_ + intro t finished + refine ⟨finished.represented, finished.matrix.trans base, finished.work.trans work, finished.laneWord, + finished.rd.trans prepared.keeps.rd, finished.wr.trans prepared.keeps.wr, ?_, + finished.mxcsr.trans prepared.keeps.mxcsr, ?_, finished.header⟩ + · have frame := finished.frame + rw [FillBlock.writes, base, work, prepared.keeps.regs .rsp (by decide), bp, prepared.keeps.mem] at frame + exact frame + · intro r hr bx ix + have ne : r ∉ [Reg.rbx] := by simp only [List.mem_cons, List.not_mem_nil, or_false]; exact bx + exact (finished.regs r hr bx ix).trans (prepared.keeps.regs r ne) + +end VG.Proof.Argon2.X86_64.FillSlice diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSliceAdvance.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSliceAdvance.lean new file mode 100644 index 000000000..ac3331a18 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSliceAdvance.lean @@ -0,0 +1,36 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillSlices +import VerifiedGarbage.Proof.Argon2.X86_64.FillSlice + +/-! Slice advancement retains the public header and matrix allocation. -/ + +namespace VG.Proof.Argon2.X86_64.FillSlices + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillSlices + +theorem advance_ok (s : State) : WP isa (.block advance) s fun t => + t.gpr .r14 = s.gpr .r14 + 1 ∧ t.cf = decide ((s.gpr .r14 + 1).toNat < 4) ∧ Divide.Keeps [.r14] s t := by + apply WP.of_runBlock + simp only [advance, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, execAlu, + RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, RegUpd.cf_arithFlags, + show BitVec.signExtend 64 (1 : BitVec 32) = (1 : Addr) from rfl, + show BitVec.signExtend 64 (4 : BitVec 32) = (4 : Addr) from rfl, + show (4 : Addr).toNat = 4 from rfl, + ite_true, Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr, ite_false] + all_goals rfl + +theorem advanced_header {s t : State} {p : Params} {pass lane slice : Nat} + (h : FillHeader.Ready p pass lane slice s) (k : Divide.Keeps [.r14] s t) + (value : t.gpr .r14 = BitVec.ofNat 64 (slice + 1)) : FillHeader.Ready p pass lane (slice + 1) t := by + obtain ⟨old, words⟩ := h.words + exact h.of_state (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl <;> exact k.regs _ (by decide)) + k.mem k.rd k.wr ((k.regs .rbx (by decide)).trans words.laneWord) value + +end VG.Proof.Argon2.X86_64.FillSlices diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSliceCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSliceCT.lean new file mode 100644 index 000000000..a6e651118 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSliceCT.lean @@ -0,0 +1,59 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSlice +import VerifiedGarbage.Proof.Argon2.X86_64.FillLanesCT + +/-! A complete slice exposes only its specified reference log. -/ + +namespace VG.Proof.Argon2.X86_64.FillSlice + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Related (p : Params) (pass slice : Nat) (leftState rightState : FillState) (s t : State) : Prop where + left : Ready p pass slice s + right : Ready p pass slice t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + matrices : FillKernel.matrix s = FillKernel.matrix t + work : AddressCalls.work s = AddressCalls.work t + leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory + indices : (Proof.Argon2.lanes p pass slice 0 p.lanes leftState).indices = + (Proof.Argon2.lanes p pass slice 0 p.lanes rightState).indices + +theorem setup_trace : RelCT isa (fun _ _ : State => True) (.block Impl.Argon2.X86_64.FillSlice.setup) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by intro r hr; simp at hr)) (by taint_decide) + +theorem setup_public_rel (p : Params) (pass slice : Nat) (leftState rightState : FillState) : + RelCT isa (Related p pass slice leftState rightState) (.block Impl.Argon2.X86_64.FillSlice.setup) + (FillLanes.Related p pass 0 slice p.lanes leftState rightState) := by + have trace := setup_trace.mono (P' := Related p pass slice leftState rightState) + (fun _ _ _ => trivial) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => ⟨setup_ok s p pass slice h.left, setup_ok t p pass slice h.right⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + refine ⟨⟨ha.ready, hb.ready, ?_, ?_, ?_, ?_⟩, ?_, ?_, hp.indices⟩ + · rw [ha.keeps.regs .rbp (by decide), hb.keeps.regs .rbp (by decide)]; exact hp.bases + · rw [ha.keeps.regs .rsp (by decide), hb.keeps.regs .rsp (by decide)]; exact hp.stacks + · unfold FillKernel.matrix + rw [ha.keeps.mem, hb.keeps.mem, ha.keeps.regs .rbp (by decide), hb.keeps.regs .rbp (by decide)] + exact hp.matrices + · unfold AddressCalls.work + rw [ha.keeps.mem, hb.keeps.mem, ha.keeps.regs .rbp (by decide), hb.keeps.regs .rbp (by decide)] + exact hp.work + · unfold FillKernel.matrix; rw [ha.keeps.mem, ha.keeps.regs .rbp (by decide)]; exact hp.leftMatrix + · unfold FillKernel.matrix; rw [hb.keeps.mem, hb.keeps.regs .rbp (by decide)]; exact hp.rightMatrix + +theorem code_rel (p : Params) (pass slice : Nat) (leftState rightState : FillState) : + RelCT isa (Related p pass slice leftState rightState) Impl.Argon2.X86_64.FillSlice.code (fun _ _ => True) := by + intro s t ts tt a b hp ea eb + cases ea with + | seq setupA lanesA => + cases eb with + | seq setupB lanesB => + obtain ⟨setupTrace, related⟩ := setup_public_rel p pass slice leftState rightState _ _ _ _ _ _ hp setupA setupB + obtain ⟨lanesTrace, _⟩ := FillLanes.loop_rel p pass 0 slice p.lanes leftState rightState + hp.left.parameters.lanesPositive (by omega) _ _ _ _ _ _ related lanesA lanesB + exact ⟨by rw [setupTrace, lanesTrace], trivial⟩ + +end VG.Proof.Argon2.X86_64.FillSlice diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicePrepare.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicePrepare.lean new file mode 100644 index 000000000..50b555c29 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicePrepare.lean @@ -0,0 +1,31 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillSlice +import VerifiedGarbage.Proof.Argon2.X86_64.FillHeader + +/-! Initialize lane zero without requiring a valid incoming lane coordinate. -/ + +namespace VG.Proof.Argon2.X86_64.FillSlice + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Ready (p : Params) (pass slice : Nat) (s : State) : Prop where + parameters : FillContext.Parameters p pass 0 slice + header : ∃ lane, FillHeader.Ready p pass lane slice s + +structure Prepared (s t : State) (p : Params) (pass slice : Nat) : Prop where + ready : SegmentSetup.Ready p pass 0 slice t + keeps : Divide.Keeps [.rbx] s t + +theorem setup_ok (s : State) (p : Params) (pass slice : Nat) (h : Ready p pass slice s) : + WP isa (.block Impl.Argon2.X86_64.FillSlice.setup) s (Prepared s · p pass slice) := by + refine (SegmentSetup.register_ok s .rbx 0).mono ?_ + rintro t ⟨laneWord, keeps⟩ + obtain ⟨lane, header⟩ := h.header + obtain ⟨old, words⟩ := header.words + have next : FillHeader.Ready p pass 0 slice t := header.of_state (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl <;> exact keeps.regs _ (by decide)) + keeps.mem keeps.rd keeps.wr laneWord ((keeps.regs .r14 (by decide)).trans words.sliceWord) + exact ⟨next.segment h.parameters, keeps⟩ + +end VG.Proof.Argon2.X86_64.FillSlice diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlices.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlices.lean new file mode 100644 index 000000000..87f40a5b6 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlices.lean @@ -0,0 +1,65 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSlicesBody +import VerifiedGarbage.Proof.Argon2.Slices + +/-! Termination and correctness of the four-slice filling pass. -/ + +namespace VG.Proof.Argon2.X86_64.FillSlices + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Finished (s t : State) (p : Params) (pass : Nat) (state : FillState) : Prop where + represented : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks state.memory + matrix : FillKernel.matrix t = FillKernel.matrix s + work : AddressCalls.work t = AddressCalls.work s + header : FillHeader.Ready p pass p.lanes 4 t + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (FillBlock.writes s p) s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + regs : ∀ r ∈ calleeSaved, r ≠ .rbx → r ≠ .r14 → r ≠ .r15 → t.gpr r = s.gpr r + +theorem Done.finished {s t : State} {p : Params} {pass slice : Nat} {state : FillState} + (h : Done s t p pass slice state) (last : slice + 1 = 4) : + Finished s t p pass (Proof.Argon2.lanes p pass slice 0 p.lanes state) := + ⟨h.represented, h.matrix, h.work, last ▸ h.header, h.rd, h.wr, h.frame, h.mxcsr, h.regs⟩ + +theorem Finished.prepend {s a t : State} {p : Params} {pass slice : Nat} {state finalState : FillState} + (first : Done s a p pass slice state) (rest : Finished a t p pass finalState) : Finished s t p pass finalState := by + refine ⟨rest.represented, rest.matrix.trans first.matrix, rest.work.trans first.work, rest.header, + rest.rd.trans first.rd, rest.wr.trans first.wr, ?_, rest.mxcsr.trans first.mxcsr, ?_⟩ + · have frame := rest.frame + rw [FillBlock.writes, first.matrix, first.work, + first.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide), + first.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)] at frame + exact first.frame.trans frame + · intro r hr bx sl ix; exact (rest.regs r hr bx sl ix).trans (first.regs r hr bx sl ix) + +theorem loop_ok (count : Nat) (s : State) (p : Params) (pass slice : Nat) + (h : FillSlice.Ready p pass slice s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) + (positive : 0 < count) (endSlice : slice + count = 4) : + WP isa Impl.Argon2.X86_64.FillSlices.loop s (Finished s · p pass (Proof.Argon2.slices p pass slice count state)) := by + induction count generalizing s slice state with + | zero => omega + | succ n ih => + obtain ⟨trace, a, run, done⟩ := body_ok s p pass slice h state represented + rw [Proof.Argon2.slices_succ] + cases n with + | zero => + have last : slice + 1 = 4 := endSlice + refine ⟨_, a, .loopExit run ?_, done.finished last⟩ + simp only [eval, done.cf, last, Nat.lt_irrefl, decide_false] + | succ n => + have active : slice + 1 < 4 := by omega + obtain ⟨restTrace, t, restRun, finished⟩ := ih a (slice + 1) (done.next active) + (Proof.Argon2.lanes p pass slice 0 p.lanes state) done.represented (by omega) (by omega) + refine ⟨_, t, .loopNext run ?_ restRun, finished.prepend done⟩ + simp only [eval, done.cf, active, decide_true] + +theorem pass_ok (s : State) (p : Params) (pass : Nat) (h : FillSlice.Ready p pass 0 s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) : + WP isa Impl.Argon2.X86_64.FillSlices.loop s (Finished s · p pass (fillPass p state pass)) := by + rw [← Proof.Argon2.slices_pass p pass state] + exact loop_ok 4 s p pass 0 h state represented (by decide) (by decide) + +end VG.Proof.Argon2.X86_64.FillSlices diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicesBody.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicesBody.lean new file mode 100644 index 000000000..0c65e938c --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicesBody.lean @@ -0,0 +1,52 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSliceAdvance + +/-! Fill one slice and advance its public coordinate. -/ + +namespace VG.Proof.Argon2.X86_64.FillSlices + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Done (s t : State) (p : Params) (pass slice : Nat) (state : FillState) : Prop where + represented : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks + (Proof.Argon2.lanes p pass slice 0 p.lanes state).memory + matrix : FillKernel.matrix t = FillKernel.matrix s + work : AddressCalls.work t = AddressCalls.work s + header : FillHeader.Ready p pass p.lanes (slice + 1) t + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (FillBlock.writes s p) s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + regs : ∀ r ∈ calleeSaved, r ≠ .rbx → r ≠ .r14 → r ≠ .r15 → t.gpr r = s.gpr r + cf : t.cf = decide (slice + 1 < 4) + next : slice + 1 < 4 → FillSlice.Ready p pass (slice + 1) t + +theorem body_ok (s : State) (p : Params) (pass slice : Nat) (h : FillSlice.Ready p pass slice s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) : + WP isa Impl.Argon2.X86_64.FillSlices.body s (Done s · p pass slice state) := by + unfold Impl.Argon2.X86_64.FillSlices.body + refine WP.seq ((FillSlice.code_ok s p pass slice h state represented).mono ?_) + intro a filled + have header := FillHeader.of_lanes_finished filled + obtain ⟨old, words⟩ := header.words + refine (advance_ok a).mono ?_ + rintro t ⟨value, flag, keeps⟩ + have added : a.gpr .r14 + 1 = BitVec.ofNat 64 (slice + 1) := by + rw [words.sliceWord, BitVec.ofNat_add]; rfl + have nextWord := value.trans added + have nextHeader := advanced_header header keeps nextWord + have bp := keeps.regs .rbp (by decide) + have base : FillKernel.matrix t = FillKernel.matrix a := by unfold FillKernel.matrix; rw [keeps.mem, bp] + have work : AddressCalls.work t = AddressCalls.work a := by unfold AddressCalls.work; rw [keeps.mem, bp] + have sliceBound := h.parameters.sliceBound + refine ⟨?_, base.trans filled.matrix, work.trans filled.work, nextHeader, keeps.rd.trans filled.rd, + keeps.wr.trans filled.wr, ?_, keeps.mxcsr.trans filled.mxcsr, ?_, ?_, ?_⟩ + · rw [keeps.mem, base]; exact filled.represented + · rw [keeps.mem]; exact filled.frame + · intro r hr bx sl ix + have outside : r ∉ [Reg.r14] := by simp only [List.mem_cons, List.not_mem_nil, or_false]; exact sl + exact (keeps.regs r outside).trans (filled.regs r hr bx ix) + · rw [flag, added, ReferenceMap.word_nat (slice + 1) (by omega)] + · intro active + exact ⟨{ h.parameters with sliceBound := active }, p.lanes, nextHeader⟩ + +end VG.Proof.Argon2.X86_64.FillSlices diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicesBodyCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicesBodyCT.lean new file mode 100644 index 000000000..3dcb93464 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicesBodyCT.lean @@ -0,0 +1,49 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSlicesBody +import VerifiedGarbage.Proof.Argon2.X86_64.FillSliceCT + +/-! A slice iteration preserves public allocations and its public continuation guard. -/ + +namespace VG.Proof.Argon2.X86_64.FillSlices + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillSlices + +theorem advance_rel : RelCT isa (fun _ _ : State => True) (.block advance) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by intro r hr; simp at hr)) (by taint_decide) + +structure NextRelated (p : Params) (pass slice : Nat) (leftState rightState : FillState) (s t : State) : Prop where + left : FillSlice.Ready p pass slice s + right : FillSlice.Ready p pass slice t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + matrices : FillKernel.matrix s = FillKernel.matrix t + work : AddressCalls.work s = AddressCalls.work t + leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory + +theorem body_rel (p : Params) (pass slice : Nat) (leftState rightState : FillState) : + RelCT isa (FillSlice.Related p pass slice leftState rightState) body + (fun s t => s.cf = t.cf ∧ (slice + 1 < 4 → NextRelated p pass (slice + 1) + (Proof.Argon2.lanes p pass slice 0 p.lanes leftState) (Proof.Argon2.lanes p pass slice 0 p.lanes rightState) s t)) := by + intro s t ts tt a b hp ea eb + cases ea with + | seq sliceA advanceA => + cases eb with + | seq sliceB advanceB => + obtain ⟨sliceTrace, _⟩ := FillSlice.code_rel p pass slice leftState rightState _ _ _ _ _ _ hp sliceA sliceB + obtain ⟨advanceTrace, _⟩ := advance_rel _ _ _ _ _ _ trivial advanceA advanceB + obtain ⟨_, a', runA, doneA⟩ := body_ok s p pass slice hp.left leftState hp.leftMatrix + obtain ⟨_, b', runB, doneB⟩ := body_ok t p pass slice hp.right rightState hp.rightMatrix + obtain ⟨_, rfl⟩ := Exec.det (.seq sliceA advanceA) runA + obtain ⟨_, rfl⟩ := Exec.det (.seq sliceB advanceB) runB + refine ⟨by rw [sliceTrace, advanceTrace], doneA.cf.trans doneB.cf.symm, ?_⟩ + intro active + refine ⟨doneA.next active, doneB.next active, ?_, ?_, + doneA.matrix.trans (hp.matrices.trans doneB.matrix.symm), + doneA.work.trans (hp.work.trans doneB.work.symm), doneA.represented, doneB.represented⟩ + · exact (doneA.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).trans + (hp.bases.trans (doneB.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).symm) + · exact (doneA.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).trans + (hp.stacks.trans (doneB.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).symm) + +end VG.Proof.Argon2.X86_64.FillSlices diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicesCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicesCT.lean new file mode 100644 index 000000000..78da1ac47 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillSlicesCT.lean @@ -0,0 +1,59 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillSlices +import VerifiedGarbage.Proof.Argon2.X86_64.FillSlicesBodyCT +import VerifiedGarbage.Proof.Argon2.SlicesIndices + +/-! The complete pass leaks only its reviewed reference log, including Argon2id's mode change. -/ + +namespace VG.Proof.Argon2.X86_64.FillSlices + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Related (p : Params) (pass slice count : Nat) (leftState rightState : FillState) (s t : State) : Prop where + states : NextRelated p pass slice leftState rightState s t + indices : (Proof.Argon2.slices p pass slice count leftState).indices = + (Proof.Argon2.slices p pass slice count rightState).indices + +theorem loop_rel (p : Params) (pass slice count : Nat) (leftState rightState : FillState) + (positive : 0 < count) (endSlice : slice + count = 4) : + RelCT isa (Related p pass slice count leftState rightState) Impl.Argon2.X86_64.FillSlices.loop (fun _ _ => True) := by + let I := fun n s t => ∃ (slice : Nat) (leftState rightState : FillState), + slice + n = 4 ∧ 0 < n ∧ Related p pass slice n leftState rightState s t + have steps : ∀ n, RelCT isa (I n) Impl.Argon2.X86_64.FillSlices.body fun s t => + isa.eval .b s = isa.eval .b t ∧ (isa.eval .b s = some false → True) ∧ + (isa.eval .b s = some true → ∃ m < n, I m s t) := by + intro n s t ts tt a b hp ea eb + obtain ⟨j, ls, rs, endSlice, positive, hp⟩ := hp + cases n with + | zero => omega + | succ n => + have sliceRelated : FillSlice.Related p pass j ls rs s t := + ⟨hp.states.left, hp.states.right, hp.states.bases, hp.states.stacks, hp.states.matrices, hp.states.work, + hp.states.leftMatrix, hp.states.rightMatrix, Proof.Argon2.slices_first_lane_fold p pass j n ls rs + hp.states.left.parameters.segment_bound.1 hp.indices⟩ + obtain ⟨trace, flags, next⟩ := body_rel p pass j ls rs _ _ _ _ _ _ sliceRelated ea eb + obtain ⟨_, a', runA, done⟩ := body_ok s p pass j hp.states.left ls hp.states.leftMatrix + obtain ⟨_, rfl⟩ := Exec.det ea runA + refine ⟨trace, ?_, fun _ => trivial, ?_⟩ + · simp only [eval, flags] + · intro taken + have active : j + 1 < 4 := by + simp only [eval, done.cf, Option.some.injEq, decide_eq_true_eq] at taken + exact taken + have indices := hp.indices + rw [Proof.Argon2.slices_succ, Proof.Argon2.slices_succ] at indices + exact ⟨n, by omega, j + 1, Proof.Argon2.lanes p pass j 0 p.lanes ls, + Proof.Argon2.lanes p pass j 0 p.lanes rs, by omega, by omega, next active, indices⟩ + exact (RelCT.loop I steps count).mono + (fun _ _ h => ⟨slice, leftState, rightState, endSlice, positive, h⟩) (fun _ _ h => h) + +theorem pass_rel (p : Params) (pass : Nat) (leftState rightState : FillState) : + RelCT isa (fun s t => NextRelated p pass 0 leftState rightState s t ∧ + (fillPass p leftState pass).indices = (fillPass p rightState pass).indices) + Impl.Argon2.X86_64.FillSlices.loop (fun _ _ => True) := by + refine (loop_rel p pass 0 4 leftState rightState (by decide) (by decide)).mono ?_ (fun _ _ h => h) + intro s t h + refine ⟨h.1, ?_⟩ + rw [Proof.Argon2.slices_pass p pass leftState, Proof.Argon2.slices_pass p pass rightState] + exact h.2 + +end VG.Proof.Argon2.X86_64.FillSlices diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWrite.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWrite.lean new file mode 100644 index 000000000..ba86b63da --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWrite.lean @@ -0,0 +1,52 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillWritePrefix +import VerifiedGarbage.Proof.Argon2.X86_64.CountCandidates + +/-! Whole-block first-pass copying and later-pass XOR, with a frame proof. -/ + +namespace VG.Proof.Argon2.X86_64.FillWrite + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillWrite + +theorem code_ok (s : State) + (hs : (⟨s.gpr .rsi, 1024⟩ : Region) ∈ s.rd ++ s.wr) + (hw : (⟨s.gpr .rdi, 1024⟩ : Region) ∈ s.wr) + (hd : (⟨s.gpr .rsi, 1024⟩ : Region).Disjoint ⟨s.gpr .rdi, 1024⟩) : + WP isa code s fun t => + blockAt t.mem (s.gpr .rdi) = + (if s.gpr .r9 = 0 then blockAt s.mem (s.gpr .rsi) + else xorBlock (blockAt s.mem (s.gpr .rsi)) (blockAt s.mem (s.gpr .rdi))) ∧ + Frame [⟨s.gpr .rdi, 1024⟩] s.mem t.mem ∧ CopyKeeps s t ∧ t.mxcsr = s.mxcsr := by + unfold code + refine WP.seq ((CountCandidates.compare_ok s).mono ?_) + rintro a ⟨flag, ka⟩ + have src : a.gpr .rsi = s.gpr .rsi := ka.regs .rsi (by simp) + have dest : a.gpr .rdi = s.gpr .rdi := ka.regs .rdi (by simp) + have hs' : (⟨a.gpr .rsi, 1024⟩ : Region) ∈ a.rd ++ a.wr := by + rw [src, ka.rd, ka.wr]; exact hs + have hw' : (⟨a.gpr .rdi, 1024⟩ : Region) ∈ a.wr := by + rw [dest, ka.wr]; exact hw + have hd' : (⟨a.gpr .rsi, 1024⟩ : Region).Disjoint ⟨a.gpr .rdi, 1024⟩ := by + rw [src, dest]; exact hd + refine WP.ite (decide (s.gpr .r9 = 0)) (by simp only [eval, flag]) ?_ ?_ + · intro h + have zero := of_decide_eq_true h + refine (prefix_ok false 128 (by decide) a hs' hw' hd').mono ?_ + rintro t ⟨written, frame, keeps, mx⟩ + refine ⟨?_, ?_, ?_, mx.trans ka.mxcsr⟩ + · rw [dest] at written + rw [ite_eq_left zero, written_block written] + simp only [result, Bool.false_eq_true, ite_false, ka.mem, src] + · rw [dest, ka.mem] at frame; exact frame + · exact (show CopyKeeps s a from ⟨fun r _ => ka.regs r (by simp), ka.rd, ka.wr⟩).trans keeps + · intro h + have nonzero := of_decide_eq_false h + refine (prefix_ok true 128 (by decide) a hs' hw' hd').mono ?_ + rintro t ⟨written, frame, keeps, mx⟩ + refine ⟨?_, ?_, ?_, mx.trans ka.mxcsr⟩ + · rw [dest] at written + rw [ite_eq_right nonzero, written_block written] + simp only [result, ite_true, ka.mem, src] + · rw [dest, ka.mem] at frame; exact frame + · exact (show CopyKeeps s a from ⟨fun r _ => ka.regs r (by simp), ka.rd, ka.wr⟩).trans keeps + +end VG.Proof.Argon2.X86_64.FillWrite diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteCT.lean new file mode 100644 index 000000000..0f85f3962 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteCT.lean @@ -0,0 +1,16 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillWriteLit +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! Both write paths have a public, fixed sequence of memory accesses. -/ + +namespace VG.Proof.Argon2.X86_64.FillWrite + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FillWrite + +theorem code_rel : RelCT isa + (fun s t => ∀ r ∈ [Reg.r9, .rdi, .rsi], s.gpr r = t.gpr r) code + (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.r9, .rdi, .rsi]) + (fun _ _ h => Taint.agree_ofRegs h) (by taint_decide) + +end VG.Proof.Argon2.X86_64.FillWrite diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteCover.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteCover.lean new file mode 100644 index 000000000..2da771373 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteCover.lean @@ -0,0 +1,34 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillWrite +import VerifiedGarbage.Proof.Framework.X86_64.Inline + +/-! Use block writes in a matrix allocation with larger permission regions. -/ + +namespace VG.Proof.Argon2.X86_64.FillWrite + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillWrite + +theorem code_cover_ok (s : State) + (hs : Covers [⟨s.gpr .rsi, 1024⟩] (s.rd ++ s.wr)) + (hw : Covers [⟨s.gpr .rdi, 1024⟩] s.wr) + (hd : (⟨s.gpr .rsi, 1024⟩ : Region).Disjoint ⟨s.gpr .rdi, 1024⟩) : + WP isa code s fun t => + blockAt t.mem (s.gpr .rdi) = + (if s.gpr .r9 = 0 then blockAt s.mem (s.gpr .rsi) + else xorBlock (blockAt s.mem (s.gpr .rsi)) (blockAt s.mem (s.gpr .rdi))) ∧ + Frame [⟨s.gpr .rdi, 1024⟩] s.mem t.mem ∧ CopyKeeps s t ∧ t.mxcsr = s.mxcsr := by + let a := s.withRegions [⟨s.gpr .rsi, 1024⟩] [⟨s.gpr .rdi, 1024⟩] + obtain ⟨tr, t, he, value, frame, keeps, mx⟩ := code_ok a (by simp [a]) (by simp [a]) hd + have cover : Covers (a.rd ++ a.wr) (s.rd ++ s.wr) := by + intro p n ⟨r, hr, hc⟩ + change r ∈ [⟨s.gpr .rsi, 1024⟩, ⟨s.gpr .rdi, 1024⟩] at hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact hs p n ⟨_, by simp, hc⟩ + · obtain ⟨r, hr, hc⟩ := hw p n ⟨_, by simp, hc⟩ + exact ⟨r, List.mem_append_right _ hr, hc⟩ + have he' := Exec.widen (rd := s.rd) (wr := s.wr) he cover hw + simp only [a, State.withRegions_withRegions, State.withRegions_self] at he' + refine ⟨tr, t.withRegions s.rd s.wr, he', value, frame, ?_, mx⟩ + exact ⟨keeps.1, rfl, rfl⟩ + +end VG.Proof.Argon2.X86_64.FillWrite diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteLit.lean new file mode 100644 index 000000000..d869cc860 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteLit.lean @@ -0,0 +1,10 @@ +import VerifiedGarbage.Proof.Framework.X86_64.Lit +import VerifiedGarbage.Impl.Argon2.X86_64.FillWrite + +/-! Checked literal of the complete copy/XOR block write. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.FillWrite.code + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWritePrefix.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWritePrefix.lean new file mode 100644 index 000000000..71ba91432 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWritePrefix.lean @@ -0,0 +1,90 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FillWriteWord +import VerifiedGarbage.Proof.Argon2.X86_64.Finish + +/-! Compose the word writes without re-executing a long load/store block. -/ + +namespace VG.Proof.Argon2.X86_64.FillWrite + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.FillWrite + +def result (xorOld : Bool) (m : Mem) (src dest : Addr) : Block := + if xorOld then xorBlock (blockAt m src) (blockAt m dest) else blockAt m src + +theorem result_get (xorOld : Bool) (m : Mem) (src dest : Addr) (i : Fin 128) : + (result xorOld m src dest)[i] = value xorOld m src dest i.val := by + cases xorOld <;> simp only [result, value, Bool.false_eq_true, ite_false, ite_true, + xorBlock_get, blockAt_get] + +theorem frame_extend {m m' : Mem} {dest : Addr} {n k : Nat} + (hf : Frame [⟨dest, 8 * n⟩] m m') (h : n ≤ k) : Frame [⟨dest, 8 * k⟩] m m' := by + apply hf.sub + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact ⟨_, by simp, Region.sub_prefix (Nat.mul_le_mul_left 8 h)⟩ + +theorem source_read {m m' : Mem} {src dest : Addr} {n : Nat} + (hf : Frame [⟨dest, 8 * n⟩] m m') (hn : n ≤ 128) + (hd : (⟨src, 1024⟩ : Region).Disjoint ⟨dest, 1024⟩) (i : Fin 128) : + m'.readW (off src (8 * i.val)) 64 = m.readW (off src (8 * i.val)) 64 := by + have full := frame_extend hf hn + exact full.readW (r := ⟨src, 1024⟩) + (Offset.contains_base src (by omega) (by omega)) + (by intro r hr; simp only [List.mem_singleton] at hr; subst r; exact hd) (by decide) + +theorem old_read {m m' : Mem} {dest : Addr} {n : Nat} + (hf : Frame [⟨dest, 8 * n⟩] m m') (hn : n < 128) : + m'.readW (off dest (8 * n)) 64 = m.readW (off dest (8 * n)) 64 := + hf.readW (r := ⟨off dest (8 * n), 8⟩) (Region.contains_self _ _) + (by + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact Offset.disjoint_base dest (Nat.le_refl _) (by omega)) (by decide) + +theorem prefix_ok (xorOld : Bool) (n : Nat) (hn : n ≤ 128) (s : State) + (hs : (⟨s.gpr .rsi, 1024⟩ : Region) ∈ s.rd ++ s.wr) + (hw : (⟨s.gpr .rdi, 1024⟩ : Region) ∈ s.wr) + (hd : (⟨s.gpr .rsi, 1024⟩ : Region).Disjoint ⟨s.gpr .rdi, 1024⟩) : + WP isa (.block (words xorOld n)) s fun t => + Written t.mem (s.gpr .rdi) (result xorOld s.mem (s.gpr .rsi) (s.gpr .rdi)) n ∧ + Frame [⟨s.gpr .rdi, 8 * n⟩] s.mem t.mem ∧ CopyKeeps s t ∧ t.mxcsr = s.mxcsr := by + induction n with + | zero => exact WP.block_nil ⟨fun i hi => by omega, Frame.refl _ _, CopyKeeps.refl s, rfl⟩ + | succ n ih => + simp only [words, List.range_succ, List.flatMap_append, List.flatMap_cons, + List.flatMap_nil, List.append_nil] + apply WP.block_append + refine (ih (by omega)).mono ?_ + rintro t ⟨written, frame, keeps, mx⟩ + have hn' : n < 128 := by omega + have src : t.gpr .rsi = s.gpr .rsi := keeps.1 .rsi (by decide) + have dest : t.gpr .rdi = s.gpr .rdi := keeps.1 .rdi (by decide) + have write : InRegions t.wr (off (t.gpr .rdi) (8 * n)) 8 := by + rw [dest, keeps.2.2] + exact ⟨_, hw, Offset.contains_base _ (by omega) (by omega)⟩ + have read : InRegions (t.rd ++ t.wr) (off (t.gpr .rsi) (8 * n)) 8 := by + rw [src, keeps.2.1, keeps.2.2] + exact ⟨_, hs, Offset.contains_base _ (by omega) (by omega)⟩ + have old : InRegions (t.rd ++ t.wr) (off (t.gpr .rdi) (8 * n)) 8 := by + obtain ⟨r, hr, hc⟩ := write + exact ⟨r, List.mem_append_right _ hr, hc⟩ + refine (word_ok xorOld t n read write old).mono ?_ + rintro u ⟨mem, regs, rd, wr, mx'⟩ + have v : value xorOld t.mem (t.gpr .rsi) (t.gpr .rdi) n = + (result xorOld s.mem (s.gpr .rsi) (s.gpr .rdi))[(⟨n, hn'⟩ : Fin 128)] := by + rw [result_get, src, dest] + unfold value + rw [source_read frame (by omega) hd ⟨n, hn'⟩] + cases xorOld + · rfl + · rw [old_read frame hn'] + refine ⟨?_, ?_, keeps.trans ⟨regs, rd, wr⟩, mx'.trans mx⟩ + · rw [mem, v, dest] + exact written_step hn' written + · rw [mem, dest] + exact (frame_extend frame (Nat.le_succ n)).writeW + (r := ⟨s.gpr .rdi, 8 * (n + 1)⟩) (by simp) _ + (Offset.contains_base _ (by omega) (by omega)) + +end VG.Proof.Argon2.X86_64.FillWrite diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteWord.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteWord.lean new file mode 100644 index 000000000..ce2f39452 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FillWriteWord.lean @@ -0,0 +1,40 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FillWrite +import VerifiedGarbage.Proof.Argon2.X86_64.Memory +import VerifiedGarbage.Proof.Argon2.X86_64.DivideStep + +/-! One output word, keeping register writes folded during execution. -/ + +namespace VG.Proof.Argon2.X86_64.FillWrite + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FillWrite + +def value (xorOld : Bool) (m : Mem) (src dest : Addr) (i : Nat) : Addr := + let next := m.readW (off src (8 * i)) 64 + if xorOld then next ^^^ m.readW (off dest (8 * i)) 64 else next + +/-- The source is readable and the destination writable; its old contents +are read only on later passes. -/ +theorem word_ok (xorOld : Bool) (s : State) (i : Nat) + (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rsi) (8 * i)) 8) + (hw : InRegions s.wr (off (s.gpr .rdi) (8 * i)) 8) + (ho : InRegions (s.rd ++ s.wr) (off (s.gpr .rdi) (8 * i)) 8) : + WP isa (.block (Impl.Argon2.X86_64.FillWrite.word xorOld i)) s fun t => + t.mem = s.mem.writeW (off (s.gpr .rdi) (8 * i)) + (value xorOld s.mem (s.gpr .rsi) (s.gpr .rdi) i) ∧ + (∀ r, r ≠ .rax → t.gpr r = s.gpr r) ∧ + t.rd = s.rd ∧ t.wr = s.wr ∧ t.mxcsr = s.mxcsr := by + cases xorOld <;> apply WP.of_runBlock <;> + simp only [Impl.Argon2.X86_64.FillWrite.word, value, Bool.false_eq_true, ite_false, ite_true, + List.cons_append, List.nil_append, + runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + State.load64, State.store64, execAlu, ea_at, hr, hw, ho, + RegUpd.gpr_setReg, RegUpd.mem_setReg, RegUpd.rd_setReg, RegUpd.wr_setReg, + RegUpd.gpr_arithFlags, RegUpd.mem_arithFlags, RegUpd.rd_arithFlags, + RegUpd.wr_arithFlags, reduceCtorEq, ite_true, ite_false, + Option.map_some, Option.bind_some, Option.some.injEq, exists_eq_left'] + all_goals + refine ⟨trivial, ?_, trivial, trivial, rfl⟩ + intro r hr + simp only [hr, ite_false] + +end VG.Proof.Argon2.X86_64.FillWrite diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalCall.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalCall.lean new file mode 100644 index 000000000..e2fc8dfec --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalCall.lean @@ -0,0 +1,94 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.MemoryInitCall + +/-! The final H′ call accepts a complete reduced block and a public tag length. -/ + +namespace VG.Proof.Argon2.X86_64.FinalCall + +open VG VG.X86_64 +open VG.Impl.Argon2.X86_64.HPrime (code) +open VG.Spec.Blake2 (bytesAt) + +structure CallReady (len : Nat) (s : State) : Prop where + positive : 1 ≤ len + bound : len < 2 ^ 32 + input : Covers [⟨s.gpr .rdi, 1024⟩] (s.rd ++ s.wr) + output : Covers [⟨s.gpr .rdx, len⟩] s.wr + work : (⟨s.gpr .r8, 16384⟩ : Region) ∈ s.wr + inputWork : (⟨s.gpr .rdi, 1024⟩ : Region).Disjoint ⟨s.gpr .r8, 16384⟩ + outputWork : (⟨s.gpr .rdx, len⟩ : Region).Disjoint ⟨s.gpr .r8, 16384⟩ + stackInput : (below (s.gpr .rsp) 24).Disjoint ⟨s.gpr .rdi, 1024⟩ + stackOutput : (below (s.gpr .rsp) 24).Disjoint ⟨s.gpr .rdx, len⟩ + stackWork : (below (s.gpr .rsp) 24).Disjoint ⟨s.gpr .r8, 16384⟩ + +structure Called (len : Nat) (s t : State) : Prop where + digest : bytesAt t.mem (s.gpr .rdx) len = Spec.Argon2.hPrime len (bytesAt s.mem (s.gpr .rdi) 1024) + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨s.gpr .rdx, len⟩, ⟨s.gpr .r8, 16384⟩, below (s.gpr .rsp) 24] s.mem t.mem + +theorem hPrime_call_hyps (len : Nat) (s : State) (h : CallReady len s) + (inputLength : s.gpr .rsi = 1024) (outputLength : s.gpr .rcx = BitVec.ofNat 64 len) : + HPrime.localContract.pre (s.callEntry.withRegions [⟨s.gpr .rdi, 1024⟩] + [⟨s.gpr .rdx, len⟩, ⟨s.gpr .r8, 16384⟩]) ∧ + Covers [⟨s.gpr .rdi, 1024⟩, ⟨s.gpr .rdx, len⟩, ⟨s.gpr .r8, 16384⟩] (s.rd ++ s.wr) ∧ + Covers [⟨s.gpr .rdx, len⟩, ⟨s.gpr .r8, 16384⟩] s.wr := by + have length : (BitVec.ofNat 64 len).toNat = len := by + rw [BitVec.toNat_ofNat, Nat.mod_eq_of_lt (Nat.lt_trans h.bound (by decide))] + have g : ∀ r, r ≠ .rsp → s.callEntry.gpr r = s.gpr r := fun r hr => State.callEntry_gpr s hr + have inner := below_callee (s.gpr .rsp) 16 + have ret := below_sub (sp := s.gpr .rsp) (by decide : 8 ≤ 24) (by decide) + refine ⟨?_, ?_, ?_⟩ + · simp only [HPrime.localContract, HPrime.inputR, HPrime.outputR, HPrime.workR, + HPrime.stackR, HPrime.retR, State.withRegions_gpr, State.withRegions_rd, + State.withRegions_wr, g _ (by decide : Reg.rdi ≠ .rsp), + g _ (by decide : Reg.rsi ≠ .rsp), g _ (by decide : Reg.rdx ≠ .rsp), + g _ (by decide : Reg.rcx ≠ .rsp), g _ (by decide : Reg.r8 ≠ .rsp), + State.callEntry_rsp, inputLength, outputLength, length] + exact ⟨rfl, trivial, by decide, h.positive, h.bound, h.inputWork, h.outputWork, + h.stackInput.sub_left inner, h.stackOutput.sub_left inner, h.stackWork.sub_left inner, + h.stackOutput.sub_left ret, h.stackWork.sub_left ret⟩ + · intro p n hp + rcases hp with ⟨r, hr, hc⟩ + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · exact h.input p n ⟨_, List.mem_singleton_self _, hc⟩ + · obtain ⟨r, hr, hc⟩ := h.output p n ⟨_, List.mem_singleton_self _, hc⟩ + exact ⟨r, List.mem_append_right _ hr, hc⟩ + · exact ⟨_, List.mem_append_right _ h.work, hc⟩ + · intro p n ⟨r, hr, hc⟩ + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact h.output p n ⟨_, List.mem_singleton_self _, hc⟩ + · exact ⟨_, h.work, hc⟩ + +theorem hPrime_call_ok (v : Proof.Blake2.X86_64.Backend) (name : String) + (len : Nat) (s : State) (h : CallReady len s) (inputLength : s.gpr .rsi = 1024) + (outputLength : s.gpr .rcx = BitVec.ofNat 64 len) : + WP isa (.call name (code (HPrime.hash v))) s (Called len s) := by + obtain ⟨pre, cover, writes⟩ := hPrime_call_hyps len s h inputLength outputLength + refine WP.call (k := HPrime.localContract) (HPrime.code_correct v) (MemoryInit.hPrime_nosp v) + (by rw [MemoryInit.hPrime_depth]; decide) pre cover writes ?_ + intro t rd wr regs frame _ ⟨u, memU, regsU, digest⟩ + have inputBytes : bytesAt s.callEntry.mem (s.gpr .rdi) 1024 = bytesAt s.mem (s.gpr .rdi) 1024 := by + apply Proof.Blake2.bytesAt_congr + intro i hi + exact Proof.MdStream.X86_64.callEntry_byte s (h.stackInput.sub_left + (below_sub (by decide) (by decide))) (show (1024 : Nat) ≤ 2 ^ 64 from by decide) hi + refine ⟨?_, regs, rd, wr, ?_⟩ + · change bytesAt u.mem (s.callEntry.gpr .rdx) (s.callEntry.gpr .rcx).toNat = + Spec.Argon2.hPrime (s.callEntry.gpr .rcx).toNat + (bytesAt s.callEntry.mem (s.callEntry.gpr .rdi) (s.callEntry.gpr .rsi).toNat) at digest + rw [State.callEntry_gpr _ (by decide : Reg.rdx ≠ .rsp), + State.callEntry_gpr _ (by decide : Reg.rcx ≠ .rsp), + State.callEntry_gpr _ (by decide : Reg.rdi ≠ .rsp), + State.callEntry_gpr _ (by decide : Reg.rsi ≠ .rsp), + memU, inputLength, outputLength, + show (1024 : Addr).toNat = 1024 from rfl, + show (BitVec.ofNat 64 len).toNat = len from by + rw [BitVec.toNat_ofNat, Nat.mod_eq_of_lt (Nat.lt_trans h.bound (by decide))], inputBytes] at digest + exact digest + · rw [MemoryInit.hPrime_depth] at frame + exact frame + +end VG.Proof.Argon2.X86_64.FinalCall diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalCallCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalCallCT.lean new file mode 100644 index 000000000..8b1a0b1ca --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalCallCT.lean @@ -0,0 +1,36 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FinalCall + +/-! # The final H′ call leak only their public argument registers -/ + +namespace VG.Proof.Argon2.X86_64.FinalCall + +open VG VG.X86_64 +open VG.Impl.Argon2.X86_64.HPrime (code) + +theorem hPrime_call_rel (v : Proof.Blake2.X86_64.Backend) (name : String) (len : Nat) + {P : State → State → Prop} + (pre : ∀ s t, P s t → CallReady len s ∧ CallReady len t ∧ + s.gpr .rsi = 1024 ∧ t.gpr .rsi = 1024 ∧ s.gpr .rcx = BitVec.ofNat 64 len ∧ t.gpr .rcx = BitVec.ofNat 64 len ∧ + s.gpr .rdi = t.gpr .rdi ∧ s.gpr .rdx = t.gpr .rdx ∧ + s.gpr .r8 = t.gpr .r8 ∧ s.gpr .rsp = t.gpr .rsp) : + RelCT isa P (.call name (code (HPrime.hash v))) (fun _ _ => True) := by + apply RelCT.callEx (k := HPrime.localContract) (HPrime.code_correct v) (HPrime.code_ct v) + intro s t hp + obtain ⟨hs, ht, ls, lt, os, ot, di, dx, r8, sp⟩ := pre s t hp + obtain ⟨ps, cs, ws⟩ := hPrime_call_hyps len s hs ls os + obtain ⟨pt, ct, wt⟩ := hPrime_call_hyps len t ht lt ot + refine ⟨_, _, _, _, ps, pt, ?_, cs, ws, ct, wt, sp⟩ + change s.callEntry.gpr .rdi = t.callEntry.gpr .rdi ∧ + s.callEntry.gpr .rsi = t.callEntry.gpr .rsi ∧ + s.callEntry.gpr .rdx = t.callEntry.gpr .rdx ∧ + s.callEntry.gpr .rcx = t.callEntry.gpr .rcx ∧ + s.callEntry.gpr .r8 = t.callEntry.gpr .r8 ∧ + s.callEntry.gpr .rsp = t.callEntry.gpr .rsp + simp only [State.callEntry_gpr _ (by decide : Reg.rdi ≠ .rsp), + State.callEntry_gpr _ (by decide : Reg.rsi ≠ .rsp), + State.callEntry_gpr _ (by decide : Reg.rdx ≠ .rsp), + State.callEntry_gpr _ (by decide : Reg.rcx ≠ .rsp), + State.callEntry_gpr _ (by decide : Reg.r8 ≠ .rsp), State.callEntry_rsp] + exact ⟨di, ls.trans lt.symm, dx, os.trans ot.symm, r8, congrArg (· - 8) sp⟩ + +end VG.Proof.Argon2.X86_64.FinalCall diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutput.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutput.lean new file mode 100644 index 000000000..2ccc2be5a --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutput.lean @@ -0,0 +1,41 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FinalOutputReady +import VerifiedGarbage.Proof.Argon2.Serialization + +/-! The generic H′ call produces exactly the reviewed final Argon2 tag. -/ + +namespace VG.Proof.Argon2.X86_64.FinalOutput + +open VG VG.X86_64 VG.Spec.Argon2 +open VG.Spec.Blake2 (bytesAt) + +structure Done (s t : State) (p : Params) (memory : Array Block) : Prop where + digest : bytesAt t.mem (output s) p.tagLen = finish p memory + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨output s, p.tagLen⟩, ⟨work s, 16384⟩, below (s.gpr .rsp) 24] s.mem t.mem + +theorem code_ok (v : Proof.Blake2.X86_64.Backend) (name : String) (s : State) (p : Params) + (h : Ready p s) (memory : Array Block) + (block : blockAt s.mem (ReductionState.matrix s) = Proof.Argon2.reduction p memory 0 p.lanes zeroBlock) : + WP isa (Impl.Argon2.X86_64.FinalOutput.code name (HPrime.hash v)) s (Done s · p memory) := by + unfold Impl.Argon2.X86_64.FinalOutput.code + refine WP.seq ((args_ok s h.reads).mono ?_) + intro a args + have length := args.outputLength.trans h.tagWord + refine (FinalCall.hPrime_call_ok v name p.tagLen a (args.ready h) args.inputLength length).mono ?_ + intro t called + refine ⟨?_, fun r hr => (called.regs r hr).trans (args.regs r hr), called.rd.trans args.keeps.rd, + called.wr.trans args.keeps.wr, ?_⟩ + · have input : bytesAt a.mem (a.gpr .rdi) 1024 = + serialize (Proof.Argon2.reduction p memory 0 p.lanes zeroBlock) := by + rw [args.keeps.mem, args.input, ← Proof.Argon2.serialize_blockAt, block] + rw [Proof.Argon2.finish_reduction] + have digest := called.digest + rw [args.output, input] at digest + exact digest + · have frame := called.frame + rw [args.output, args.work, args.regs .rsp (by simp [calleeSaved]), args.keeps.mem] at frame + exact frame + +end VG.Proof.Argon2.X86_64.FinalOutput diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutputArgs.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutputArgs.lean new file mode 100644 index 000000000..c9555a98e --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutputArgs.lean @@ -0,0 +1,47 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FinalOutput +import VerifiedGarbage.Proof.Argon2.X86_64.ReductionLoopState + +/-! Load the public final-call pointers and tag length from the enclosing frame. -/ + +namespace VG.Proof.Argon2.X86_64.FinalOutput + +open VG VG.X86_64 VG.Spec.Argon2 + +def output (s : State) : Addr := s.mem.readW (off (s.gpr .rbp) 256) 64 + +def work (s : State) : Addr := s.mem.readW (off (s.gpr .rbp) 248) 64 + +def changed : List Reg := [.rdi, .rsi, .rdx, .rcx, .r8] + +structure Arguments (s t : State) : Prop where + input : t.gpr .rdi = ReductionState.matrix s + inputLength : t.gpr .rsi = 1024 + output : t.gpr .rdx = output s + outputLength : t.gpr .rcx = s.mem.readW (off (s.gpr .rbp) 264) 64 + work : t.gpr .r8 = work s + keeps : Divide.Keeps changed s t + +theorem args_ok (s : State) (read : ∀ d ∈ [232, 256, 264, 248], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8) : + WP isa (.block Impl.Argon2.X86_64.FinalOutput.args) s (Arguments s) := by + have input := read 232 (by simp) + have out := read 256 (by simp) + have len := read 264 (by simp) + have scratch := read 248 (by simp) + apply WP.of_runBlock + simp only [Impl.Argon2.X86_64.FinalOutput.args, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + State.load64, ea_at, RegUpd.gpr_setReg, RegUpd.mem_setReg, RegUpd.rd_setReg, RegUpd.wr_setReg, + input, out, len, scratch, reduceCtorEq, ite_true, ite_false, Option.map_some, Option.some.injEq, exists_eq_left'] + refine ⟨rfl, rfl, rfl, rfl, rfl, ?_⟩ + constructor + · intro r hr + simp only [changed, List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, hr.1, hr.2.1, hr.2.2.1, hr.2.2.2.1, hr.2.2.2.2, ite_false] + all_goals rfl + +theorem Arguments.regs {s t : State} (h : Arguments s t) (r : Reg) (hr : r ∈ calleeSaved) : t.gpr r = s.gpr r := by + have unchanged : r ∉ changed := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + exact h.keeps.regs r unchanged + +end VG.Proof.Argon2.X86_64.FinalOutput diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutputCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutputCT.lean new file mode 100644 index 000000000..71156dbe6 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutputCT.lean @@ -0,0 +1,56 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FinalOutputReady +import VerifiedGarbage.Proof.Argon2.X86_64.FinalCallCT + +/-! Final hashing exposes only the public tag length and pointers, for any hash backend. -/ + +namespace VG.Proof.Argon2.X86_64.FinalOutput + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Related (p : Params) (s t : State) : Prop where + left : Ready p s + right : Ready p t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + matrices : ReductionState.matrix s = ReductionState.matrix t + outputs : output s = output t + works : work s = work t + +structure NextRelated (p : Params) (s t : State) : Prop where + left : FinalCall.CallReady p.tagLen s + right : FinalCall.CallReady p.tagLen t + leftInputLength : s.gpr .rsi = 1024 + rightInputLength : t.gpr .rsi = 1024 + leftOutputLength : s.gpr .rcx = BitVec.ofNat 64 p.tagLen + rightOutputLength : t.gpr .rcx = BitVec.ofNat 64 p.tagLen + inputs : s.gpr .rdi = t.gpr .rdi + outputs : s.gpr .rdx = t.gpr .rdx + works : s.gpr .r8 = t.gpr .r8 + stacks : s.gpr .rsp = t.gpr .rsp + +theorem args_trace : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block Impl.Argon2.X86_64.FinalOutput.args) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr; simp only [List.mem_cons, List.not_mem_nil, or_false] at hr; subst r; exact h)) (by taint_decide) + +theorem args_rel (p : Params) : RelCT isa (Related p) + (.block Impl.Argon2.X86_64.FinalOutput.args) (NextRelated p) := by + have trace := args_trace.mono (P' := Related p) (fun _ _ h => h.bases) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => ⟨args_ok s h.left.reads, args_ok t h.right.reads⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + exact ⟨ha.ready hp.left, hb.ready hp.right, ha.inputLength, hb.inputLength, + ha.outputLength.trans hp.left.tagWord, hb.outputLength.trans hp.right.tagWord, + ha.input.trans (hp.matrices.trans hb.input.symm), ha.output.trans (hp.outputs.trans hb.output.symm), + ha.work.trans (hp.works.trans hb.work.symm), + (ha.regs .rsp (by simp [calleeSaved])).trans (hp.stacks.trans (hb.regs .rsp (by simp [calleeSaved])).symm)⟩ + +theorem code_rel (v : Proof.Blake2.X86_64.Backend) (name : String) (p : Params) : + RelCT isa (Related p) (Impl.Argon2.X86_64.FinalOutput.code name (HPrime.hash v)) (fun _ _ => True) := + (args_rel p).seq (FinalCall.hPrime_call_rel v name p.tagLen (fun _ _ h => + ⟨h.left, h.right, h.leftInputLength, h.rightInputLength, h.leftOutputLength, h.rightOutputLength, + h.inputs, h.outputs, h.works, h.stacks⟩)) + +end VG.Proof.Argon2.X86_64.FinalOutput diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutputReady.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutputReady.lean new file mode 100644 index 000000000..caaa62530 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalOutputReady.lean @@ -0,0 +1,39 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FinalOutputArgs +import VerifiedGarbage.Proof.Argon2.X86_64.FinalCall + +/-! Final output uses matrix block zero and the original disjoint hash scratch allocation. -/ + +namespace VG.Proof.Argon2.X86_64.FinalOutput + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Ready (p : Params) (s : State) : Prop where + positive : 1 ≤ p.tagLen + bound : p.tagLen < 2 ^ 32 + reads : ∀ d ∈ [232, 256, 264, 248], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8 + tagWord : s.mem.readW (off (s.gpr .rbp) 264) 64 = BitVec.ofNat 64 p.tagLen + input : Covers [⟨ReductionState.matrix s, 1024⟩] (s.rd ++ s.wr) + outputWrite : Covers [⟨output s, p.tagLen⟩] s.wr + workWrite : (⟨work s, 16384⟩ : Region) ∈ s.wr + inputWork : (⟨ReductionState.matrix s, 1024⟩ : Region).Disjoint ⟨work s, 16384⟩ + outputWork : (⟨output s, p.tagLen⟩ : Region).Disjoint ⟨work s, 16384⟩ + stackInput : (below (s.gpr .rsp) 24).Disjoint ⟨ReductionState.matrix s, 1024⟩ + stackOutput : (below (s.gpr .rsp) 24).Disjoint ⟨output s, p.tagLen⟩ + stackWork : (below (s.gpr .rsp) 24).Disjoint ⟨work s, 16384⟩ + +theorem Arguments.ready {p : Params} {s t : State} (h : Ready p s) (a : Arguments s t) : + FinalCall.CallReady p.tagLen t := by + have sp := a.regs .rsp (by simp [calleeSaved]) + constructor + · exact h.positive + · exact h.bound + · rw [a.input, a.keeps.rd, a.keeps.wr]; exact h.input + · rw [a.output, a.keeps.wr]; exact h.outputWrite + · rw [a.work, a.keeps.wr]; exact h.workWrite + · rw [a.input, a.work]; exact h.inputWork + · rw [a.output, a.work]; exact h.outputWork + · rw [a.input, sp]; exact h.stackInput + · rw [a.output, sp]; exact h.stackOutput + · rw [a.work, sp]; exact h.stackWork + +end VG.Proof.Argon2.X86_64.FinalOutput diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalReduction.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalReduction.lean new file mode 100644 index 000000000..4cfbcb29c --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinalReduction.lean @@ -0,0 +1,34 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FinalReduction +import VerifiedGarbage.Proof.Argon2.X86_64.ReductionInit +import VerifiedGarbage.Proof.Argon2.X86_64.ReductionInitCT + +/-! Complete final block reduction, including setup, termination and a public trace. -/ + +namespace VG.Proof.Argon2.X86_64.FinalReduction + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState + +theorem code_ok (s : State) (p : Params) (h : ReductionInit.Ready p s) (memory : Array Block) + (represented : Proof.Argon2.Represents s.mem (matrix s) p.blocks memory) : + WP isa Impl.Argon2.X86_64.FinalReduction.code s + (ReduceLanes.Finished s · p memory (Proof.Argon2.reduction p memory 0 p.lanes zeroBlock)) := by + unfold Impl.Argon2.X86_64.FinalReduction.code + refine WP.seq ((ReductionInit.code_ok s p h memory represented).mono ?_) + intro a prepared + refine (ReduceLanes.loop_ok p.lanes a p 0 prepared.ready memory zeroBlock prepared.represented + h.allocation.positive (Nat.zero_add _)).mono ?_ + intro t finished + refine ⟨finished.represented, finished.base.trans prepared.base, finished.laneWord, + finished.rd.trans prepared.rd, finished.wr.trans prepared.wr, ?_, finished.mxcsr.trans prepared.mxcsr, ?_⟩ + · have frame := finished.frame + rw [prepared.base] at frame + exact prepared.frame.trans frame + · intro r hr bx; exact (finished.regs r hr bx).trans (prepared.regs r hr bx) + +theorem code_rel (p : Params) (positive : 0 < p.lanes) (leftMemory rightMemory : Array Block) : + RelCT isa (ReductionInit.Related p leftMemory rightMemory) Impl.Argon2.X86_64.FinalReduction.code + (fun _ _ => True) := + (ReductionInit.code_rel p leftMemory rightMemory).seq + (ReduceLanes.loop_rel p 0 p.lanes leftMemory rightMemory zeroBlock zeroBlock positive (Nat.zero_add _)) + +end VG.Proof.Argon2.X86_64.FinalReduction diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinishReady.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinishReady.lean new file mode 100644 index 000000000..7b1833b58 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinishReady.lean @@ -0,0 +1,43 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FinalReduction +import VerifiedGarbage.Proof.Argon2.X86_64.FinalOutputReady + +/-! Preserve the final-call allocations and metadata through the matrix reduction. -/ + +namespace VG.Proof.Argon2.X86_64.Finish + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState + +structure Ready (p : Params) (s : State) : Prop where + reduction : ReductionInit.Ready p s + output : FinalOutput.Ready p s + +theorem frame_word {s t : State} {p : Params} {memory : Array Block} {acc : Block} + (ready : ReductionInit.Ready p s) (done : ReduceLanes.Finished s t p memory acc) + (d : Nat) (bound : d + 8 ≤ 272) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [done.regs .rbp (by simp [calleeSaved]) (by decide)] + exact done.frame.readW (r := ⟨s.gpr .rbp, 272⟩) (Offset.contains_base _ bound (by omega)) + (by intro r hr; simp only [List.mem_singleton] at hr; subst r + exact ready.allocation.frame.symm.sub_right (Region.sub_prefix (by + have nonempty := Proof.Argon2.lastIndex_bounds p ready.allocation.positive ready.allocation.minimum 0 ready.allocation.positive + omega))) (by decide) + +theorem output_ready {s t : State} {p : Params} {memory : Array Block} {acc : Block} + (ready : Ready p s) (done : ReduceLanes.Finished s t p memory acc) : FinalOutput.Ready p t := by + have bp := done.regs .rbp (by simp [calleeSaved]) (by decide) + have sp := done.regs .rsp (by simp [calleeSaved]) (by decide) + have output : FinalOutput.output t = FinalOutput.output s := frame_word ready.reduction done 256 (by decide) + have work : FinalOutput.work t = FinalOutput.work s := frame_word ready.reduction done 248 (by decide) + refine ⟨ready.output.positive, ready.output.bound, ?_, + (frame_word ready.reduction done 264 (by decide)).trans ready.output.tagWord, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · rw [done.rd, done.wr, bp]; exact ready.output.reads + · rw [done.base, done.rd, done.wr]; exact ready.output.input + · rw [output, done.wr]; exact ready.output.outputWrite + · rw [work, done.wr]; exact ready.output.workWrite + · rw [done.base, work]; exact ready.output.inputWork + · rw [output, work]; exact ready.output.outputWork + · rw [sp, done.base]; exact ready.output.stackInput + · rw [sp, output]; exact ready.output.stackOutput + · rw [sp, work]; exact ready.output.stackWork + +end VG.Proof.Argon2.X86_64.Finish diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinishStage.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinishStage.lean new file mode 100644 index 000000000..aa9e52815 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinishStage.lean @@ -0,0 +1,51 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.Finish +import VerifiedGarbage.Proof.Argon2.X86_64.FinishReady +import VerifiedGarbage.Proof.Argon2.X86_64.FinalOutput + +/-! The complete reviewed finish computation, with its enclosing frame and ABI obligations. -/ + +namespace VG.Proof.Argon2.X86_64.Finish + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState +open VG.Spec.Blake2 (bytesAt) + +def writes (s : State) (p : Params) : List Region := + [⟨matrix s, 1024⟩, ⟨FinalOutput.output s, p.tagLen⟩, + ⟨FinalOutput.work s, 16384⟩, below (s.gpr .rsp) 24] + +structure Done (s t : State) (p : Params) (memory : Array Block) : Prop where + digest : bytesAt t.mem (FinalOutput.output s) p.tagLen = Spec.Argon2.finish p memory + regs : ∀ r ∈ calleeSaved, r ≠ .rbx → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (writes s p) s.mem t.mem + +theorem code_ok (v : Proof.Blake2.X86_64.Backend) (name : String) (s : State) (p : Params) + (h : Ready p s) (memory : Array Block) + (represented : Proof.Argon2.Represents s.mem (matrix s) p.blocks memory) : + WP isa (Impl.Argon2.X86_64.Finish.code name (HPrime.hash v)) s (Done s · p memory) := by + unfold Impl.Argon2.X86_64.Finish.code + refine WP.seq ((FinalReduction.code_ok s p h.reduction memory represented).mono ?_) + intro a reduced + refine (FinalOutput.code_ok v name a p (output_ready h reduced) memory reduced.represented.accumulator).mono ?_ + intro t written + have output : FinalOutput.output a = FinalOutput.output s := frame_word h.reduction reduced 256 (by decide) + have work : FinalOutput.work a = FinalOutput.work s := frame_word h.reduction reduced 248 (by decide) + refine ⟨?_, fun r hr bx => (written.regs r hr).trans (reduced.regs r hr bx), + written.rd.trans reduced.rd, written.wr.trans reduced.wr, ?_⟩ + · have digest := written.digest + rw [output] at digest; exact digest + · have firstFrame : Frame (writes s p) s.mem a.mem := reduced.frame.sub (by + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact ⟨_, by simp [writes], fun _ h => h⟩) + have lastFrame := written.frame + rw [output, work, reduced.regs .rsp (by simp [calleeSaved]) (by decide)] at lastFrame + apply firstFrame.trans + apply lastFrame.sub + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl <;> exact ⟨_, by simp [writes], fun _ h => h⟩ + +end VG.Proof.Argon2.X86_64.Finish diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinishStageCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinishStageCT.lean new file mode 100644 index 000000000..b2874c355 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FinishStageCT.lean @@ -0,0 +1,53 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FinishReady +import VerifiedGarbage.Impl.Argon2.X86_64.Finish +import VerifiedGarbage.Proof.Argon2.X86_64.FinalOutputCT + +/-! Complete finalization has a public trace for every BLAKE2b backend. -/ + +namespace VG.Proof.Argon2.X86_64.Finish + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState + +structure Related (p : Params) (leftMemory rightMemory : Array Block) (s t : State) : Prop where + left : Ready p s + right : Ready p t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + matrices : matrix s = matrix t + outputs : FinalOutput.output s = FinalOutput.output t + works : FinalOutput.work s = FinalOutput.work t + leftMatrix : Proof.Argon2.Represents s.mem (matrix s) p.blocks leftMemory + rightMatrix : Proof.Argon2.Represents t.mem (matrix t) p.blocks rightMemory + +theorem code_rel (v : Proof.Blake2.X86_64.Backend) (name : String) (p : Params) + (leftMemory rightMemory : Array Block) : + RelCT isa (Related p leftMemory rightMemory) (Impl.Argon2.X86_64.Finish.code name (HPrime.hash v)) + (fun _ _ => True) := by + intro s t ts tt a b hp ea eb + cases ea with + | seq reduceA outputA => + cases eb with + | seq reduceB outputB => + have related : ReductionInit.Related p leftMemory rightMemory s t := + ⟨hp.left.reduction, hp.right.reduction, hp.bases, hp.matrices, hp.leftMatrix, hp.rightMatrix⟩ + obtain ⟨reduceTrace, _⟩ := FinalReduction.code_rel p hp.left.reduction.allocation.positive + leftMemory rightMemory _ _ _ _ _ _ related reduceA reduceB + obtain ⟨_, sa, runA, doneA⟩ := FinalReduction.code_ok s p hp.left.reduction leftMemory hp.leftMatrix + obtain ⟨_, sb, runB, doneB⟩ := FinalReduction.code_ok t p hp.right.reduction rightMemory hp.rightMatrix + obtain ⟨_, rfl⟩ := Exec.det reduceA runA + obtain ⟨_, rfl⟩ := Exec.det reduceB runB + have finalRelated : FinalOutput.Related p _ _ := + ⟨output_ready hp.left doneA, output_ready hp.right doneB, + (doneA.regs .rbp (by simp [calleeSaved]) (by decide)).trans + (hp.bases.trans (doneB.regs .rbp (by simp [calleeSaved]) (by decide)).symm), + (doneA.regs .rsp (by simp [calleeSaved]) (by decide)).trans + (hp.stacks.trans (doneB.regs .rsp (by simp [calleeSaved]) (by decide)).symm), + doneA.base.trans (hp.matrices.trans doneB.base.symm), + (frame_word hp.left.reduction doneA 256 (by decide)).trans + (hp.outputs.trans (frame_word hp.right.reduction doneB 256 (by decide)).symm), + (frame_word hp.left.reduction doneA 248 (by decide)).trans + (hp.works.trans (frame_word hp.right.reduction doneB 248 (by decide)).symm)⟩ + obtain ⟨outputTrace, _⟩ := FinalOutput.code_rel v name p _ _ _ _ _ _ finalRelated outputA outputB + exact ⟨by rw [reduceTrace, outputTrace], trivial⟩ + +end VG.Proof.Argon2.X86_64.Finish diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FirstLane.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FirstLane.lean new file mode 100644 index 000000000..4d3c83efe --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FirstLane.lean @@ -0,0 +1,61 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.FirstLane +import VerifiedGarbage.Proof.Argon2.X86_64.DivideStep + +/-! The first reference window stays in the current lane. -/ + +namespace VG.Proof.Argon2.X86_64.FirstLane + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FirstLane + +theorem test_ok (s : State) : WP isa (.block test) s fun t => + t.zf = decide (s.gpr .r9 = 0 ∧ s.gpr .r14 = 0) ∧ Divide.Keeps [.rax] s t := by + apply WP.of_runBlock + simp only [test, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, execAlu, + RegUpd.gpr_setReg, RegUpd.zf_setReg, RegUpd.zf_arithFlags, + reduceCtorEq, ite_true, Option.map_some, Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨?_, ?_⟩ + · apply Bool.eq_iff_iff.mpr + simp only [beq_iff_eq, decide_eq_true_eq] + change (s.gpr .r9 ||| s.gpr .r14) = 0#64 ↔ _ + exact BitVec.or_eq_zero_iff + · constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr, ite_false] + all_goals rfl + +theorem current_ok (s : State) : WP isa (.block current) s fun t => + t.gpr .r8 = s.gpr .rbx ∧ Divide.Keeps [.r8] s t := by + apply WP.of_runBlock + simp only [current, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + RegUpd.gpr_setReg, ite_true, Option.map_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, hr, ite_false] + all_goals rfl + +theorem code_ok (s : State) : WP isa code s fun t => + t.gpr .r8 = (if s.gpr .r9 = 0 ∧ s.gpr .r14 = 0 then s.gpr .rbx else s.gpr .r8) ∧ + Divide.Keeps [.rax, .r8] s t := by + unfold code + refine WP.seq ((test_ok s).mono ?_) + rintro a ⟨flag, keeps⟩ + refine WP.ite (decide (s.gpr .r9 = 0 ∧ s.gpr .r14 = 0)) + (by simp only [eval, flag]) ?_ ?_ + · intro h + have position := of_decide_eq_true h + refine (current_ok a).mono ?_ + rintro t ⟨out, tail⟩ + refine ⟨?_, (keeps.mono (by decide)).trans (tail.mono (by decide))⟩ + simpa only [position, and_self, ite_true, keeps.regs .rbx (by decide)] using out + · intro h + have position := of_decide_eq_false h + apply WP.of_runBlock + simp only [runBlock_nil, Option.some.injEq, exists_eq_left'] + refine ⟨?_, keeps.mono (by decide)⟩ + simp only [position, ite_false] + exact keeps.regs .r8 (by decide) + +end VG.Proof.Argon2.X86_64.FirstLane diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FirstLaneCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FirstLaneCT.lean new file mode 100644 index 000000000..f9094a469 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FirstLaneCT.lean @@ -0,0 +1,21 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.FirstLaneLit +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! The first-slice override branches only on the public position. -/ + +namespace VG.Proof.Argon2.X86_64.FirstLane + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.FirstLane + +theorem code_rel : RelCT isa + (fun s t => s.gpr .r9 = t.gpr .r9 ∧ s.gpr .r14 = t.gpr .r14) code + (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.r9, .r14]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact h.1 + · exact h.2)) (by taint_decide) + +end VG.Proof.Argon2.X86_64.FirstLane diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/FirstLaneLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FirstLaneLit.lean new file mode 100644 index 000000000..c128ec479 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/FirstLaneLit.lean @@ -0,0 +1,10 @@ +import VerifiedGarbage.Proof.Framework.X86_64.Lit +import VerifiedGarbage.Impl.Argon2.X86_64.FirstLane + +/-! A checked literal for the public first-slice lane override. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.FirstLane.code + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFill.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFill.lean new file mode 100644 index 000000000..a0fa47b11 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFill.lean @@ -0,0 +1,79 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.InitFill +import VerifiedGarbage.Proof.Argon2.X86_64.InitFillFrames + +/-! Exact initialization, every filling pass, final reduction and H′ after the reviewed H₀. -/ + +namespace VG.Proof.Argon2.X86_64.InitFill + +open VG VG.X86_64 VG.Spec.Argon2 +open VG.Spec.Blake2 (bytesAt) + +def result (p : Params) (h0 : List Byte) : List Byte := + Spec.Argon2.finish p (Proof.Argon2.iterations p 0 p.passes (initMemory p h0)).memory + +structure Done (s t : State) (p : Params) : Prop where + digest : bytesAt t.mem (FinalOutput.output s) p.tagLen = result p (bytesAt s.mem (s.gpr .rbp) 64) + bp : t.gpr .rbp = s.gpr .rbp + sp : t.gpr .rsp = s.gpr .rsp + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (writes s p) s.mem t.mem + +theorem writes_eq (s t : State) (p : Params) (bp : t.gpr .rbp = s.gpr .rbp) (sp : t.gpr .rsp = s.gpr .rsp) + (base : FillKernel.matrix t = FillKernel.matrix s) (work : FinalOutput.work t = FinalOutput.work s) + (output : FinalOutput.output t = FinalOutput.output s) : writes t p = writes s p := by + unfold writes + rw [bp, sp, base, work, output] + +theorem code_ok (v : Proof.Blake2.X86_64.Backend) (name : String) (s : State) (p : Params) (h : Ready p s) : + WP isa (Impl.Argon2.X86_64.InitFill.code name (HPrime.hash v)) s (Done s · p) := by + have params := h.environment.parameters + have q : 2 ≤ p.laneLen := by + have segments := Proof.Argon2.laneLen_segments p params.lanesPositive + have minimum := params.segment_bound.1 + omega + have blocks := Proof.Argon2.lastIndex_bounds p params.lanesPositive params.segment_bound.1 0 params.lanesPositive + unfold Impl.Argon2.X86_64.InitFill.code + refine WP.seq ((MemoryInit.complete_ok v name s (FillKernel.matrix s) p.lanes p.laneLen h.initializing + params.lanesPositive (Nat.lt_trans params.lanesBound (by decide)) q).mono ?_) + intro a initialized + have setupReady := initialized_setup h initialized + have initializedBase : FillKernel.matrix a = FillKernel.matrix s := + initialized.frame_word h.initializing.space 232 (by decide) (Or.inr (by decide)) + have initializedWork : FinalOutput.work a = FinalOutput.work s := + initialized.frame_word h.initializing.space 248 (by decide) (Or.inr (by decide)) + have initializedOutput : FinalOutput.output a = FinalOutput.output s := + initialized.frame_word h.initializing.space 256 (by decide) (Or.inr (by decide)) + have rep : Proof.Argon2.Represents a.mem (FillKernel.matrix a) p.blocks + (initMemory p (bytesAt s.mem (s.gpr .rbp) 64)).memory := by + rw [initializedBase] + exact initialized.initialized.represents params.lanesPositive (by omega) + refine WP.seq ((FillSetup.code_ok a p setupReady).mono ?_) + intro b prepared + refine (FillFinish.code_ok v name b p (prepared.finish_ready setupReady (initialized_output h initialized) h.positive) + (initMemory p (bytesAt s.mem (s.gpr .rbp) 64)) (prepared.represents setupReady _ rep)).mono ?_ + intro t filled + have bp := prepared.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide) (by decide) + have sp := prepared.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide) (by decide) + have work : FinalOutput.work b = FinalOutput.work a := prepared.words 248 (by decide) (by decide) + have output : FinalOutput.output b = FinalOutput.output a := prepared.words 256 (by decide) (by decide) + refine ⟨?_, (filled.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).trans (bp.trans initialized.bp), + (filled.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide)).trans (sp.trans initialized.sp), + filled.rd.trans (prepared.rd.trans initialized.rd), filled.wr.trans (prepared.wr.trans initialized.wr), ?_⟩ + · have digest := filled.digest + rw [output, initializedOutput] at digest + exact digest + · have initialFrame := initialization_frame h initialized + have setupFrame := setup_frame (p := p) prepared.frame + rw [writes_eq s a p initialized.bp initialized.sp initializedBase initializedWork initializedOutput] at setupFrame + have fillFrame := filling_frame (by omega : 0 < p.blocks) filled.frame + rw [writes_eq a b p bp sp prepared.matrix work output, + writes_eq s a p initialized.bp initialized.sp initializedBase initializedWork initializedOutput] at fillFrame + exact (initialFrame.trans setupFrame).trans fillFrame + +theorem result_derive (p : Params) (password salt secret ad : List Byte) : + result p (initialHash p password salt secret ad) = derive p password salt secret ad := by + unfold result derive + rw [Proof.Argon2.iterations_fill] + +end VG.Proof.Argon2.X86_64.InitFill diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFillCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFillCT.lean new file mode 100644 index 000000000..0e13dc05c --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFillCT.lean @@ -0,0 +1,90 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.InitFill +import VerifiedGarbage.Proof.Argon2.X86_64.FillFinishCT + +/-! The entire post-H₀ pipeline leaks only the reviewed complete filling reference log. -/ + +namespace VG.Proof.Argon2.X86_64.InitFill + +open VG VG.X86_64 VG.Spec.Argon2 +open VG.Spec.Blake2 (bytesAt) + +def initial (p : Params) (s : State) : FillState := initMemory p (bytesAt s.mem (s.gpr .rbp) 64) + +structure Related (p : Params) (s t : State) : Prop where + left : Ready p s + right : Ready p t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + matrices : FillKernel.matrix s = FillKernel.matrix t + outputs : FinalOutput.output s = FinalOutput.output t + works : FinalOutput.work s = FinalOutput.work t + indices : (Proof.Argon2.iterations p 0 p.passes (initial p s)).indices = + (Proof.Argon2.iterations p 0 p.passes (initial p t)).indices + +theorem code_rel (v : Proof.Blake2.X86_64.Backend) (name : String) (p : Params) : + RelCT isa (Related p) (Impl.Argon2.X86_64.InitFill.code name (HPrime.hash v)) (fun _ _ => True) := by + intro s t ts tt a b hp ea eb + have params := hp.left.environment.parameters + have q : 2 ≤ p.laneLen := by + have segments := Proof.Argon2.laneLen_segments p params.lanesPositive + have minimum := params.segment_bound.1 + omega + have lanesBound : p.lanes < 2 ^ 64 := Nat.lt_trans params.lanesBound (by decide) + have pub : MemoryInit.AgreeBases s t := by + intro r hr + simp only [MemoryInit.publicBases, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl + · exact hp.bases + · exact hp.left.scratch.trans (hp.works.trans hp.right.scratch.symm) + · exact hp.stacks + · exact hp.left.initializing.laneLength.trans hp.right.initializing.laneLength.symm + have rightReady : MemoryInit.Ready (FillKernel.matrix s) p.lanes p.laneLen t := by + rw [hp.matrices]; exact hp.right.initializing + cases ea with + | seq initA restA => + cases eb with + | seq initB restB => + have initTrace := MemoryInit.code_ct v name (FillKernel.matrix s) p.lanes p.laneLen + params.lanesPositive lanesBound q _ _ _ _ _ _ hp.left.initializing rightReady pub initA initB + obtain ⟨_, sa, runA, doneA⟩ := MemoryInit.complete_ok v name s (FillKernel.matrix s) p.lanes p.laneLen + hp.left.initializing params.lanesPositive lanesBound q + obtain ⟨_, sb, runB, doneB⟩ := MemoryInit.complete_ok v name t (FillKernel.matrix t) p.lanes p.laneLen + hp.right.initializing params.lanesPositive lanesBound q + obtain ⟨_, rfl⟩ := Exec.det initA runA + obtain ⟨_, rfl⟩ := Exec.det initB runB + cases restA with + | seq setupA fillA => + cases restB with + | seq setupB fillB => + have bases := doneA.bp.trans (hp.bases.trans doneB.bp.symm) + obtain ⟨setupTrace, _⟩ := FillSetup.code_rel _ _ _ _ _ _ bases setupA setupB + obtain ⟨_, ca, runA, preparedA⟩ := FillSetup.code_ok _ p (initialized_setup hp.left doneA) + obtain ⟨_, cb, runB, preparedB⟩ := FillSetup.code_ok _ p (initialized_setup hp.right doneB) + obtain ⟨_, rfl⟩ := Exec.det setupA runA + obtain ⟨_, rfl⟩ := Exec.det setupB runB + have preparedBases := (preparedA.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide) (by decide)).trans + (bases.trans (preparedB.regs .rbp (by simp [calleeSaved]) (by decide) (by decide) (by decide) (by decide)).symm) + have preparedStacks := (preparedA.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide) (by decide)).trans + ((doneA.sp.trans (hp.stacks.trans doneB.sp.symm)).trans + (preparedB.regs .rsp (by simp [calleeSaved]) (by decide) (by decide) (by decide) (by decide)).symm) + have initMatrices := (doneA.frame_word hp.left.initializing.space 232 (by decide) (Or.inr (by decide))).trans + (hp.matrices.trans (doneB.frame_word hp.right.initializing.space 232 (by decide) (Or.inr (by decide))).symm) + have matrices := preparedA.matrix.trans (initMatrices.trans preparedB.matrix.symm) + have initOutputs := (doneA.frame_word hp.left.initializing.space 256 (by decide) (Or.inr (by decide))).trans + (hp.outputs.trans (doneB.frame_word hp.right.initializing.space 256 (by decide) (Or.inr (by decide))).symm) + have outputs := (preparedA.words 256 (by decide) (by decide)).trans + (initOutputs.trans (preparedB.words 256 (by decide) (by decide)).symm) + have initWorks := (doneA.frame_word hp.left.initializing.space 248 (by decide) (Or.inr (by decide))).trans + (hp.works.trans (doneB.frame_word hp.right.initializing.space 248 (by decide) (Or.inr (by decide))).symm) + have works := (preparedA.words 248 (by decide) (by decide)).trans + (initWorks.trans (preparedB.words 248 (by decide) (by decide)).symm) + have related : FillFinish.Related p (initial p s) (initial p t) _ _ := + ⟨preparedA.finish_ready (initialized_setup hp.left doneA) (initialized_output hp.left doneA) hp.left.positive, + preparedB.finish_ready (initialized_setup hp.right doneB) (initialized_output hp.right doneB) hp.right.positive, + preparedBases, preparedStacks, matrices, outputs, works, + preparedA.represents (initialized_setup hp.left doneA) _ (initialized_represents hp.left doneA), + preparedB.represents (initialized_setup hp.right doneB) _ (initialized_represents hp.right doneB), hp.indices⟩ + obtain ⟨fillTrace, _⟩ := FillFinish.code_rel v name p (initial p s) (initial p t) _ _ _ _ _ _ related fillA fillB + exact ⟨by rw [initTrace, setupTrace, fillTrace], trivial⟩ + +end VG.Proof.Argon2.X86_64.InitFill diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFillFrames.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFillFrames.lean new file mode 100644 index 000000000..4c03ead9f --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFillFrames.lean @@ -0,0 +1,49 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.InitFillReady + +/-! Each stage writes only the matrix, hash scratch, output, call stack and local hash prefix. -/ + +namespace VG.Proof.Argon2.X86_64.InitFill + +open VG VG.X86_64 VG.Spec.Argon2 + +def writes (s : State) (p : Params) : List Region := + [⟨FillKernel.matrix s, p.blocks * 1024⟩, ⟨FinalOutput.work s, 16384⟩, + ⟨FinalOutput.output s, p.tagLen⟩, below (s.gpr .rsp) 24, ⟨s.gpr .rbp, 72⟩] + +theorem initialization_frame {s t : State} {p : Params} (h : Ready p s) + (done : MemoryInit.Done s t (FillKernel.matrix s) p.lanes p.laneLen) : Frame (writes s p) s.mem t.mem := by + apply done.frame.sub + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl + · have blocks := Proof.Argon2.blocks_lanes p h.environment.parameters.lanesPositive + exact ⟨⟨FillKernel.matrix s, p.blocks * 1024⟩, by simp [writes], by rw [blocks, Nat.mul_comm 1024]; intro _ h; exact h⟩ + · exact ⟨⟨FinalOutput.work s, 16384⟩, by simp [writes], by rw [h.scratch]; intro _ h; exact h⟩ + · exact ⟨_, by simp [writes], fun _ h => h⟩ + · exact ⟨⟨s.gpr .rbp, 72⟩, by simp [writes], Offset.sub_base _ (by decide)⟩ + +theorem setup_frame {s t : State} {p : Params} (h : Frame [⟨s.gpr .rbp, 8⟩] s.mem t.mem) : + Frame (writes s p) s.mem t.mem := by + apply h.sub + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact ⟨⟨s.gpr .rbp, 72⟩, by simp [writes], Region.sub_prefix (by decide)⟩ + +theorem filling_frame {s t : State} {p : Params} (positive : 0 < p.blocks) (h : Frame (FillFinish.writes s p) s.mem t.mem) : + Frame (writes s p) s.mem t.mem := by + apply h.sub + intro r hr + simp only [FillFinish.writes, FillIterations.writes, Finish.writes, + List.mem_append, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with (rfl | rfl | rfl | rfl) | (rfl | rfl | rfl | rfl) + · exact ⟨_, by simp [writes], fun _ h => h⟩ + · exact ⟨⟨FinalOutput.work s, 16384⟩, by simp [writes], Region.sub_prefix (by decide)⟩ + · exact ⟨below (s.gpr .rsp) 24, by simp [writes], below_sub (by decide) (by decide)⟩ + · exact ⟨⟨s.gpr .rbp, 72⟩, by simp [writes], Region.sub_prefix (by decide)⟩ + · exact ⟨⟨FillKernel.matrix s, p.blocks * 1024⟩, by simp [writes], Region.sub_prefix (by omega)⟩ + · exact ⟨_, by simp [writes], fun _ h => h⟩ + · exact ⟨_, by simp [writes], fun _ h => h⟩ + · exact ⟨_, by simp [writes], fun _ h => h⟩ + +end VG.Proof.Argon2.X86_64.InitFill diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFillReady.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFillReady.lean new file mode 100644 index 000000000..52f6fada7 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitFillReady.lean @@ -0,0 +1,77 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.MemoryInitDone +import VerifiedGarbage.Proof.Argon2.X86_64.FillSetupFinish + +/-! Retain the filling environment and final-call layout across memory initialization. -/ + +namespace VG.Proof.Argon2.X86_64.InitFill + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Ready (p : Params) (s : State) : Prop where + initializing : MemoryInit.Ready (FillKernel.matrix s) p.lanes p.laneLen s + environment : FillSetup.Environment p s + output : FinalOutput.Ready p s + positive : 0 < p.passes + scratch : s.gpr .rbx = FinalOutput.work s + +theorem initialized_environment {s t : State} {p : Params} (h : Ready p s) + (done : MemoryInit.Done s t (FillKernel.matrix s) p.lanes p.laneLen) : FillSetup.Environment p t := by + have base : FillKernel.matrix t = FillKernel.matrix s := done.frame_word h.initializing.space 232 (by decide) (Or.inr (by decide)) + have work : AddressCalls.work t = AddressCalls.work s := done.frame_word h.initializing.space 248 (by decide) (Or.inr (by decide)) + have e := h.environment + refine ⟨e.parameters, e.passesBound, e.layout.of_preserved done.bp done.sp base work done.rd done.wr, + ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · constructor + · rw [done.rd, done.wr, done.bp]; exact e.addressLayout.frameRead + · rw [done.wr, work]; exact e.addressLayout.workWrite + · rw [done.bp, work]; exact e.addressLayout.frameWork + · rw [done.bp, done.sp]; exact e.addressLayout.frameStack + · rw [done.sp, work]; exact e.addressLayout.stackWork + · rw [done.rd, done.wr, done.bp]; exact e.reads + · rw [done.wr, done.bp]; exact e.counterWrite + · rw [done.wr, done.bp]; exact e.passWrite + · rw [base, work]; exact e.matrixWork + · exact (done.frame_word h.initializing.space 240 (by decide) (Or.inr (by decide))).trans e.blocksWord + · exact (done.frame_word h.initializing.space 72 (by decide) (Or.inr (by decide))).trans e.passesWord + · exact (done.frame_word h.initializing.space 112 (by decide) (Or.inr (by decide))).trans e.variantWord + · exact (done.frame_word h.initializing.space 184 (by decide) (Or.inr (by decide))).trans e.lanesWord + +theorem initialized_output {s t : State} {p : Params} (h : Ready p s) + (done : MemoryInit.Done s t (FillKernel.matrix s) p.lanes p.laneLen) : FinalOutput.Ready p t := by + have base : ReductionState.matrix t = ReductionState.matrix s := done.frame_word h.initializing.space 232 (by decide) (Or.inr (by decide)) + have output : FinalOutput.output t = FinalOutput.output s := done.frame_word h.initializing.space 256 (by decide) (Or.inr (by decide)) + have work : FinalOutput.work t = FinalOutput.work s := done.frame_word h.initializing.space 248 (by decide) (Or.inr (by decide)) + refine ⟨h.output.positive, h.output.bound, ?_, + (done.frame_word h.initializing.space 264 (by decide) (Or.inr (by decide))).trans h.output.tagWord, + ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · rw [done.rd, done.wr, done.bp]; exact h.output.reads + · rw [base, done.rd, done.wr]; exact h.output.input + · rw [output, done.wr]; exact h.output.outputWrite + · rw [work, done.wr]; exact h.output.workWrite + · rw [base, work]; exact h.output.inputWork + · rw [output, work]; exact h.output.outputWork + · rw [done.sp, base]; exact h.output.stackInput + · rw [done.sp, output]; exact h.output.stackOutput + · rw [done.sp, work]; exact h.output.stackWork + +theorem initialized_setup {s t : State} {p : Params} (h : Ready p s) + (done : MemoryInit.Done s t (FillKernel.matrix s) p.lanes p.laneLen) : FillSetup.Ready p t := by + have params := h.environment.parameters + have product : p.laneLen ≤ p.lanes * p.laneLen := by + simpa only [Nat.one_mul] using Nat.mul_le_mul_right p.laneLen (show 1 ≤ p.lanes from params.lanesPositive) + refine ⟨initialized_environment h done, ?_, done.stride⟩ + have bound := h.initializing.space.bound + have bytes := Nat.mul_le_mul_left 1024 product + omega + +theorem initialized_represents {s t : State} {p : Params} (h : Ready p s) + (done : MemoryInit.Done s t (FillKernel.matrix s) p.lanes p.laneLen) : + Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks + (initMemory p (Spec.Blake2.bytesAt s.mem (s.gpr .rbp) 64)).memory := by + have base : FillKernel.matrix t = FillKernel.matrix s := done.frame_word h.initializing.space 232 (by decide) (Or.inr (by decide)) + rw [base] + have segments := Proof.Argon2.laneLen_segments p h.environment.parameters.lanesPositive + have minimum := h.environment.parameters.segment_bound.1 + exact done.initialized.represents h.environment.parameters.lanesPositive (by omega) + +end VG.Proof.Argon2.X86_64.InitFill diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/MemoryInit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/MemoryInit.lean index eaf7c8ff6..a2d02db7e 100644 --- a/lean/VerifiedGarbage/Proof/Argon2/X86_64/MemoryInit.lean +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/MemoryInit.lean @@ -40,7 +40,7 @@ theorem code_ok (v : Proof.Blake2.X86_64.Backend) (name : String) WP isa (code name (HPrime.hash v)) s fun t => Initialized t.mem memory lanes q lanes (bytesAt s.mem (s.gpr .rbp) 64) ∧ t.gpr .rbp = s.gpr .rbp ∧ t.gpr .rbx = s.gpr .rbx ∧ t.gpr .rsp = s.gpr .rsp ∧ - t.rd = s.rd ∧ t.wr = s.wr ∧ + t.gpr .r13 = BitVec.ofNat 64 (1024 * q) ∧ t.rd = s.rd ∧ t.wr = s.wr ∧ Frame [⟨memory, 1024 * (lanes * q)⟩, ⟨s.gpr .rbx, 16384⟩, below (s.gpr .rsp) 24, ⟨s.gpr .rbp + 64, 8⟩] s.mem t.mem := by unfold code lanesSetupCode @@ -81,7 +81,7 @@ theorem code_ok (v : Proof.Blake2.X86_64.Backend) (name : String) · intro t ht refine ⟨ht.initialized, ht.keeps.rbp.trans (bpB.trans bpA), ht.keeps.rbx.trans (bxB.trans bxA), ht.keeps.rsp.trans (spB.trans spA), - ht.keeps.rd.trans (hb.rd.trans ha.rd), ht.keeps.wr.trans (hb.wr.trans ha.wr), ?_⟩ + (ht.keeps.regs .r13 (by decide)).trans hb.stride, ht.keeps.rd.trans (hb.rd.trans ha.rd), ht.keeps.wr.trans (hb.wr.trans ha.wr), ?_⟩ have fb : Frame [⟨memory, 1024 * (lanes * q)⟩, ⟨s.gpr .rbx, 16384⟩, below (s.gpr .rsp) 24, ⟨s.gpr .rbp + 64, 8⟩] s.mem b.mem := by rw [hb.mem] diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/MemoryInitDone.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/MemoryInitDone.lean new file mode 100644 index 000000000..b5d875a33 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/MemoryInitDone.lean @@ -0,0 +1,45 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.MemoryInitCT +import VerifiedGarbage.Proof.Argon2.X86_64.MemoryInitRepresent + +/-! Initialization retains its byte stride and every public frame word outside its lane suffix. -/ + +namespace VG.Proof.Argon2.X86_64.MemoryInit + +open VG VG.X86_64 VG.Spec.Argon2 +open VG.Spec.Blake2 (bytesAt) + +structure Done (s t : State) (memory : Addr) (lanes q : Nat) : Prop where + initialized : Initialized t.mem memory lanes q lanes (bytesAt s.mem (s.gpr .rbp) 64) + bp : t.gpr .rbp = s.gpr .rbp + bx : t.gpr .rbx = s.gpr .rbx + sp : t.gpr .rsp = s.gpr .rsp + stride : t.gpr .r13 = BitVec.ofNat 64 (1024 * q) + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨memory, 1024 * (lanes * q)⟩, ⟨s.gpr .rbx, 16384⟩, + below (s.gpr .rsp) 24, ⟨s.gpr .rbp + 64, 8⟩] s.mem t.mem + +theorem complete_ok (v : Proof.Blake2.X86_64.Backend) (name : String) (s : State) + (memory : Addr) (lanes q : Nat) (ready : Ready memory lanes q s) + (positive : 1 ≤ lanes) (lanesBound : lanes < 2 ^ 64) (minimum : 2 ≤ q) : + WP isa (Impl.Argon2.X86_64.MemoryInit.code name (HPrime.hash v)) s (Done s · memory lanes q) := + (code_ok v name s memory lanes q positive minimum lanesBound ready.space ready.memoryRead ready.lanesRead + ready.blocksRead ready.memoryWord ready.lanesWord ready.blocksWord ready.laneLength).mono + (fun _ h => ⟨h.1, h.2.1, h.2.2.1, h.2.2.2.1, h.2.2.2.2.1, h.2.2.2.2.2.1, h.2.2.2.2.2.2.1, h.2.2.2.2.2.2.2⟩) + +theorem Done.frame_word {s t : State} {memory : Addr} {lanes q : Nat} + (space : Space s memory (1024 * (lanes * q))) (done : Done s t memory lanes q) + (d : Nat) (bound : d + 8 ≤ 272) (separate : d + 8 ≤ 64 ∨ 72 ≤ d) : + t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 d) 64 = s.mem.readW (s.gpr .rbp + BitVec.ofNat 64 d) 64 := by + rw [done.bp] + have sub : Region.Sub ⟨s.gpr .rbp + BitVec.ofNat 64 d, 8⟩ ⟨s.gpr .rbp, 272⟩ := Offset.sub_base _ bound + exact done.frame.readW (r := ⟨s.gpr .rbp + BitVec.ofNat 64 d, 8⟩) (Region.contains_self _ _) (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl + · exact space.frameMatrix.sub_left sub + · exact space.frameWork.sub_left sub + · exact space.stackFrame.symm.sub_left sub + · exact Offset.disjoint (s.gpr .rbp) separate (by omega) (by decide)) (by decide) + +end VG.Proof.Argon2.X86_64.MemoryInit diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/MemoryInitRepresent.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/MemoryInitRepresent.lean new file mode 100644 index 000000000..6ff514d58 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/MemoryInitRepresent.lean @@ -0,0 +1,21 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.MemoryInit +import VerifiedGarbage.Proof.Argon2.Matrix + +/-! Memory initialization establishes the shared matrix representation invariant. -/ + +namespace VG.Proof.Argon2.X86_64.MemoryInit + +open VG VG.Spec.Argon2 + +theorem Initialized.represents {m : Mem} {base : Addr} {p : Params} {h0 : List Byte} + (positive : 0 < p.lanes) (lanePositive : 0 < p.laneLen) + (h : Initialized m base p.lanes p.laneLen p.lanes h0) : + Proof.Argon2.Represents m base p.blocks (initMemory p h0).memory := by + refine ⟨Proof.Argon2.initMemory_size p h0, ?_⟩ + intro k hk + rw [Array.getElem?_eq_getElem (by rw [Proof.Argon2.initMemory_size]; exact hk), Option.getD_some] + unfold Proof.Argon2.matrixCell + rw [Nat.mul_comm k 1024] + exact h.spec positive lanePositive k hk + +end VG.Proof.Argon2.X86_64.MemoryInit diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/Parameters.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/Parameters.lean new file mode 100644 index 000000000..674a731e9 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/Parameters.lean @@ -0,0 +1,50 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ParametersSteps + +/-! Exact rounded lane length using the verified fixed-time divider. -/ + +namespace VG.Proof.Argon2.X86_64.Parameters + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Ready (p : Params) (s : State) : Prop where + memoryRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 176) 8 + lanesRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 184) 8 + memoryWord : s.mem.readW (off (s.gpr .rbp) 176) 64 = BitVec.ofNat 64 p.memory + lanesWord : s.mem.readW (off (s.gpr .rbp) 184) 64 = BitVec.ofNat 64 p.lanes + positive : 0 < p.lanes + memoryBound : p.memory < 2 ^ 32 + lanesBound : p.lanes < 2 ^ 24 + +def changed : List Reg := [.rdi, .rsi] ++ Divide.changed ++ [.r13] + +theorem code_ok (s : State) (p : Params) (h : Ready p s) : + WP isa Impl.Argon2.X86_64.Parameters.code s fun t => + t.gpr .r13 = BitVec.ofNat 64 p.laneLen ∧ Divide.Keeps changed s t := by + unfold Impl.Argon2.X86_64.Parameters.code + refine WP.seq ((args_ok s h.memoryRead h.lanesRead).mono ?_) + rintro a ⟨memory, lanes, ka⟩ + have divisor : a.gpr .rsi = BitVec.ofNat 64 (4 * p.lanes) := by + rw [lanes, h.lanesWord, show (4 : Addr) = BitVec.ofNat 64 4 from rfl, ← BitVec.ofNat_mul, Nat.mul_comm] + have n : (a.gpr .rdi).toNat = p.memory := by + rw [memory, h.memoryWord, BitVec.toNat_ofNat, Nat.mod_eq_of_lt (Nat.lt_trans h.memoryBound (by decide))] + have bound : 4 * p.lanes < 2 ^ 32 := by have lanesBound := h.lanesBound; omega + have d : (a.gpr .rsi).toNat = 4 * p.lanes := by + rw [divisor, BitVec.toNat_ofNat, Nat.mod_eq_of_lt (Nat.lt_trans bound (by decide))] + refine WP.seq ((Divide.code_ok a (by rw [n]; exact h.memoryBound) + (by rw [d]; have positive := h.positive; omega) (by rw [d]; exact bound)).mono ?_) + rintro b ⟨quotient, _, kb⟩ + rw [n, d] at quotient + have word : b.gpr .r9 = BitVec.ofNat 64 (p.memory / (4 * p.lanes)) := by + rw [← quotient] + simp only [BitVec.ofNat_toNat, BitVec.setWidth_eq] + refine (finish_ok b).mono ?_ + rintro t ⟨value, kt⟩ + refine ⟨?_, (ka.mono (by simp [changed])).trans + ((kb.mono (by + intro r hr + simp only [changed, List.mem_append, List.mem_cons, List.not_mem_nil, or_false] + exact Or.inl (Or.inr hr))).trans (kt.mono (by simp [changed])))⟩ + rw [value, word, show (4 : Addr) = BitVec.ofNat 64 4 from rfl, ← BitVec.ofNat_mul, + Nat.mul_comm, ← Proof.Argon2.laneLen_eq p h.positive] + +end VG.Proof.Argon2.X86_64.Parameters diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ParametersCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ParametersCT.lean new file mode 100644 index 000000000..7d8b68256 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ParametersCT.lean @@ -0,0 +1,16 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ParametersLit +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! Rounded-memory computation has a fixed trace, reading only public frame addresses. -/ + +namespace VG.Proof.Argon2.X86_64.Parameters + +open VG VG.X86_64 + +theorem code_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + Impl.Argon2.X86_64.Parameters.code (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr; simp only [List.mem_cons, List.not_mem_nil, or_false] at hr; subst r; exact h)) (by taint_decide) + +end VG.Proof.Argon2.X86_64.Parameters diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ParametersLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ParametersLit.lean new file mode 100644 index 000000000..c25b17b49 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ParametersLit.lean @@ -0,0 +1,10 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.Parameters +import VerifiedGarbage.Proof.Framework.X86_64.Lit + +/-! Checked literal of rounded-memory parameter computation. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.Parameters.code + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ParametersSteps.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ParametersSteps.lean new file mode 100644 index 000000000..0efecf6f4 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ParametersSteps.lean @@ -0,0 +1,46 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.Parameters +import VerifiedGarbage.Proof.Argon2.X86_64.Divide +import VerifiedGarbage.Proof.Argon2.X86_64.DivideCT +import VerifiedGarbage.Proof.Argon2.X86_64.Initialize +import VerifiedGarbage.Proof.Argon2.Dimensions + +/-! Public frame loads and fixed arithmetic for the RFC's rounded memory dimensions. -/ + +namespace VG.Proof.Argon2.X86_64.Parameters + +open VG VG.X86_64 + +theorem args_ok (s : State) + (memoryRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 176) 8) + (lanesRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 184) 8) : + WP isa (.block Impl.Argon2.X86_64.Parameters.args) s fun t => + t.gpr .rdi = s.mem.readW (off (s.gpr .rbp) 176) 64 ∧ + t.gpr .rsi = (s.mem.readW (off (s.gpr .rbp) 184) 64) * 4 ∧ Divide.Keeps [.rdi, .rsi] s t := by + apply WP.of_runBlock + simp only [Impl.Argon2.X86_64.Parameters.args, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + execAlu, State.load64, ea_at, RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, + RegUpd.mem_setReg, RegUpd.rd_setReg, RegUpd.wr_setReg, memoryRead, lanesRead, reduceCtorEq, ite_true, ite_false, Option.map_some, Option.bind_some, + Option.some.injEq, exists_eq_left'] + refine ⟨trivial, ?_, ?_⟩ + · simp only [show (4 : Addr) = 2#64 + 2#64 from rfl, BitVec.mul_add, BitVec.mul_two] + · constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr.1, hr.2, ite_false] + all_goals rfl + +theorem finish_ok (s : State) : WP isa (.block Impl.Argon2.X86_64.Parameters.finish) s fun t => + t.gpr .r13 = s.gpr .r9 * 4 ∧ Divide.Keeps [.r13] s t := by + apply WP.of_runBlock + simp only [Impl.Argon2.X86_64.Parameters.finish, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, execAlu, + RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, ite_true, Option.map_some, Option.bind_some, + Option.some.injEq, exists_eq_left'] + refine ⟨?_, ?_⟩ + · simp only [show (4 : Addr) = 2#64 + 2#64 from rfl, BitVec.mul_add, BitVec.mul_two] + · constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr, ite_false] + all_goals rfl + +end VG.Proof.Argon2.X86_64.Parameters diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSource.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSource.lean new file mode 100644 index 000000000..71754ca3a --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSource.lean @@ -0,0 +1,100 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.RandomSourcePrepare +import VerifiedGarbage.Proof.Argon2.X86_64.DependentWordState + +/-! Both random sources satisfy the same filling-step postcondition. -/ + +namespace VG.Proof.Argon2.X86_64.RandomSource + +open VG VG.X86_64 VG.Spec.Argon2 + +def writes (s : State) : List Region := AddressCache.writes s + +structure Done (s t : State) (p : Params) (pass lane slice index : Nat) (state : FillState) : Prop where + random : t.gpr .rdi = Proof.Argon2.FillStep.random p pass lane slice index state.memory + ready : ∃ old, Ready p pass lane slice index old t + represented : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks state.memory + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (writes s) s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + +theorem Ready.index_nat {p : Params} {pass lane slice index old : Nat} {s : State} + (h : Ready p pass lane slice index old s) : (s.gpr .r15).toNat = index := by + rw [h.filling.position.index, ReferenceMap.word_nat index h.filling.bounds.index_bound64] + +theorem independent_ok (s : State) (p : Params) (pass lane slice index old : Nat) + (h : Ready p pass lane slice index old s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) + (mode : independent p pass slice = true) : + WP isa Impl.Argon2.X86_64.AddressCache.code s (Done s · p pass lane slice index state) := by + refine (AddressCache.code_ok p pass lane slice old s h.cache.ready).mono ?_ + intro t done + have base : FillKernel.matrix t = FillKernel.matrix s := + done.selected.frame_word h.cache.layout 232 (by decide) (by decide) + have nextReady : Ready p pass lane slice index (AddressCache.wanted s) t := by + refine ⟨done.selected.filling_ready h.cache.layout h.filling, + done.selected.invariant h.cache.ready, ?_⟩ + rw [base, done.selected.work_eq]; exact h.matrixWork + refine ⟨?_, ⟨_, nextReady⟩, + done.selected.represents h.cache.layout h.filling h.matrixWork state.memory represented, + done.selected.regs, done.selected.rd, done.selected.wr, done.selected.frame, done.selected.mxcsr⟩ + have random := done.random + unfold AddressCache.wanted at random + rw [h.index_nat] at random + unfold Proof.Argon2.FillStep.random + simp only [mode, ite_true] + exact random + +theorem dependent_ok (s : State) (p : Params) (pass lane slice index old : Nat) + (h : Ready p pass lane slice index old s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) + (mode : independent p pass slice = false) : + WP isa Impl.Argon2.X86_64.DependentWord.code s (Done s · p pass lane slice index state) := by + refine (DependentWord.state_ok s p pass lane slice index h.filling state represented mode).mono ?_ + rintro t ⟨random, _, matrix, keeps⟩ + refine ⟨random, ⟨old, h.of_keeps keeps⟩, matrix, ?_, keeps.rd, keeps.wr, ?_, keeps.mxcsr⟩ + · intro r hr + apply keeps.regs + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + · rw [keeps.mem]; exact Frame.refl _ _ + +theorem code_ok (s : State) (p : Params) (pass lane slice index old : Nat) + (h : Ready p pass lane slice index old s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) : + WP isa Impl.Argon2.X86_64.RandomSource.code s (Done s · p pass lane slice index state) := by + unfold Impl.Argon2.X86_64.RandomSource.code + refine WP.seq ((prepare_ok s p pass lane slice index old h).mono ?_) + rintro a ⟨flag, keeps⟩ + have next := h.of_keeps keeps + have representedA : Proof.Argon2.Represents a.mem (FillKernel.matrix a) p.blocks state.memory := by + have base : FillKernel.matrix a = FillKernel.matrix s := by + unfold FillKernel.matrix; rw [keeps.mem, keeps.regs .rbp (by decide)] + rw [base, keeps.mem]; exact represented + have finish {t : State} (done : Done a t p pass lane slice index state) : + Done s t p pass lane slice index state := by + refine ⟨done.random, done.ready, done.represented, ?_, done.rd.trans keeps.rd, + done.wr.trans keeps.wr, ?_, done.mxcsr.trans keeps.mxcsr⟩ + · intro r hr + have ne : r ∉ ReferenceMap.changed := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + exact (done.regs r hr).trans (keeps.regs r ne) + · have frame := done.frame + unfold writes AddressCache.writes AddressCalls.work at frame ⊢ + rw [keeps.mem, keeps.regs .rbp (by decide), keeps.regs .rsp (by decide)] at frame + exact frame + refine WP.ite (!independent p pass slice) (by simp only [eval, flag]) ?_ ?_ + · intro mode + have dependent : independent p pass slice = false := by + cases eq : independent p pass slice <;> simp_all + exact (dependent_ok a p pass lane slice index old next state representedA dependent).mono + (fun _ done => finish done) + · intro mode + have independent : independent p pass slice = true := by + cases eq : independent p pass slice <;> simp_all + exact (independent_ok a p pass lane slice index old next state representedA independent).mono + (fun _ done => finish done) + +end VG.Proof.Argon2.X86_64.RandomSource diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourceCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourceCT.lean new file mode 100644 index 000000000..ea40dba74 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourceCT.lean @@ -0,0 +1,74 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.RandomSourcePrepare +import VerifiedGarbage.Proof.Argon2.X86_64.AddressModeCT +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheSelectCT +import VerifiedGarbage.Proof.Argon2.X86_64.DependentWordCT + +/-! Source dispatch and cached-word selection use only public addresses and guards. -/ + +namespace VG.Proof.Argon2.X86_64.RandomSource + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.RandomSource + +structure Related (p : Params) (pass lane slice index old : Nat) (s t : State) : Prop where + left : Ready p pass lane slice index old s + right : Ready p pass lane slice index old t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + matrices : FillKernel.matrix s = FillKernel.matrix t + work : AddressCalls.work s = AddressCalls.work t + +theorem Related.of_keeps {p : Params} {pass lane slice index old : Nat} {s t a b : State} + (h : Related p pass lane slice index old s t) + (ka : Divide.Keeps ReferenceMap.changed s a) (kb : Divide.Keeps ReferenceMap.changed t b) : + Related p pass lane slice index old a b := by + refine ⟨h.left.of_keeps ka, h.right.of_keeps kb, ?_, ?_, ?_, ?_⟩ + · rw [ka.regs .rbp (by decide), kb.regs .rbp (by decide)]; exact h.bases + · rw [ka.regs .rsp (by decide), kb.regs .rsp (by decide)]; exact h.stacks + · unfold FillKernel.matrix + rw [ka.mem, kb.mem, ka.regs .rbp (by decide), kb.regs .rbp (by decide)] + exact h.matrices + · unfold AddressCalls.work + rw [ka.mem, kb.mem, ka.regs .rbp (by decide), kb.regs .rbp (by decide)] + exact h.work + +theorem test_rel : RelCT isa (fun _ _ : State => True) (.block test) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by intro r hr; simp at hr)) (by taint_decide) + +theorem prepare_rel (p : Params) (pass lane slice index old : Nat) : + RelCT isa (Related p pass lane slice index old) prepare + (fun s t => Related p pass lane slice index old s t ∧ s.zf = t.zf) := by + have trace := AddressMode.code_rel.seq test_rel + have narrowed := trace.mono (P' := Related p pass lane slice index old) + (fun _ _ h => h.bases) (fun _ _ h => h) + have full := narrowed.wpDep (fun s t h => + ⟨prepare_ok s p pass lane slice index old h.left, + prepare_ok t p pass lane slice index old h.right⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ⟨fa, ka⟩, ⟨fb, kb⟩⟩ := h + exact ⟨hp.of_keeps ka kb, fa.trans fb.symm⟩ + +theorem Related.cache {p : Params} {pass lane slice index old : Nat} {s t : State} + (h : Related p pass lane slice index old s t) : AddressCache.ReadyRelated p pass lane slice old s t := by + refine ⟨h.left.cache.ready, h.right.cache.ready, + ⟨⟨⟨h.left.cache.layout, h.right.cache.layout, h.bases, h.stacks, h.work⟩, + h.left.cache.reads, h.right.cache.reads⟩, ?_, ?_, h.left.cache.write, h.right.cache.write⟩⟩ + · exact h.left.filling.position.index.trans h.right.filling.position.index.symm + · exact h.left.cache.words.counterWord.trans h.right.cache.words.counterWord.symm + +theorem code_rel (p : Params) (pass lane slice index old : Nat) : + RelCT isa (Related p pass lane slice index old) code (fun _ _ => True) := by + have branches : RelCT isa + (fun s t => Related p pass lane slice index old s t ∧ s.zf = t.zf) + (.ite .e Impl.Argon2.X86_64.DependentWord.code Impl.Argon2.X86_64.AddressCache.code) + (fun _ _ => True) := by + apply RelCT.ite (by intro s t h; simp only [eval, h.2]) + · exact (DependentWord.code_rel p pass lane slice index).mono + (fun _ _ h => ⟨h.1.1.left.filling, h.1.1.right.filling, h.1.1.bases, h.1.1.matrices⟩) + (fun _ _ h => h) + · exact (AddressCache.code_rel p pass lane slice old).mono + (fun _ _ h => h.1.1.cache) (fun _ _ h => h) + exact (prepare_rel p pass lane slice index old).seq branches + +end VG.Proof.Argon2.X86_64.RandomSource diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourceCounter.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourceCounter.lean new file mode 100644 index 000000000..1668022b5 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourceCounter.lean @@ -0,0 +1,37 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.RandomSource + +/-! The stored address counter remains public after either source. -/ + +namespace VG.Proof.Argon2.X86_64.RandomSource + +open VG VG.X86_64 VG.Spec.Argon2 + +def counterValue (p : Params) (pass slice index old : Nat) : Addr := + BitVec.ofNat 64 (if independent p pass slice then index / 128 + 1 else old) + +theorem counter_ok (s : State) (p : Params) (pass lane slice index old : Nat) + (h : Ready p pass lane slice index old s) : + WP isa Impl.Argon2.X86_64.RandomSource.code s fun t => + t.mem.readW (off (t.gpr .rbp) 8) 64 = counterValue p pass slice index old := by + unfold Impl.Argon2.X86_64.RandomSource.code + refine WP.seq ((prepare_ok s p pass lane slice index old h).mono ?_) + rintro a ⟨flag, keeps⟩ + have next := h.of_keeps keeps + refine WP.ite (!independent p pass slice) (by simp only [eval, flag]) ?_ ?_ + · intro mode + have dependent : independent p pass slice = false := by cases eq : independent p pass slice <;> simp_all + refine (DependentWord.code_ok a p pass lane slice index next.filling).mono ?_ + rintro t ⟨_, saved⟩ + unfold counterValue + simp only [dependent] + rw [saved.mem, saved.regs .rbp (by decide)] + exact next.cache.words.counterWord + · intro mode + have independent : independent p pass slice = true := by cases eq : independent p pass slice <;> simp_all + refine (AddressCache.code_ok p pass lane slice old a next.cache.ready).mono ?_ + intro t done + unfold counterValue + simp only [independent, ite_true] + rw [done.selected.counterWord, AddressCache.counter_nat, next.index_nat] + +end VG.Proof.Argon2.X86_64.RandomSource diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourcePrepare.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourcePrepare.lean new file mode 100644 index 000000000..366a67562 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourcePrepare.lean @@ -0,0 +1,60 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.RandomSource +import VerifiedGarbage.Proof.Argon2.X86_64.AddressMode +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheInvariant +import VerifiedGarbage.Proof.Argon2.X86_64.AddressCacheMatrix + +/-! Retain the source invariants while selecting the public addressing mode. -/ + +namespace VG.Proof.Argon2.X86_64.RandomSource + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.RandomSource + +structure Ready (p : Params) (pass lane slice index old : Nat) (s : State) : Prop where + filling : FillKernel.Ready p pass lane slice index s + cache : AddressCache.Invariant p pass lane slice old s + matrixWork : (⟨FillKernel.matrix s, p.blocks * 1024⟩ : Region).Disjoint ⟨AddressCalls.work s, 8192⟩ + +theorem Ready.of_keeps {p : Params} {pass lane slice index old : Nat} {s t : State} + (h : Ready p pass lane slice index old s) (k : Divide.Keeps ReferenceMap.changed s t) : + Ready p pass lane slice index old t := by + refine ⟨h.filling.of_keeps k, h.cache.of_keeps k, ?_⟩ + have matrix : FillKernel.matrix t = FillKernel.matrix s := by + unfold FillKernel.matrix; rw [k.mem, k.regs .rbp (by decide)] + have work : AddressCalls.work t = AddressCalls.work s := by + unfold AddressCalls.work; rw [k.mem, k.regs .rbp (by decide)] + rw [matrix, work]; exact h.matrixWork + +theorem test_ok (s : State) : WP isa (.block test) s fun t => + t.zf = decide (s.gpr .r10 = 0#64) ∧ Divide.Keeps [] s t := by + apply WP.of_runBlock + simp only [test, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, execAlu, + RegUpd.zf_arithFlags, show BitVec.signExtend 64 (0 : BitVec 32) = (0 : Addr) from rfl, + Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨?_, ?_⟩ + · change (s.gpr .r10 - 0#64 == 0#64) = decide (s.gpr .r10 = 0#64) + rw [BitVec.sub_zero] + apply Bool.eq_iff_iff.mpr + simp only [beq_iff_eq, decide_eq_true_eq] + constructor + · intro r _; exact congrFun (RegUpd.gpr_arithFlags _ _ _ _) r + all_goals rfl + +theorem bool_zero : ∀ b : Bool, decide ((BitVec.ofBool b).setWidth 64 = 0#64) = !b := by + decide +kernel + +theorem prepare_ok (s : State) (p : Params) (pass lane slice index old : Nat) + (h : Ready p pass lane slice index old s) : WP isa prepare s fun t => + t.zf = !independent p pass slice ∧ Divide.Keeps ReferenceMap.changed s t := by + unfold prepare + refine WP.seq ((AddressMode.code_spec_ok s p pass slice + (h.cache.reads 112 (by simp)) (h.cache.reads 0 (by simp)) + h.cache.words.variantWord h.filling.passWord h.filling.position.slice + (Nat.lt_trans h.filling.bounds.passBound (by decide)) + (Nat.lt_trans h.filling.bounds.sliceBound (by decide))).mono ?_) + rintro a ⟨mode, keeps⟩ + refine (test_ok a).mono ?_ + rintro t ⟨flag, tested⟩ + refine ⟨?_, (keeps.mono (by decide)).trans (tested.mono (by decide))⟩ + rw [flag, mode, bool_zero] + +end VG.Proof.Argon2.X86_64.RandomSource diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourceState.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourceState.lean new file mode 100644 index 000000000..5be3d8155 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/RandomSourceState.lean @@ -0,0 +1,23 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.RandomSource + +/-! Frame words and public allocation pointers survive random-word dispatch. -/ + +namespace VG.Proof.Argon2.X86_64.RandomSource + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem Done.frame_word {s t : State} {p : Params} {pass lane slice index old : Nat} {state : FillState} + (h : Ready p pass lane slice index old s) (done : Done s t p pass lane slice index state) + (d : Nat) (bound : d + 8 ≤ 272) (separate : d + 8 ≤ 8 ∨ 16 ≤ d) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [done.regs .rbp (by simp [calleeSaved])] + have sub : Region.Sub ⟨off (s.gpr .rbp) d, 8⟩ ⟨s.gpr .rbp, 272⟩ := Offset.sub_base _ bound + exact done.frame.readW (r := ⟨off (s.gpr .rbp) d, 8⟩) (Region.contains_self _ _) (by + intro r hr + simp only [writes, AddressCache.writes, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · exact h.cache.layout.frameWork.sub_left sub + · exact h.cache.layout.frameStack.sub_left sub + · exact Offset.disjoint _ separate (by omega) (by decide)) (by decide) + +end VG.Proof.Argon2.X86_64.RandomSource diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceBlock.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceBlock.lean new file mode 100644 index 000000000..2407b966b --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceBlock.lean @@ -0,0 +1,34 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ReduceBlock +import VerifiedGarbage.Proof.Argon2.X86_64.FillWriteCover +import VerifiedGarbage.Proof.Argon2.FinalReduction + +/-! Reuse the verified word loop with allocation-level permissions. -/ + +namespace VG.Proof.Argon2.X86_64.ReduceBlock + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.ReduceBlock + +theorem code_ok (s : State) + (read : Covers [⟨s.gpr .rsi, 1024⟩] (s.rd ++ s.wr)) + (write : Covers [⟨s.gpr .rdi, 1024⟩] s.wr) + (separate : (⟨s.gpr .rsi, 1024⟩ : Region).Disjoint ⟨s.gpr .rdi, 1024⟩) : + WP isa code s fun t => + blockAt t.mem (s.gpr .rdi) = xorBlock (blockAt s.mem (s.gpr .rdi)) (blockAt s.mem (s.gpr .rsi)) ∧ + Frame [⟨s.gpr .rdi, 1024⟩] s.mem t.mem ∧ CopyKeeps s t ∧ t.mxcsr = s.mxcsr := by + let a := s.withRegions [⟨s.gpr .rsi, 1024⟩] [⟨s.gpr .rdi, 1024⟩] + obtain ⟨trace, t, run, written, frame, keeps, mx⟩ := + FillWrite.prefix_ok true 128 (by decide) a (by simp [a]) (by simp [a]) separate + have cover : Covers (a.rd ++ a.wr) (s.rd ++ s.wr) := by + intro q n ⟨r, hr, hc⟩ + change r ∈ [⟨s.gpr .rsi, 1024⟩, ⟨s.gpr .rdi, 1024⟩] at hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact read q n ⟨_, by simp, hc⟩ + · obtain ⟨r, hr, hc⟩ := write q n ⟨_, by simp, hc⟩ + exact ⟨r, List.mem_append_right _ hr, hc⟩ + have run' := Exec.widen (rd := s.rd) (wr := s.wr) run cover write + simp only [a, State.withRegions_withRegions, State.withRegions_self] at run' + refine ⟨trace, t.withRegions s.rd s.wr, run', ?_, frame, ⟨keeps.1, rfl, rfl⟩, mx⟩ + exact (written_block written).trans (Proof.Argon2.xorBlock_comm _ _) + +end VG.Proof.Argon2.X86_64.ReduceBlock diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceBlockCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceBlockCT.lean new file mode 100644 index 000000000..b37c8ce36 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceBlockCT.lean @@ -0,0 +1,15 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReduceBlockLit +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! Final block XOR has fixed accesses determined only by its public pointers. -/ + +namespace VG.Proof.Argon2.X86_64.ReduceBlock + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.ReduceBlock + +theorem code_rel : RelCT isa + (fun s t => ∀ r ∈ [Reg.rdi, .rsi], s.gpr r = t.gpr r) code (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rdi, .rsi]) + (fun _ _ h => Taint.agree_ofRegs h) (by taint_decide) + +end VG.Proof.Argon2.X86_64.ReduceBlock diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceBlockLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceBlockLit.lean new file mode 100644 index 000000000..2f202634c --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceBlockLit.lean @@ -0,0 +1,8 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ReduceBlock +import VerifiedGarbage.Proof.Framework.X86_64.Lit + +namespace VG.Impl.Argon2.X86_64.ReduceBlock + +materialize_code code + +end VG.Impl.Argon2.X86_64.ReduceBlock diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLane.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLane.lean new file mode 100644 index 000000000..f8c9665d0 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLane.lean @@ -0,0 +1,89 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ReduceLane +import VerifiedGarbage.Proof.Argon2.X86_64.ReductionState +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressSetup + +/-! A lane reduction writes only the accumulator, retaining every last-lane block. -/ + +namespace VG.Proof.Argon2.X86_64.ReduceLane + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState + +structure Done (s t : State) (p : Params) (memory : Array Block) (acc : Block) : Prop where + ready : Ready p t + represented : ReductionState.Represents p memory acc t + base : matrix t = matrix s + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨matrix s, 1024⟩] s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + +theorem code_ok (s : State) (p : Params) (lane : Nat) (h : Ready p s) (active : lane < p.lanes) + (laneWord : s.gpr .rbx = BitVec.ofNat 64 lane) (memory : Array Block) (acc : Block) + (represented : ReductionState.Represents p memory acc s) : + WP isa Impl.Argon2.X86_64.ReduceLane.code s + (Done s · p memory (xorBlock acc (memory[Proof.Argon2.lastIndex p lane]?.getD zeroBlock))) := by + have lastBounds := Proof.Argon2.lastIndex_bounds p h.positive h.minimum lane active + have q : 0 < p.laneLen := by + have eq := Proof.Argon2.laneLen_segments p h.positive + have minimum := h.minimum + omega + unfold Impl.Argon2.X86_64.ReduceLane.code + refine WP.seq ((ReducePointers.code_ok s lane p.laneLen q h.read laneWord h.length).mono ?_) + rintro a ⟨dest, src, keeps⟩ + have ha := h.of_keeps keeps + have rep := represented.of_keeps keeps + have base : matrix a = matrix s := by unfold matrix; rw [keeps.mem, keeps.regs .rbp (by decide)] + have dest' : a.gpr .rdi = matrix a := dest.trans base.symm + have src' : a.gpr .rsi = Proof.Argon2.matrixCell (matrix a) (Proof.Argon2.lastIndex p lane) := by rw [base]; exact src + have sourceWrite : Covers [⟨a.gpr .rsi, 1024⟩] a.wr := by rw [src']; exact ha.block_cover _ lastBounds.2 + have sourceRead : Covers [⟨a.gpr .rsi, 1024⟩] (a.rd ++ a.wr) := by + intro x n hx + obtain ⟨r, hr, hc⟩ := sourceWrite x n hx + exact ⟨r, List.mem_append_right _ hr, hc⟩ + have destWrite : Covers [⟨a.gpr .rdi, 1024⟩] a.wr := by rw [dest']; exact ha.accumulator_cover + have sep : (⟨a.gpr .rsi, 1024⟩ : Region).Disjoint ⟨a.gpr .rdi, 1024⟩ := by + rw [src', dest'] + simpa only [Proof.Argon2.matrixCell, Nat.zero_mul, BitVec.add_zero] using + Proof.Argon2.matrixCell_disjoint (matrix a) p.blocks (Proof.Argon2.lastIndex p lane) 0 ha.bound + lastBounds.2 (by omega) (by omega) + refine (ReduceBlock.code_ok a sourceRead destWrite sep).mono ?_ + rintro t ⟨written, frame, copied, mx⟩ + rw [dest'] at frame written + have bp : t.gpr .rbp = a.gpr .rbp := copied.1 .rbp (by decide) + have base' : matrix t = matrix a := by + unfold matrix + rw [bp] + exact frame.readW (r := ⟨a.gpr .rbp, 272⟩) (Offset.contains_base _ (by decide) (by decide)) + (by intro r hr; simp only [List.mem_singleton] at hr; subst r + exact ha.frame.symm.sub_right (Region.sub_prefix (by omega))) (by decide) + refine ⟨?_, ?_, base'.trans base, ?_, copied.2.1.trans keeps.rd, copied.2.2.trans keeps.wr, + ?_, mx.trans keeps.mxcsr⟩ + · refine ⟨ha.positive, ha.minimum, ha.bound, ?_, ?_, ?_, (copied.1 .r12 (by decide)).trans ha.length⟩ + · rw [copied.2.1, copied.2.2, bp]; exact ha.read + · rw [base', copied.2.2]; exact ha.write + · rw [base', bp]; exact ha.frame + · constructor + · rw [base', written, src', rep.accumulator, rep.last lane active] + · intro j hj + rw [base'] + apply Eq.trans _ (rep.last j hj) + apply FillCompress.block_frame frame + intro r hr + simp only [List.mem_singleton] at hr + subst r + have bounds := Proof.Argon2.lastIndex_bounds p ha.positive ha.minimum j hj + simpa only [Proof.Argon2.matrixCell, Nat.zero_mul, BitVec.add_zero] using + Proof.Argon2.matrixCell_disjoint (matrix a) p.blocks (Proof.Argon2.lastIndex p j) 0 ha.bound + bounds.2 (by omega) (by omega) + · intro r hr + have ne : r ≠ .rax := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + have unchanged : r ∉ ReducePointers.changed := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + exact (copied.1 r ne).trans (keeps.regs r unchanged) + · rw [base, keeps.mem] at frame; exact frame + +end VG.Proof.Argon2.X86_64.ReduceLane diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLaneCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLaneCT.lean new file mode 100644 index 000000000..7ac2952b8 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLaneCT.lean @@ -0,0 +1,45 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReduceLane +import VerifiedGarbage.Proof.Argon2.X86_64.ReducePointersCT +import VerifiedGarbage.Proof.Argon2.X86_64.ReduceBlockCT + +/-! Final lane reduction depends only on public lane coordinates and matrix pointers. -/ + +namespace VG.Proof.Argon2.X86_64.ReduceLane + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState + +structure Related (p : Params) (lane : Nat) (s t : State) : Prop where + left : Ready p s + right : Ready p t + active : lane < p.lanes + leftLane : s.gpr .rbx = BitVec.ofNat 64 lane + rightLane : t.gpr .rbx = BitVec.ofNat 64 lane + bases : s.gpr .rbp = t.gpr .rbp + matrices : matrix s = matrix t + +theorem pointers_rel (p : Params) (lane : Nat) : + RelCT isa (Related p lane) Impl.Argon2.X86_64.ReducePointers.code + (fun s t => ∀ r ∈ [Reg.rdi, .rsi], s.gpr r = t.gpr r) := by + have trace := ReducePointers.code_rel.mono (P' := Related p lane) (fun _ _ h => h.bases) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => by + have q : 0 < p.laneLen := by + have eq := Proof.Argon2.laneLen_segments p h.left.positive + have minimum := h.left.minimum + omega + exact ⟨ReducePointers.code_ok s lane p.laneLen q h.left.read h.leftLane h.left.length, + ReducePointers.code_ok t lane p.laneLen q h.right.read h.rightLane h.right.length⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ⟨destA, srcA, _⟩, ⟨destB, srcB, _⟩⟩ := h + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact destA.trans (hp.matrices.trans destB.symm) + · have bases : s.mem.readW (off (s.gpr .rbp) 232) 64 = t.mem.readW (off (t.gpr .rbp) 232) 64 := hp.matrices + rw [srcA, srcB, bases] + +theorem code_rel (p : Params) (lane : Nat) : + RelCT isa (Related p lane) Impl.Argon2.X86_64.ReduceLane.code (fun _ _ => True) := + (pointers_rel p lane).seq ReduceBlock.code_rel + +end VG.Proof.Argon2.X86_64.ReduceLane diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanes.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanes.lean new file mode 100644 index 000000000..c91eb2a68 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanes.lean @@ -0,0 +1,55 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReduceLanesBody + + +/-! Termination and correctness of the final lane reduction. -/ + +namespace VG.Proof.Argon2.X86_64.ReduceLanes + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState + +structure Finished (s t : State) (p : Params) (memory : Array Block) (acc : Block) : Prop where + represented : ReductionState.Represents p memory acc t + base : matrix t = matrix s + laneWord : t.gpr .rbx = BitVec.ofNat 64 p.lanes + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨matrix s, 1024⟩] s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + regs : ∀ r ∈ calleeSaved, r ≠ .rbx → t.gpr r = s.gpr r + +theorem Done.finished {s t : State} {p : Params} {lane : Nat} {memory : Array Block} {acc : Block} + (h : Done s t p lane memory acc) (last : lane + 1 = p.lanes) : Finished s t p memory acc := + ⟨h.represented, h.base, last ▸ h.laneWord, h.rd, h.wr, h.frame, h.mxcsr, h.regs⟩ + +theorem Finished.prepend {s a t : State} {p : Params} {lane : Nat} {memory : Array Block} {acc result : Block} + (first : Done s a p lane memory acc) (rest : Finished a t p memory result) : Finished s t p memory result := by + refine ⟨rest.represented, rest.base.trans first.base, rest.laneWord, + rest.rd.trans first.rd, rest.wr.trans first.wr, ?_, rest.mxcsr.trans first.mxcsr, ?_⟩ + · have frame := rest.frame + rw [first.base] at frame + exact first.frame.trans frame + · intro r hr bx; exact (rest.regs r hr bx).trans (first.regs r hr bx) + +theorem loop_ok (count : Nat) (s : State) (p : Params) (lane : Nat) (h : Ready p lane s) + (memory : Array Block) (acc : Block) (represented : ReductionState.Represents p memory acc s) + (positive : 0 < count) (endLane : lane + count = p.lanes) : + WP isa Impl.Argon2.X86_64.ReduceLanes.loop s + (Finished s · p memory (Proof.Argon2.reduction p memory lane count acc)) := by + induction count generalizing s lane acc with + | zero => omega + | succ n ih => + obtain ⟨trace, a, run, done⟩ := body_ok s p lane h memory acc represented + rw [Proof.Argon2.reduction_succ] + cases n with + | zero => + have last : lane + 1 = p.lanes := endLane + refine ⟨_, a, .loopExit run ?_, done.finished last⟩ + simp only [eval, done.cf, last, Nat.lt_irrefl, decide_false] + | succ n => + have active : lane + 1 < p.lanes := by omega + obtain ⟨restTrace, t, restRun, finished⟩ := ih a (lane + 1) (done.next active) _ + done.represented (by omega) (by omega) + refine ⟨_, t, .loopNext run ?_ restRun, finished.prepend done⟩ + simp only [eval, done.cf, active, decide_true] + +end VG.Proof.Argon2.X86_64.ReduceLanes diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanesBody.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanesBody.lean new file mode 100644 index 000000000..f44a401d8 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanesBody.lean @@ -0,0 +1,61 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ReduceLanes +import VerifiedGarbage.Proof.Argon2.X86_64.ReductionLoopState +import VerifiedGarbage.Proof.Argon2.X86_64.FillLaneAdvance + +/-! One reduction iteration advances a public lane and preserves the accumulator invariant. -/ + +namespace VG.Proof.Argon2.X86_64.ReduceLanes + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState + +structure Ready (p : Params) (lane : Nat) (s : State) : Prop where + allocation : ReductionState.Ready p s + active : lane < p.lanes + lanesBound : p.lanes < 2 ^ 32 + laneWord : s.gpr .rbx = BitVec.ofNat 64 lane + lanesRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 184) 8 + lanesWord : s.mem.readW (off (s.gpr .rbp) 184) 64 = BitVec.ofNat 64 p.lanes + +structure Done (s t : State) (p : Params) (lane : Nat) (memory : Array Block) (acc : Block) : Prop where + represented : ReductionState.Represents p memory acc t + base : matrix t = matrix s + laneWord : t.gpr .rbx = BitVec.ofNat 64 (lane + 1) + regs : ∀ r ∈ calleeSaved, r ≠ .rbx → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨matrix s, 1024⟩] s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + cf : t.cf = decide (lane + 1 < p.lanes) + next : lane + 1 < p.lanes → Ready p (lane + 1) t + +theorem body_ok (s : State) (p : Params) (lane : Nat) (h : Ready p lane s) + (memory : Array Block) (acc : Block) (represented : ReductionState.Represents p memory acc s) : + WP isa Impl.Argon2.X86_64.ReduceLanes.body s + (Done s · p lane memory (xorBlock acc (memory[Proof.Argon2.lastIndex p lane]?.getD zeroBlock))) := by + unfold Impl.Argon2.X86_64.ReduceLanes.body Impl.Argon2.X86_64.ReduceLanes.advance + refine WP.seq ((ReduceLane.code_ok s p lane h.allocation h.active h.laneWord memory acc represented).mono ?_) + intro a reduced + have read : InRegions (a.rd ++ a.wr) (off (a.gpr .rbp) 184) 8 := by + rw [reduced.rd, reduced.wr, reduced.regs .rbp (by simp [calleeSaved])]; exact h.lanesRead + have word := (ReductionState.frame_word h.allocation reduced 184 (by decide)).trans h.lanesWord + refine (FillLanes.advance_ok a read).mono ?_ + rintro t ⟨value, flag, keeps⟩ + have bp := keeps.regs .rbp (by decide) + have base : matrix t = matrix a := by unfold matrix; rw [keeps.mem, bp] + have added : a.gpr .rbx + 1 = BitVec.ofNat 64 (lane + 1) := by + rw [reduced.regs .rbx (by simp [calleeSaved]), h.laneWord, BitVec.ofNat_add]; rfl + have nextWord := value.trans added + refine ⟨reduced.represented.of_state bp keeps.mem, base.trans reduced.base, nextWord, + ?_, keeps.rd.trans reduced.rd, keeps.wr.trans reduced.wr, ?_, keeps.mxcsr.trans reduced.mxcsr, ?_, ?_⟩ + · intro r hr bx + exact (keeps.regs r (by simpa only [List.mem_cons, List.not_mem_nil, or_false] using bx)).trans (reduced.regs r hr) + · rw [keeps.mem]; exact reduced.frame + · rw [flag, added, word, ReferenceMap.word_nat (lane + 1) (by have bound := h.lanesBound; have active := h.active; omega), + ReferenceMap.word_nat p.lanes (Nat.lt_trans h.lanesBound (by decide))] + · intro active + refine ⟨reduced.ready.of_state bp (keeps.regs .r12 (by decide)) keeps.mem keeps.rd keeps.wr, + active, h.lanesBound, nextWord, ?_, ?_⟩ + · rw [keeps.rd, keeps.wr, bp]; exact read + · rw [keeps.mem, bp]; exact word + +end VG.Proof.Argon2.X86_64.ReduceLanes diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanesBodyCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanesBodyCT.lean new file mode 100644 index 000000000..91cf42f65 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanesBodyCT.lean @@ -0,0 +1,58 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReduceLanesBody +import VerifiedGarbage.Proof.Argon2.X86_64.ReduceLaneCT + +/-! Reduction visits the same last blocks even when their contents differ. -/ + +namespace VG.Proof.Argon2.X86_64.ReduceLanes + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState + +structure Related (p : Params) (lane : Nat) (leftMemory rightMemory : Array Block) (leftAcc rightAcc : Block) + (s t : State) : Prop where + left : Ready p lane s + right : Ready p lane t + bases : s.gpr .rbp = t.gpr .rbp + matrices : matrix s = matrix t + leftRep : ReductionState.Represents p leftMemory leftAcc s + rightRep : ReductionState.Represents p rightMemory rightAcc t + +theorem advance_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block Impl.Argon2.X86_64.ReduceLanes.advance) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr; simp only [List.mem_cons, List.not_mem_nil, or_false] at hr; subst r; exact h)) (by taint_decide) + +theorem body_rel (p : Params) (lane : Nat) (leftMemory rightMemory : Array Block) (leftAcc rightAcc : Block) : + RelCT isa (Related p lane leftMemory rightMemory leftAcc rightAcc) Impl.Argon2.X86_64.ReduceLanes.body + (fun s t => s.cf = t.cf ∧ (lane + 1 < p.lanes → Related p (lane + 1) leftMemory rightMemory + (xorBlock leftAcc (leftMemory[Proof.Argon2.lastIndex p lane]?.getD zeroBlock)) + (xorBlock rightAcc (rightMemory[Proof.Argon2.lastIndex p lane]?.getD zeroBlock)) s t)) := by + intro s t ts tt a b hp ea eb + have related : ReduceLane.Related p lane s t := + ⟨hp.left.allocation, hp.right.allocation, hp.left.active, hp.left.laneWord, hp.right.laneWord, hp.bases, hp.matrices⟩ + cases ea with + | seq reduceA advanceA => + cases eb with + | seq reduceB advanceB => + obtain ⟨reduceTrace, _⟩ := ReduceLane.code_rel p lane _ _ _ _ _ _ related reduceA reduceB + obtain ⟨_, sa, runA, reducedA⟩ := ReduceLane.code_ok s p lane hp.left.allocation hp.left.active + hp.left.laneWord leftMemory leftAcc hp.leftRep + obtain ⟨_, sb, runB, reducedB⟩ := ReduceLane.code_ok t p lane hp.right.allocation hp.right.active + hp.right.laneWord rightMemory rightAcc hp.rightRep + obtain ⟨_, rfl⟩ := Exec.det reduceA runA + obtain ⟨_, rfl⟩ := Exec.det reduceB runB + have bases := (reducedA.regs .rbp (by simp [calleeSaved])).trans + (hp.bases.trans (reducedB.regs .rbp (by simp [calleeSaved])).symm) + obtain ⟨advanceTrace, _⟩ := advance_rel _ _ _ _ _ _ bases advanceA advanceB + obtain ⟨_, a', runA, doneA⟩ := body_ok s p lane hp.left leftMemory leftAcc hp.leftRep + obtain ⟨_, b', runB, doneB⟩ := body_ok t p lane hp.right rightMemory rightAcc hp.rightRep + obtain ⟨_, rfl⟩ := Exec.det (.seq reduceA advanceA) runA + obtain ⟨_, rfl⟩ := Exec.det (.seq reduceB advanceB) runB + refine ⟨by rw [reduceTrace, advanceTrace], doneA.cf.trans doneB.cf.symm, ?_⟩ + intro active + refine ⟨doneA.next active, doneB.next active, ?_, + doneA.base.trans (hp.matrices.trans doneB.base.symm), doneA.represented, doneB.represented⟩ + exact (doneA.regs .rbp (by simp [calleeSaved]) (by decide)).trans + (hp.bases.trans (doneB.regs .rbp (by simp [calleeSaved]) (by decide)).symm) + +end VG.Proof.Argon2.X86_64.ReduceLanes diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanesCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanesCT.lean new file mode 100644 index 000000000..ecab8f66b --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReduceLanesCT.lean @@ -0,0 +1,36 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReduceLanesBodyCT + +/-! The final reduction leaks only public matrix addresses and the public lane count. -/ + +namespace VG.Proof.Argon2.X86_64.ReduceLanes + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem loop_rel (p : Params) (lane count : Nat) (leftMemory rightMemory : Array Block) (leftAcc rightAcc : Block) + (positive : 0 < count) (endLane : lane + count = p.lanes) : + RelCT isa (Related p lane leftMemory rightMemory leftAcc rightAcc) Impl.Argon2.X86_64.ReduceLanes.loop + (fun _ _ => True) := by + let I := fun n s t => ∃ (lane : Nat) (leftAcc rightAcc : Block), lane + n = p.lanes ∧ 0 < n ∧ + Related p lane leftMemory rightMemory leftAcc rightAcc s t + have steps : ∀ n, RelCT isa (I n) Impl.Argon2.X86_64.ReduceLanes.body fun s t => + isa.eval .b s = isa.eval .b t ∧ (isa.eval .b s = some false → True) ∧ + (isa.eval .b s = some true → ∃ m < n, I m s t) := by + intro n s t ts tt a b hp ea eb + obtain ⟨j, la, ra, endLane, positive, hp⟩ := hp + cases n with + | zero => omega + | succ n => + obtain ⟨trace, flags, next⟩ := body_rel p j leftMemory rightMemory la ra _ _ _ _ _ _ hp ea eb + obtain ⟨_, a', runA, done⟩ := body_ok s p j hp.left leftMemory la hp.leftRep + obtain ⟨_, rfl⟩ := Exec.det ea runA + refine ⟨trace, ?_, fun _ => trivial, ?_⟩ + · simp only [eval, flags] + · intro taken + have active : j + 1 < p.lanes := by + simp only [eval, done.cf, Option.some.injEq, decide_eq_true_eq] at taken + exact taken + exact ⟨n, by omega, j + 1, _, _, by omega, by omega, next active⟩ + exact (RelCT.loop I steps count).mono + (fun _ _ h => ⟨lane, leftAcc, rightAcc, endLane, positive, h⟩) (fun _ _ h => h) + +end VG.Proof.Argon2.X86_64.ReduceLanes diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReducePointers.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReducePointers.lean new file mode 100644 index 000000000..02e7d27b6 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReducePointers.lean @@ -0,0 +1,69 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ReducePointers +import VerifiedGarbage.Proof.Argon2.X86_64.BlockAddress +import VerifiedGarbage.Proof.Argon2.X86_64.Initialize +import VerifiedGarbage.Proof.Argon2.FinalReduction + +/-! The last-lane address calculation preserves all callee-saved registers. -/ + +namespace VG.Proof.Argon2.X86_64.ReducePointers + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.ReducePointers + +def changed : List Reg := [.r8, .rax, .rcx, .rdx, .rsi, .rdi] + +theorem setup_ok (s : State) (read : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 232) 8) : + WP isa (.block Impl.Argon2.X86_64.ReducePointers.setup) s fun t => + t.gpr .r8 = s.mem.readW (off (s.gpr .rbp) 232) 64 ∧ + t.gpr .rax = s.gpr .rbx ∧ t.gpr .rcx = s.gpr .r12 - 1 ∧ + Divide.Keeps [.r8, .rax, .rcx] s t := by + apply WP.of_runBlock + simp only [Impl.Argon2.X86_64.ReducePointers.setup, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + State.load64, ea_at, read, ite_true, Option.map_some, Option.bind_some, Option.some.injEq, + exists_eq_left', execAlu, RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, reduceCtorEq, ite_false] + refine ⟨trivial, trivial, rfl, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr.1, hr.2.1, hr.2.2, ite_false] + all_goals rfl + +theorem finish_ok (s : State) : WP isa (.block Impl.Argon2.X86_64.ReducePointers.finish) s fun t => + t.gpr .rsi = s.gpr .rax ∧ t.gpr .rdi = s.gpr .r8 ∧ Divide.Keeps [.rsi, .rdi] s t := by + apply WP.of_runBlock + simp only [Impl.Argon2.X86_64.ReducePointers.finish, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + Option.map_some, Option.some.injEq, exists_eq_left', RegUpd.gpr_setReg, + reduceCtorEq, ite_true, ite_false] + refine ⟨trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, hr.1, hr.2, ite_false] + all_goals rfl + +theorem code_ok (s : State) (lane q : Nat) (positive : 0 < q) + (read : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 232) 8) + (laneWord : s.gpr .rbx = BitVec.ofNat 64 lane) (lengthWord : s.gpr .r12 = BitVec.ofNat 64 q) : + WP isa code s fun t => + t.gpr .rdi = s.mem.readW (off (s.gpr .rbp) 232) 64 ∧ + t.gpr .rsi = Proof.Argon2.matrixCell (s.mem.readW (off (s.gpr .rbp) 232) 64) ((lane + 1) * q - 1) ∧ + Divide.Keeps changed s t := by + unfold code + refine WP.seq ((setup_ok s read).mono ?_) + rintro a ⟨base, lan, col, ka⟩ + have column : a.gpr .rcx = BitVec.ofNat 64 (q - 1) := by + rw [col, lengthWord] + exact Offset.ofNat_sub_ofNat (by omega : 1 ≤ q) + refine WP.seq ((BlockAddress.code_nat_ok a lane (q - 1) q (lan.trans laneWord) column + ((ka.regs .r12 (by decide)).trans lengthWord)).mono ?_) + rintro b ⟨address, kb⟩ + refine (finish_ok b).mono ?_ + rintro t ⟨src, dest, kt⟩ + refine ⟨dest.trans ((kb.regs .r8 (by decide)).trans base), ?_, ?_⟩ + · rw [src, address, base] + unfold Proof.Argon2.matrixCell + have offset : lane * q + (q - 1) = (lane + 1) * q - 1 := by rw [Nat.add_mul, Nat.one_mul]; omega + rw [offset] + · exact (ka.mono (by simp [changed])).trans + ((kb.mono (by simp [changed])).trans (kt.mono (by simp [changed]))) + +end VG.Proof.Argon2.X86_64.ReducePointers diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReducePointersCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReducePointersCT.lean new file mode 100644 index 000000000..a37c47e79 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReducePointersCT.lean @@ -0,0 +1,18 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReducePointers + +/-! Pointer preparation reads one public frame location and performs fixed arithmetic. -/ + +namespace VG.Proof.Argon2.X86_64.ReducePointers + +open VG VG.X86_64 + +theorem code_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + Impl.Argon2.X86_64.ReducePointers.code (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +end VG.Proof.Argon2.X86_64.ReducePointers diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionClear.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionClear.lean new file mode 100644 index 000000000..fcb34b79e --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionClear.lean @@ -0,0 +1,64 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReductionLoopState +import VerifiedGarbage.Proof.Argon2.X86_64.ClearBlock + +/-! Clear only block zero, preserving the original last blocks in the matrix. -/ + +namespace VG.Proof.Argon2.X86_64.ReductionState + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Cleared (s t : State) (p : Params) (memory : Array Block) : Prop where + ready : Ready p t + represented : Represents p memory zeroBlock t + base : matrix t = matrix s + keeps : CopyKeeps s t + frame : Frame [⟨matrix s, 1024⟩] s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + +theorem clear_ok (s : State) (p : Params) (h : Ready p s) + (dest : s.gpr .rdi = matrix s) (memory : Array Block) + (represented : Proof.Argon2.Represents s.mem (matrix s) p.blocks memory) : + WP isa Impl.Argon2.X86_64.ClearBlock.code s (Cleared s · p memory) := by + have write : Covers [⟨s.gpr .rdi, 1024⟩] s.wr := by rw [dest]; exact h.accumulator_cover + refine (ClearBlock.code_ok s write).mono ?_ + rintro t ⟨zero, frame, keeps, mx⟩ + rw [dest] at frame zero + have bp := keeps.1 .rbp (by decide) + have nonempty := Proof.Argon2.lastIndex_bounds p h.positive h.minimum 0 h.positive + have metadata (d : Nat) (bound : d + 8 ≤ 272) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [bp] + exact frame.readW (r := ⟨s.gpr .rbp, 272⟩) (Offset.contains_base _ bound (by omega)) + (by intro r hr; simp only [List.mem_singleton] at hr; subst r + exact h.frame.symm.sub_right (Region.sub_prefix (by omega))) (by decide) + have base : matrix t = matrix s := metadata 232 (by decide) + refine ⟨?_, ?_, base, keeps, frame, mx⟩ + · refine ⟨h.positive, h.minimum, h.bound, ?_, ?_, ?_, (keeps.1 .r12 (by decide)).trans h.length⟩ + · rw [keeps.2.1, keeps.2.2, bp]; exact h.read + · rw [base, keeps.2.2]; exact h.write + · rw [base, bp]; exact h.frame + · constructor + · rw [base]; exact zero + · intro lane active + rw [base] + apply Eq.trans _ (represented.block _ (Proof.Argon2.lastIndex_bounds p h.positive h.minimum lane active).2) + apply FillCompress.block_frame frame + intro r hr + simp only [List.mem_singleton] at hr + subst r + have bounds := Proof.Argon2.lastIndex_bounds p h.positive h.minimum lane active + simpa only [Proof.Argon2.matrixCell, Nat.zero_mul, BitVec.add_zero] using + Proof.Argon2.matrixCell_disjoint (matrix s) p.blocks (Proof.Argon2.lastIndex p lane) 0 h.bound + bounds.2 (by omega) (by omega) + +theorem Cleared.frame_word {p : Params} {s t : State} {memory : Array Block} + (ready : Ready p s) (done : Cleared s t p memory) (d : Nat) (bound : d + 8 ≤ 272) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [done.keeps.1 .rbp (by decide)] + exact done.frame.readW (r := ⟨s.gpr .rbp, 272⟩) (Offset.contains_base _ bound (by omega)) + (by intro r hr; simp only [List.mem_singleton] at hr; subst r + exact ready.frame.symm.sub_right (Region.sub_prefix (by + have nonempty := Proof.Argon2.lastIndex_bounds p ready.positive ready.minimum 0 ready.positive + omega))) (by decide) + +end VG.Proof.Argon2.X86_64.ReductionState diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionInit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionInit.lean new file mode 100644 index 000000000..2e4be05b9 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionInit.lean @@ -0,0 +1,72 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ReductionInit +import VerifiedGarbage.Proof.Argon2.X86_64.ReductionClear +import VerifiedGarbage.Proof.Argon2.X86_64.ReduceLanes + +/-! Establish the invariant for reducing all lanes, retaining the input matrix's last blocks. -/ + +namespace VG.Proof.Argon2.X86_64.ReductionInit + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState + +structure Ready (p : Params) (s : State) : Prop where + allocation : ReductionState.Ready p s + lanesBound : p.lanes < 2 ^ 32 + lanesRead : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 184) 8 + lanesWord : s.mem.readW (off (s.gpr .rbp) 184) 64 = BitVec.ofNat 64 p.lanes + +theorem setup_ok (s : State) (read : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 232) 8) : + WP isa (.block Impl.Argon2.X86_64.ReductionInit.setup) s fun t => + t.gpr .rdi = matrix s ∧ t.gpr .rbx = 0 ∧ Divide.Keeps [.rdi, .rbx] s t := by + apply WP.of_runBlock + simp only [Impl.Argon2.X86_64.ReductionInit.setup, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + State.load64, ea_at, read, ite_true, Option.map_some, Option.some.injEq, exists_eq_left', + RegUpd.gpr_setReg, reduceCtorEq, ite_false] + refine ⟨rfl, rfl, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, hr.1, hr.2, ite_false] + all_goals rfl + +structure Prepared (s t : State) (p : Params) (memory : Array Block) : Prop where + ready : ReduceLanes.Ready p 0 t + represented : ReductionState.Represents p memory zeroBlock t + base : matrix t = matrix s + regs : ∀ r ∈ calleeSaved, r ≠ .rbx → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨matrix s, 1024⟩] s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + +theorem code_ok (s : State) (p : Params) (h : Ready p s) (memory : Array Block) + (represented : Proof.Argon2.Represents s.mem (matrix s) p.blocks memory) : + WP isa Impl.Argon2.X86_64.ReductionInit.code s (Prepared s · p memory) := by + unfold Impl.Argon2.X86_64.ReductionInit.code + refine WP.seq ((setup_ok s h.allocation.read).mono ?_) + rintro a ⟨dest, lane, keeps⟩ + have bp := keeps.regs .rbp (by decide) + have base : matrix a = matrix s := by unfold matrix; rw [keeps.mem, bp] + have ha := h.allocation.of_state bp (keeps.regs .r12 (by decide)) keeps.mem keeps.rd keeps.wr + have rep : Proof.Argon2.Represents a.mem (matrix a) p.blocks memory := by rw [keeps.mem, base]; exact represented + refine (ReductionState.clear_ok a p ha (dest.trans base.symm) memory rep).mono ?_ + intro t cleared + refine ⟨⟨cleared.ready, ha.positive, h.lanesBound, + (cleared.keeps.1 .rbx (by decide)).trans lane, ?_, ?_⟩, + cleared.represented, cleared.base.trans base, ?_, cleared.keeps.2.1.trans keeps.rd, + cleared.keeps.2.2.trans keeps.wr, ?_, cleared.mxcsr.trans keeps.mxcsr⟩ + · rw [cleared.keeps.2.1, cleared.keeps.2.2, cleared.keeps.1 .rbp (by decide), keeps.rd, keeps.wr, bp] + exact h.lanesRead + · rw [cleared.frame_word ha 184 (by decide), keeps.mem, bp]; exact h.lanesWord + · intro r hr bx + have ne : r ≠ .rax := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + have notDest : r ≠ .rdi := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + exact (cleared.keeps.1 r ne).trans (keeps.regs r (by + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or]; exact ⟨notDest, bx⟩)) + · have frame := cleared.frame + rw [base, keeps.mem] at frame; exact frame + +end VG.Proof.Argon2.X86_64.ReductionInit diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionInitCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionInitCT.lean new file mode 100644 index 000000000..bd5848b51 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionInitCT.lean @@ -0,0 +1,54 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReductionInit +import VerifiedGarbage.Proof.Argon2.X86_64.ReduceLanesCT +import VerifiedGarbage.Proof.Argon2.X86_64.ClearBlockLit + +/-! Clearing the accumulator follows public pointers and visits a fixed block. -/ + +namespace VG.Proof.Argon2.X86_64.ReductionInit + +open VG VG.X86_64 VG.Spec.Argon2 ReductionState + +structure Related (p : Params) (leftMemory rightMemory : Array Block) (s t : State) : Prop where + left : Ready p s + right : Ready p t + bases : s.gpr .rbp = t.gpr .rbp + matrices : matrix s = matrix t + leftMatrix : Proof.Argon2.Represents s.mem (matrix s) p.blocks leftMemory + rightMatrix : Proof.Argon2.Represents t.mem (matrix t) p.blocks rightMemory + +theorem setup_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block Impl.Argon2.X86_64.ReductionInit.setup) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr; simp only [List.mem_cons, List.not_mem_nil, or_false] at hr; subst r; exact h)) (by taint_decide) + +theorem clear_rel : RelCT isa (fun s t => s.gpr .rdi = t.gpr .rdi) + Impl.Argon2.X86_64.ClearBlock.code (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rdi]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr; simp only [List.mem_cons, List.not_mem_nil, or_false] at hr; subst r; exact h)) (by taint_decide) + +theorem code_rel (p : Params) (leftMemory rightMemory : Array Block) : + RelCT isa (Related p leftMemory rightMemory) Impl.Argon2.X86_64.ReductionInit.code + (ReduceLanes.Related p 0 leftMemory rightMemory zeroBlock zeroBlock) := by + intro s t ts tt a b hp ea eb + cases ea with + | seq setupA clearA => + cases eb with + | seq setupB clearB => + obtain ⟨setupTrace, _⟩ := setup_rel _ _ _ _ _ _ hp.bases setupA setupB + obtain ⟨_, sa, runA, destA, _, _⟩ := setup_ok s hp.left.allocation.read + obtain ⟨_, sb, runB, destB, _, _⟩ := setup_ok t hp.right.allocation.read + obtain ⟨_, rfl⟩ := Exec.det setupA runA + obtain ⟨_, rfl⟩ := Exec.det setupB runB + obtain ⟨clearTrace, _⟩ := clear_rel _ _ _ _ _ _ (destA.trans (hp.matrices.trans destB.symm)) clearA clearB + obtain ⟨_, a', runA, doneA⟩ := code_ok s p hp.left leftMemory hp.leftMatrix + obtain ⟨_, b', runB, doneB⟩ := code_ok t p hp.right rightMemory hp.rightMatrix + obtain ⟨_, rfl⟩ := Exec.det (.seq setupA clearA) runA + obtain ⟨_, rfl⟩ := Exec.det (.seq setupB clearB) runB + refine ⟨by rw [setupTrace, clearTrace], doneA.ready, doneB.ready, ?_, + doneA.base.trans (hp.matrices.trans doneB.base.symm), doneA.represented, doneB.represented⟩ + exact (doneA.regs .rbp (by simp [calleeSaved]) (by decide)).trans + (hp.bases.trans (doneB.regs .rbp (by simp [calleeSaved]) (by decide)).symm) + +end VG.Proof.Argon2.X86_64.ReductionInit diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionLoopState.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionLoopState.lean new file mode 100644 index 000000000..c9f83cce7 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionLoopState.lean @@ -0,0 +1,36 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReduceLane + +/-! Public loop metadata survives accumulator writes and register-only advancement. -/ + +namespace VG.Proof.Argon2.X86_64.ReductionState + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem Ready.of_state {p : Params} {s t : State} (h : Ready p s) + (bp : t.gpr .rbp = s.gpr .rbp) (length : t.gpr .r12 = s.gpr .r12) + (mem : t.mem = s.mem) (rd : t.rd = s.rd) (wr : t.wr = s.wr) : Ready p t := by + have base : matrix t = matrix s := by unfold matrix; rw [mem, bp] + refine ⟨h.positive, h.minimum, h.bound, ?_, ?_, ?_, length.trans h.length⟩ + · rw [rd, wr, bp]; exact h.read + · rw [base, wr]; exact h.write + · rw [base, bp]; exact h.frame + +theorem Represents.of_state {p : Params} {s t : State} {memory : Array Block} {acc : Block} + (h : Represents p memory acc s) (bp : t.gpr .rbp = s.gpr .rbp) (mem : t.mem = s.mem) : + Represents p memory acc t := by + have base : matrix t = matrix s := by unfold matrix; rw [mem, bp] + constructor + · rw [mem, base]; exact h.accumulator + · rw [mem, base]; exact h.last + +theorem frame_word {p : Params} {s t : State} {memory : Array Block} {acc : Block} + (h : Ready p s) (done : ReduceLane.Done s t p memory acc) (d : Nat) (bound : d + 8 ≤ 272) : + t.mem.readW (off (t.gpr .rbp) d) 64 = s.mem.readW (off (s.gpr .rbp) d) 64 := by + rw [done.regs .rbp (by simp [calleeSaved])] + exact done.frame.readW (r := ⟨s.gpr .rbp, 272⟩) (Offset.contains_base _ bound (by omega)) + (by intro r hr; simp only [List.mem_singleton] at hr; subst r + exact h.frame.symm.sub_right (Region.sub_prefix (by + have nonempty := Proof.Argon2.lastIndex_bounds p h.positive h.minimum 0 h.positive + omega))) (by decide) + +end VG.Proof.Argon2.X86_64.ReductionState diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionState.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionState.lean new file mode 100644 index 000000000..d933e214f --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReductionState.lean @@ -0,0 +1,58 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReduceBlock +import VerifiedGarbage.Proof.Argon2.X86_64.ReducePointers + +/-! Block zero is the accumulator; every lane's last block remains unchanged. -/ + +namespace VG.Proof.Argon2.X86_64.ReductionState + +open VG VG.X86_64 VG.Spec.Argon2 + +def matrix (s : State) : Addr := s.mem.readW (off (s.gpr .rbp) 232) 64 + +structure Ready (p : Params) (s : State) : Prop where + positive : 0 < p.lanes + minimum : 2 ≤ p.segmentLen + bound : p.blocks * 1024 < 2 ^ 64 + read : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 232) 8 + write : Covers [⟨matrix s, p.blocks * 1024⟩] s.wr + frame : (⟨matrix s, p.blocks * 1024⟩ : Region).Disjoint ⟨s.gpr .rbp, 272⟩ + length : s.gpr .r12 = BitVec.ofNat 64 p.laneLen + +structure Represents (p : Params) (memory : Array Block) (acc : Block) (s : State) : Prop where + accumulator : blockAt s.mem (matrix s) = acc + last : ∀ lane < p.lanes, + blockAt s.mem (Proof.Argon2.matrixCell (matrix s) (Proof.Argon2.lastIndex p lane)) = + memory[Proof.Argon2.lastIndex p lane]?.getD zeroBlock + +theorem Ready.block_cover {p : Params} {s : State} (h : Ready p s) (k : Nat) (active : k < p.blocks) : + Covers [⟨Proof.Argon2.matrixCell (matrix s) k, 1024⟩] s.wr := by + have sub : Covers [⟨Proof.Argon2.matrixCell (matrix s) k, 1024⟩] [⟨matrix s, p.blocks * 1024⟩] := + Covers.of_sub (by + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact ⟨⟨matrix s, p.blocks * 1024⟩, by simp, k * 1024, rfl, by change k * 1024 + 1024 ≤ p.blocks * 1024; omega⟩) + exact fun a n ha => h.write a n (sub a n ha) + +theorem Ready.accumulator_cover {p : Params} {s : State} (h : Ready p s) : + Covers [⟨matrix s, 1024⟩] s.wr := by + have nonempty := Proof.Argon2.lastIndex_bounds p h.positive h.minimum 0 h.positive + simpa only [Proof.Argon2.matrixCell, Nat.zero_mul, BitVec.add_zero] using h.block_cover 0 (by omega) + +theorem Ready.of_keeps {p : Params} {s t : State} (h : Ready p s) + (k : Divide.Keeps ReducePointers.changed s t) : Ready p t := by + have bp := k.regs .rbp (by decide) + have base : matrix t = matrix s := by unfold matrix; rw [k.mem, bp] + refine ⟨h.positive, h.minimum, h.bound, ?_, ?_, ?_, (k.regs .r12 (by decide)).trans h.length⟩ + · rw [k.rd, k.wr, bp]; exact h.read + · rw [base, k.wr]; exact h.write + · rw [base, bp]; exact h.frame + +theorem Represents.of_keeps {p : Params} {s t : State} {memory : Array Block} {acc : Block} + (h : Represents p memory acc s) (k : Divide.Keeps ReducePointers.changed s t) : Represents p memory acc t := by + have base : matrix t = matrix s := by unfold matrix; rw [k.mem, k.regs .rbp (by decide)] + constructor + · rw [k.mem, base]; exact h.accumulator + · rw [k.mem, base]; exact h.last + +end VG.Proof.Argon2.X86_64.ReductionState diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMap.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMap.lean new file mode 100644 index 000000000..45f203cf3 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMap.lean @@ -0,0 +1,39 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapFinish + +/-! Complete reference mapping against the reviewed RFC specification. -/ + +namespace VG.Proof.Argon2.X86_64.ReferenceMap + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.ReferenceMap + +theorem code_ok (s : State) (p : Spec.Argon2.Params) (pass lane slice index : Nat) + (ready : Ready p pass lane slice index s) : + WP isa code s (Result p pass lane slice index s) := by + unfold code + refine WP.seq ((prepareLanes_ok s p pass lane slice index ready).mono ?_) + intro a ha + refine WP.seq ((window_ok s a p pass lane slice index ready.bounds ha).mono ?_) + intro b hb + refine WP.seq ((relative_ok s b p pass lane slice index ready.bounds hb).mono ?_) + intro c hc + exact finish_ok s c p pass lane slice index ready.bounds hc + +theorem spec_lane (p : Spec.Argon2.Params) (pass lane slice index : Nat) (random : Addr) : + (Spec.Argon2.reference p pass lane slice index random).1 = chosenLane p pass lane slice random := rfl + +theorem spec_column (p : Spec.Argon2.Params) (pass lane slice index : Nat) (random : Addr) : + (Spec.Argon2.reference p pass lane slice index random).2 = + (windowStart p pass slice + relativeValue p pass lane slice index random) % p.laneLen := rfl + +theorem code_spec_ok (s : State) (p : Spec.Argon2.Params) (pass lane slice index : Nat) + (ready : Ready p pass lane slice index s) : + WP isa code s fun t => + t.gpr .r9 = BitVec.ofNat 64 (Spec.Argon2.reference p pass lane slice index (s.gpr .rdi)).1 ∧ + t.gpr .rdi = BitVec.ofNat 64 (Spec.Argon2.reference p pass lane slice index (s.gpr .rdi)).2 ∧ + t.gpr .r11 = s.gpr .rdi ∧ Divide.Keeps changed s t := by + refine (code_ok s p pass lane slice index ready).mono ?_ + intro t h + rw [spec_lane, spec_column] + exact ⟨h.selected, h.column, h.original, h.keeps⟩ + +end VG.Proof.Argon2.X86_64.ReferenceMap diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapArgs.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapArgs.lean new file mode 100644 index 000000000..d1987e655 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapArgs.lean @@ -0,0 +1,70 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.ReferenceMap +import VerifiedGarbage.Proof.Argon2.X86_64.DivideStep + +/-! Register preparation for the reference-index stages. -/ + +namespace VG.Proof.Argon2.X86_64.ReferenceMap + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.ReferenceMap + +theorem pass_ea (s : State) : s.ea { base := .rbp } = s.gpr .rbp := by + change s.gpr .rbp + 0#64 = s.gpr .rbp + exact BitVec.add_zero _ + +theorem loadPass_ok (s : State) (read : InRegions (s.rd ++ s.wr) (s.gpr .rbp) 8) : + WP isa (.block loadPass) s fun t => + t.gpr .r9 = s.mem.readW (s.gpr .rbp) 64 ∧ Divide.Keeps [.r9] s t := by + apply WP.of_runBlock + simp only [loadPass, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + pass_ea, State.load64, read, ite_true, Option.map_some, RegUpd.gpr_setReg, + Option.some.injEq, exists_eq_left'] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, hr, ite_false] + all_goals rfl + +theorem laneArgs_ok (s : State) : WP isa (.block laneArgs) s fun t => + t.gpr .rdi = s.gpr .r8 ∧ t.gpr .rsi = s.gpr .rbx ∧ + Divide.Keeps [.rdi, .rsi] s t := by + apply WP.of_runBlock + simp only [laneArgs, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + Option.map_some, RegUpd.gpr_setReg, reduceCtorEq, ite_true, ite_false, + Option.some.injEq, exists_eq_left'] + refine ⟨trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, hr.1, hr.2, ite_false] + all_goals rfl + +theorem relativeArgs_ok (s : State) : WP isa (.block relativeArgs) s fun t => + t.gpr .r9 = s.gpr .rdi ∧ t.gpr .rdi = s.gpr .r11 ∧ t.gpr .rsi = s.gpr .r8 ∧ + Divide.Keeps [.r9, .rdi, .rsi] s t := by + apply WP.of_runBlock + simp only [relativeArgs, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + Option.map_some, RegUpd.gpr_setReg, reduceCtorEq, ite_true, ite_false, + Option.some.injEq, exists_eq_left'] + refine ⟨trivial, trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, hr.1, hr.2.1, hr.2.2, ite_false] + all_goals rfl + +theorem wrapArgs_ok (s : State) : WP isa (.block wrapArgs) s fun t => + t.gpr .rdi = s.gpr .rax + s.gpr .r10 ∧ t.gpr .rsi = s.gpr .r12 ∧ + Divide.Keeps [.rdi, .rsi] s t := by + apply WP.of_runBlock + simp only [wrapArgs, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, execAlu, + Option.map_some, Option.bind_some, RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, + reduceCtorEq, ite_true, ite_false, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr.1, hr.2, ite_false] + all_goals rfl + +end VG.Proof.Argon2.X86_64.ReferenceMap diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapCT.lean new file mode 100644 index 000000000..dd93d0306 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapCT.lean @@ -0,0 +1,30 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapLaneCT +import VerifiedGarbage.Proof.Argon2.X86_64.Relative +import VerifiedGarbage.Proof.Argon2.X86_64.Wrap + +/-! Complete reference mapping has no secret-dependent execution trace. -/ + +namespace VG.Proof.Argon2.X86_64.ReferenceMap + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.ReferenceMap + +theorem relativeArgs_secret_rel : + RelCT isa (fun _ _ => True) (.block relativeArgs) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by simp)) (by taint_decide) + +theorem wrapArgs_secret_rel : + RelCT isa (fun _ _ => True) (.block wrapArgs) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by simp)) (by taint_decide) + +theorem tail_secret_rel : + RelCT isa (fun _ _ => True) (.seq relative finish) (fun _ _ => True) := + (relativeArgs_secret_rel.seq Relative.code_secret_rel).seq + (wrapArgs_secret_rel.seq Wrap.code_secret_rel) + +theorem code_rel (p : Spec.Argon2.Params) (pass lane slice index : Nat) : + RelCT isa (Related p pass lane slice index) code (fun _ _ => True) := + (prepareLanes_rel p pass lane slice index).seq (window_rel.seq tail_secret_rel) + +end VG.Proof.Argon2.X86_64.ReferenceMap diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapFinish.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapFinish.lean new file mode 100644 index 000000000..3ef35cc99 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapFinish.lean @@ -0,0 +1,48 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapRelative +import VerifiedGarbage.Proof.Argon2.X86_64.Wrap + +/-! Wrap the selected relative position into the lane's columns. -/ + +namespace VG.Proof.Argon2.X86_64.ReferenceMap + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.ReferenceMap + +structure Result (p : Spec.Argon2.Params) (pass lane slice index : Nat) (s t : State) : Prop where + selected : t.gpr .r9 = BitVec.ofNat 64 (chosenLane p pass lane slice (s.gpr .rdi)) + column : t.gpr .rdi = BitVec.ofNat 64 + ((windowStart p pass slice + relativeValue p pass lane slice index (s.gpr .rdi)) % p.laneLen) + original : t.gpr .r11 = s.gpr .rdi + position : Position p lane slice index t + keeps : Divide.Keeps changed s t + +theorem finish_ok (s a : State) (p : Spec.Argon2.Params) (pass lane slice index : Nat) + (bounds : Bounds p pass lane slice index) (mapped : Mapped p pass lane slice index s a) : + WP isa finish a (Result p pass lane slice index s) := by + unfold finish + refine WP.seq ((wrapArgs_ok a).mono ?_) + rintro b ⟨sum, length, kb⟩ + have sumWord : b.gpr .rdi = BitVec.ofNat 64 + (windowStart p pass slice + relativeValue p pass lane slice index (s.gpr .rdi)) := by + rw [sum, mapped.relative, mapped.start, ← BitVec.ofNat_add, Nat.add_comm] + have sumBound : windowStart p pass slice + relativeValue p pass lane slice index (s.gpr .rdi) + < 2 ^ 64 := by + have small := bounds.sum_bound (s.gpr .rdi) + have q := bounds.laneLength_bound + omega + have sumNat : (b.gpr .rdi).toNat = + windowStart p pass slice + relativeValue p pass lane slice index (s.gpr .rdi) := by + rw [sumWord, word_nat _ sumBound] + have lengthNat : (b.gpr .rsi).toNat = p.laneLen := by + rw [length, mapped.position.laneLength, + word_nat _ (Nat.lt_trans bounds.laneLength_bound (by decide))] + refine (Wrap.code_nat_ok b (by rw [sumNat, lengthNat]; exact bounds.sum_bound _)).mono ?_ + rintro t ⟨out, kt⟩ + have kb' : Divide.Keeps changed a b := kb.mono (by decide) + have kt' : Divide.Keeps changed b t := kt.mono (by decide) + refine ⟨?_, ?_, ?_, mapped.position.of_keeps (kb'.trans kt'), + mapped.keeps.trans (kb'.trans kt')⟩ + · exact (kt.regs .r9 (by decide)).trans ((kb.regs .r9 (by decide)).trans mapped.selected) + · rw [out, sumNat, lengthNat] + · exact (kt.regs .r11 (by decide)).trans ((kb.regs .r11 (by decide)).trans mapped.original) + +end VG.Proof.Argon2.X86_64.ReferenceMap diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapLane.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapLane.lean new file mode 100644 index 000000000..884bbfe01 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapLane.lean @@ -0,0 +1,83 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapState +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceLane +import VerifiedGarbage.Proof.Argon2.X86_64.FirstLane + +/-! Choose the reference lane, restore the pass and prepare the window inputs. -/ + +namespace VG.Proof.Argon2.X86_64.ReferenceMap + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.ReferenceMap + +structure Chosen (p : Spec.Argon2.Params) (pass lane slice index : Nat) (s t : State) : Prop where + selected : t.gpr .r8 = BitVec.ofNat 64 (chosenLane p pass lane slice (s.gpr .rdi)) + pass : t.gpr .r9 = BitVec.ofNat 64 pass + original : t.gpr .r11 = s.gpr .rdi + position : Position p lane slice index t + keeps : Divide.Keeps changed s t + +theorem chooseLane_ok (s : State) (p : Spec.Argon2.Params) (pass lane slice index : Nat) + (ready : Ready p pass lane slice index s) : + WP isa chooseLane s (Chosen p pass lane slice index s) := by + have lanesNat : (s.gpr .rsi).toNat = p.lanes := by + rw [ready.lanes, word_nat _ (by have := ready.bounds.lanesBound; omega)] + unfold chooseLane + refine WP.seq ((ReferenceLane.code_ok s + (by rw [lanesNat]; exact ready.bounds.lanesPositive) + (by rw [lanesNat]; exact ready.bounds.lanesBound)).mono ?_) + rintro a ⟨laneNat, original, ka⟩ + have ka' : Divide.Keeps changed s a := ka.mono (by decide) + have readA : InRegions (a.rd ++ a.wr) (a.gpr .rbp) 8 := by + rw [ka'.rd, ka'.wr, ka'.regs .rbp (by decide)] + exact ready.passRead + have laneWord : a.gpr .r8 = BitVec.ofNat 64 ((s.gpr .rdi >>> 32).toNat % p.lanes) := by + calc + a.gpr .r8 = BitVec.ofNat 64 (a.gpr .r8).toNat := by simp only [BitVec.ofNat_toNat, BitVec.setWidth_eq] + _ = _ := by rw [laneNat, lanesNat] + refine WP.seq ((loadPass_ok a readA).mono ?_) + rintro b ⟨loaded, kb⟩ + have kb' : Divide.Keeps changed a b := kb.mono (by decide) + have kab := ka'.trans kb' + have pb := ready.position.of_keeps kab + have passWord : b.gpr .r9 = BitVec.ofNat 64 pass := by + rw [loaded, ka'.mem, ka'.regs .rbp (by decide)] + exact ready.passWord + have passZero : b.gpr .r9 = 0 ↔ pass = 0 := by + rw [passWord] + exact word_zero _ (by have := ready.bounds.passBound; omega) + have sliceZero : b.gpr .r14 = 0 ↔ slice = 0 := by + rw [pb.slice] + exact word_zero _ (by have := ready.bounds.sliceBound; omega) + refine (FirstLane.code_ok b).mono ?_ + rintro t ⟨out, kt⟩ + have kt' : Divide.Keeps changed b t := kt.mono (by decide) + refine ⟨?_, ?_, ?_, ready.position.of_keeps (kab.trans kt'), kab.trans kt'⟩ + · rw [out] + by_cases position : pass = 0 ∧ slice = 0 <;> + simp only [passZero, sliceZero, pb.current, kb.regs .r8 (by decide), laneWord, + chosenLane, position, and_self, ite_true, ite_false] + · exact (kt.regs .r9 (by decide)).trans passWord + · exact (kt.regs .r11 (by decide)).trans ((kb.regs .r11 (by decide)).trans original) + +structure Prepared (p : Spec.Argon2.Params) (pass lane slice index : Nat) (s t : State) : Prop where + selected : t.gpr .rdi = BitVec.ofNat 64 (chosenLane p pass lane slice (s.gpr .rdi)) + current : t.gpr .rsi = BitVec.ofNat 64 lane + pass : (t.gpr .r9).toNat = pass + original : t.gpr .r11 = s.gpr .rdi + position : Position p lane slice index t + keeps : Divide.Keeps changed s t + +theorem prepareLanes_ok (s : State) (p : Spec.Argon2.Params) (pass lane slice index : Nat) + (ready : Ready p pass lane slice index s) : + WP isa prepareLanes s (Prepared p pass lane slice index s) := by + unfold prepareLanes + refine WP.seq ((chooseLane_ok s p pass lane slice index ready).mono ?_) + intro a ha + refine (laneArgs_ok a).mono ?_ + rintro t ⟨laneOut, currentOut, kt⟩ + have kt' : Divide.Keeps changed a t := kt.mono (by decide) + refine ⟨laneOut.trans ha.selected, currentOut.trans ha.position.current, ?_, + (kt.regs .r11 (by decide)).trans ha.original, + ha.position.of_keeps kt', ha.keeps.trans kt'⟩ + rw [kt.regs .r9 (by decide), ha.pass, word_nat _ (by have := ready.bounds.passBound; omega)] + +end VG.Proof.Argon2.X86_64.ReferenceMap diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapLaneCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapLaneCT.lean new file mode 100644 index 000000000..a22007d7c --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapLaneCT.lean @@ -0,0 +1,115 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapLane +import VerifiedGarbage.Proof.Argon2.X86_64.FirstLaneCT +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapWindowCT + +/-! Recover the public pass from the frame without exposing the secret word. -/ + +namespace VG.Proof.Argon2.X86_64.ReferenceMap + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.ReferenceMap + +def Related (p : Spec.Argon2.Params) (pass lane slice index : Nat) (s t : State) : Prop := + Ready p pass lane slice index s ∧ Ready p pass lane slice index t ∧ s.gpr .rbp = t.gpr .rbp + +theorem division_keeps (s : State) (p : Spec.Argon2.Params) (pass lane slice index : Nat) + (ready : Ready p pass lane slice index s) : + WP isa VG.Impl.Argon2.X86_64.ReferenceLane.code s fun t => + Ready p pass lane slice index t ∧ Divide.Keeps changed s t := by + refine (ReferenceLane.code_ok s + (by rw [ready.lanes_nat]; exact ready.bounds.lanesPositive) + (by rw [ready.lanes_nat]; exact ready.bounds.lanesBound)).mono ?_ + rintro t ⟨_, _, keeps⟩ + have k : Divide.Keeps changed s t := keeps.mono (by decide) + exact ⟨ready.of_keeps k (keeps.regs .rsi (by decide)), k⟩ + +theorem division_rel (p : Spec.Argon2.Params) (pass lane slice index : Nat) : + RelCT isa (Related p pass lane slice index) + VG.Impl.Argon2.X86_64.ReferenceLane.code (Related p pass lane slice index) := by + have full := (ReferenceLane.code_secret_rel.mono + (P' := Related p pass lane slice index) (fun _ _ _ => trivial) + (fun _ _ h => h)).wpDep (fun s t hp => + ⟨division_keeps s p pass lane slice index hp.1, + division_keeps t p pass lane slice index hp.2.1⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + exact ⟨ha.1, hb.1, (ha.2.regs .rbp (by decide)).trans + (hp.2.2.trans (hb.2.regs .rbp (by decide)).symm)⟩ + +theorem loadPass_rel : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) + (.block loadPass) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + subst r + exact h)) (by taint_decide) + +def Loaded (p : Spec.Argon2.Params) (pass lane slice index : Nat) (s t : State) : Prop := + Related p pass lane slice index s t ∧ + s.gpr .r9 = BitVec.ofNat 64 pass ∧ t.gpr .r9 = BitVec.ofNat 64 pass + +theorem loadPass_public (s : State) (p : Spec.Argon2.Params) (pass lane slice index : Nat) + (ready : Ready p pass lane slice index s) : WP isa (.block loadPass) s fun t => + Ready p pass lane slice index t ∧ t.gpr .r9 = BitVec.ofNat 64 pass ∧ + Divide.Keeps changed s t := by + refine (loadPass_ok s ready.passRead).mono ?_ + rintro t ⟨loaded, keeps⟩ + have k : Divide.Keeps changed s t := keeps.mono (by decide) + exact ⟨ready.of_keeps k (keeps.regs .rsi (by decide)), loaded.trans ready.passWord, k⟩ + +theorem loadPass_public_rel (p : Spec.Argon2.Params) (pass lane slice index : Nat) : + RelCT isa (Related p pass lane slice index) (.block loadPass) + (Loaded p pass lane slice index) := by + have full := (loadPass_rel.mono (P' := Related p pass lane slice index) + (fun _ _ h => h.2.2) (fun _ _ h => h)).wpDep (fun s t hp => + ⟨loadPass_public s p pass lane slice index hp.1, + loadPass_public t p pass lane slice index hp.2.1⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + exact ⟨⟨ha.1, hb.1, (ha.2.2.regs .rbp (by decide)).trans + (hp.2.2.trans (hb.2.2.regs .rbp (by decide)).symm)⟩, ha.2.1, hb.2.1⟩ + +theorem firstLane_loaded_rel (p : Spec.Argon2.Params) (pass lane slice index : Nat) : + RelCT isa (Loaded p pass lane slice index) VG.Impl.Argon2.X86_64.FirstLane.code + (Loaded p pass lane slice index) := by + have trace := FirstLane.code_rel.mono (P' := Loaded p pass lane slice index) + (fun _ _ hp => ⟨hp.2.1.trans hp.2.2.symm, + hp.1.1.position.slice.trans hp.1.2.1.position.slice.symm⟩) (fun _ _ h => h) + have full := trace.wpDep (fun s t _ => ⟨FirstLane.code_ok s, FirstLane.code_ok t⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + have ka : Divide.Keeps changed s a := ha.2.mono (by decide) + have kb : Divide.Keeps changed t b := hb.2.mono (by decide) + refine ⟨⟨hp.1.1.of_keeps ka (ha.2.regs .rsi (by decide)), + hp.1.2.1.of_keeps kb (hb.2.regs .rsi (by decide)), + (ka.regs .rbp (by decide)).trans (hp.1.2.2.trans (kb.regs .rbp (by decide)).symm)⟩, ?_, ?_⟩ + · exact (ha.2.regs .r9 (by decide)).trans hp.2.1 + · exact (hb.2.regs .r9 (by decide)).trans hp.2.2 + +theorem laneArgs_secret_rel : RelCT isa (fun _ _ => True) (.block laneArgs) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by simp)) (by taint_decide) + +theorem prepareLanes_rel (p : Spec.Argon2.Params) (pass lane slice index : Nat) : + RelCT isa (Related p pass lane slice index) prepareLanes PublicPosition := by + have head := (division_rel p pass lane slice index).seq + ((loadPass_public_rel p pass lane slice index).seq (firstLane_loaded_rel p pass lane slice index)) + have trace := head.seq (laneArgs_secret_rel.mono (fun _ _ _ => trivial) (fun _ _ h => h)) + have full := trace.wpDep (fun s t hp => + ⟨prepareLanes_ok s p pass lane slice index hp.1, + prepareLanes_ok t p pass lane slice index hp.2.1⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, _, _, _, ha, hb⟩ := h + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · apply BitVec.eq_of_toNat_eq + exact ha.pass.trans hb.pass.symm + · exact ha.position.slice.trans hb.position.slice.symm + · exact ha.position.segmentLength.trans hb.position.segmentLength.symm + +end VG.Proof.Argon2.X86_64.ReferenceMap diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapLit.lean new file mode 100644 index 000000000..d146119a0 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapLit.lean @@ -0,0 +1,10 @@ +import VerifiedGarbage.Proof.Framework.X86_64.Lit +import VerifiedGarbage.Impl.Argon2.X86_64.ReferenceMap + +/-! A checked literal for complete reference-index mapping. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.ReferenceMap.code + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapRelative.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapRelative.lean new file mode 100644 index 000000000..6052f06fe --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapRelative.lean @@ -0,0 +1,55 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapWindow +import VerifiedGarbage.Proof.Argon2.X86_64.Relative +import VerifiedGarbage.Proof.Framework.X86_64.Abi + +/-! Apply the squared J₁ mapping while retaining the lane and window start. -/ + +namespace VG.Proof.Argon2.X86_64.ReferenceMap + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.ReferenceMap + +theorem relativeWord_ok (s : State) (positive : 0 < (s.gpr .rsi).toNat) + (bound : (s.gpr .rsi).toNat < 2 ^ 32) : + WP isa VG.Impl.Argon2.X86_64.Relative.code s fun t => + t.gpr .rax = BitVec.ofNat 64 + ((s.gpr .rsi).toNat - 1 - (s.gpr .rsi).toNat * + ((s.gpr .rdi &&& 0xffffffff).toNat * (s.gpr .rdi &&& 0xffffffff).toNat / 2 ^ 32) / + 2 ^ 32) ∧ Divide.Keeps [.rax, .rdx, .rcx] s t := by + refine WP.mono_mx (by decide +kernel) (Relative.code_nat_ok s positive bound) ?_ + rintro t ⟨out, other, mem, rd, wr⟩ mx + refine ⟨out, ⟨?_, mem, rd, wr, mx⟩⟩ + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false, not_or] at hr + exact other r hr.1 hr.2.1 hr.2.2 + +structure Mapped (p : Spec.Argon2.Params) (pass lane slice index : Nat) (s t : State) : Prop where + selected : t.gpr .r9 = BitVec.ofNat 64 (chosenLane p pass lane slice (s.gpr .rdi)) + relative : t.gpr .rax = BitVec.ofNat 64 (relativeValue p pass lane slice index (s.gpr .rdi)) + start : t.gpr .r10 = BitVec.ofNat 64 (windowStart p pass slice) + original : t.gpr .r11 = s.gpr .rdi + position : Position p lane slice index t + keeps : Divide.Keeps changed s t + +theorem relative_ok (s a : State) (p : Spec.Argon2.Params) (pass lane slice index : Nat) + (bounds : Bounds p pass lane slice index) (counted : Counted p pass lane slice index s a) : + WP isa relative a (Mapped p pass lane slice index s) := by + unfold relative + refine WP.seq ((relativeArgs_ok a).mono ?_) + rintro b ⟨selected, random, count, kb⟩ + have countNat : (b.gpr .rsi).toNat = windowSize p pass lane slice index (s.gpr .rdi) := by + rw [count, counted.count, word_nat _ (Nat.lt_trans (bounds.windowSize_bound32 _) (by decide))] + have randomWord : b.gpr .rdi = s.gpr .rdi := random.trans counted.original + refine (relativeWord_ok b + (by rw [countNat]; exact bounds.windowSize_positive _) + (by rw [countNat]; exact bounds.windowSize_bound32 _)).mono ?_ + rintro t ⟨out, kt⟩ + have kb' : Divide.Keeps changed a b := kb.mono (by decide) + have kt' : Divide.Keeps changed b t := kt.mono (by decide) + refine ⟨?_, ?_, ?_, ?_, counted.position.of_keeps (kb'.trans kt'), + counted.keeps.trans (kb'.trans kt')⟩ + · exact (kt.regs .r9 (by decide)).trans (selected.trans counted.selected) + · simpa only [relativeValue, countNat, randomWord] using out + · exact (kt.regs .r10 (by decide)).trans ((kb.regs .r10 (by decide)).trans counted.start) + · exact (kt.regs .r11 (by decide)).trans ((kb.regs .r11 (by decide)).trans counted.original) + +end VG.Proof.Argon2.X86_64.ReferenceMap diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapState.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapState.lean new file mode 100644 index 000000000..593f7b09c --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapState.lean @@ -0,0 +1,185 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapArgs +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceCount + +/-! Parameters and register invariants for the complete reference mapping. -/ + +namespace VG.Proof.Argon2.X86_64.ReferenceMap + +open VG VG.X86_64 + +/-- Every stage preserves the enclosing loop's callee-saved registers. -/ +def changed : List Reg := [.rax, .rdx, .rcx, .r8, .r9, .r10, .r11, .rdi, .rsi] + +structure Bounds (p : Spec.Argon2.Params) (pass lane slice index : Nat) : Prop where + lanesPositive : 0 < p.lanes + lanesBound : p.lanes < 2 ^ 32 + memoryMinimum : 8 * p.lanes ≤ p.memory + memoryBound : p.memory < 2 ^ 32 + passBound : pass < 2 ^ 32 + laneBound : lane < p.lanes + sliceBound : slice < 4 + indexBound : index < p.segmentLen + active : pass ≠ 0 ∨ slice ≠ 0 ∨ 2 ≤ index + +structure Position (p : Spec.Argon2.Params) (lane slice index : Nat) (s : State) : Prop where + current : s.gpr .rbx = BitVec.ofNat 64 lane + laneLength : s.gpr .r12 = BitVec.ofNat 64 p.laneLen + segmentLength : s.gpr .r13 = BitVec.ofNat 64 p.segmentLen + slice : s.gpr .r14 = BitVec.ofNat 64 slice + index : s.gpr .r15 = BitVec.ofNat 64 index + +theorem Position.of_keeps {s t : State} {p : Spec.Argon2.Params} {lane slice index : Nat} + (h : Position p lane slice index s) (k : Divide.Keeps changed s t) : + Position p lane slice index t := + ⟨(k.regs .rbx (by decide)).trans h.current, + (k.regs .r12 (by decide)).trans h.laneLength, + (k.regs .r13 (by decide)).trans h.segmentLength, + (k.regs .r14 (by decide)).trans h.slice, + (k.regs .r15 (by decide)).trans h.index⟩ + +structure Ready (p : Spec.Argon2.Params) (pass lane slice index : Nat) (s : State) : Prop where + bounds : Bounds p pass lane slice index + position : Position p lane slice index s + lanes : s.gpr .rsi = BitVec.ofNat 64 p.lanes + passRead : InRegions (s.rd ++ s.wr) (s.gpr .rbp) 8 + passWord : s.mem.readW (s.gpr .rbp) 64 = BitVec.ofNat 64 pass + +theorem Ready.lanes_nat {p : Spec.Argon2.Params} {pass lane slice index : Nat} {s : State} + (h : Ready p pass lane slice index s) : (s.gpr .rsi).toNat = p.lanes := by + rw [h.lanes, BitVec.toNat_ofNat, Nat.mod_eq_of_lt (Nat.lt_trans h.bounds.lanesBound (by decide))] + +theorem Ready.of_keeps {p : Spec.Argon2.Params} {pass lane slice index : Nat} {s t : State} + (h : Ready p pass lane slice index s) (k : Divide.Keeps changed s t) + (lanes : t.gpr .rsi = s.gpr .rsi) : Ready p pass lane slice index t := by + refine ⟨h.bounds, h.position.of_keeps k, lanes.trans h.lanes, ?_, ?_⟩ + · rw [k.rd, k.wr, k.regs .rbp (by decide)] + exact h.passRead + · rw [k.mem, k.regs .rbp (by decide)] + exact h.passWord + +def chosenLane (p : Spec.Argon2.Params) (pass lane slice : Nat) (random : Addr) : Nat := + if pass = 0 ∧ slice = 0 then lane else (random >>> 32).toNat % p.lanes + +theorem chosenLane_bound (p : Spec.Argon2.Params) (pass lane slice : Nat) (random : Addr) + (positive : 0 < p.lanes) (bound : lane < p.lanes) : + chosenLane p pass lane slice random < p.lanes := by + unfold chosenLane + split + · exact bound + · exact Nat.mod_lt _ positive + +theorem word_nat (n : Nat) (bound : n < 2 ^ 64) : (BitVec.ofNat 64 n).toNat = n := by + rw [BitVec.toNat_ofNat, Nat.mod_eq_of_lt bound] + +theorem word_zero (n : Nat) (bound : n < 2 ^ 64) : BitVec.ofNat 64 n = (0 : Addr) ↔ n = 0 := by + constructor + · intro h + have hn := congrArg BitVec.toNat h + rw [word_nat n bound] at hn + exact hn + · intro h; rw [h]; rfl + +theorem word_eq (x y : Nat) (hx : x < 2 ^ 64) (hy : y < 2 ^ 64) : + BitVec.ofNat 64 x = BitVec.ofNat 64 y ↔ x = y := by + constructor + · intro h + have hn := congrArg BitVec.toNat h + rw [word_nat x hx, word_nat y hy] at hn + exact hn + · intro h; rw [h] + +theorem Bounds.laneLength_bound {p : Spec.Argon2.Params} {pass lane slice index : Nat} + (h : Bounds p pass lane slice index) : p.laneLen < 2 ^ 32 := by + have hb : p.laneLen ≤ p.blocks := by + rw [Proof.Argon2.blocks_lanes p h.lanesPositive] + exact Nat.le_mul_of_pos_left _ h.lanesPositive + exact Nat.lt_of_le_of_lt (Nat.le_trans hb (Proof.Argon2.blocks_le_memory p)) h.memoryBound + +theorem Bounds.window_positive {p : Spec.Argon2.Params} {pass lane slice index : Nat} + (h : Bounds p pass lane slice index) : + 0 < ReferenceCount.windowBase p pass slice + index ∧ + (index = 0 → 0 < ReferenceCount.windowBase p pass slice) := by + have hp := Proof.Argon2.reference_count_positive p h.lanesPositive h.memoryMinimum + pass slice index true h.active (by intro _ _; rfl) + rw [ReferenceCount.spec_count] at hp + change 0 < ReferenceCount.windowBase p pass slice + index - 1 at hp + constructor + · omega + · intro zero; rw [zero, Nat.add_zero] at hp; omega + +def windowSize (p : Spec.Argon2.Params) (pass lane slice index : Nat) (random : Addr) : Nat := + Spec.Argon2.referenceCount p pass slice index (chosenLane p pass lane slice random == lane) + +def windowStart (p : Spec.Argon2.Params) (pass slice : Nat) : Nat := + if pass = 0 then 0 else (slice + 1) * p.segmentLen % p.laneLen + +def relativeValue (p : Spec.Argon2.Params) (pass lane slice index : Nat) (random : Addr) : Nat := + let count := windowSize p pass lane slice index random + let j := (random &&& 0xffffffff).toNat + count - 1 - count * (j * j / 2 ^ 32) / 2 ^ 32 + +theorem Bounds.segment_le_lane {p : Spec.Argon2.Params} {pass lane slice index : Nat} + (h : Bounds p pass lane slice index) : p.segmentLen ≤ p.laneLen := by + have segments := Proof.Argon2.laneLen_segments p h.lanesPositive + omega + +theorem Bounds.index_bound64 {p : Spec.Argon2.Params} {pass lane slice index : Nat} + (h : Bounds p pass lane slice index) : index < 2 ^ 64 := + Nat.lt_trans (Nat.lt_of_lt_of_le h.indexBound h.segment_le_lane) + (Nat.lt_trans h.laneLength_bound (by decide)) + +theorem Bounds.chosenLane_bound64 {p : Spec.Argon2.Params} {pass lane slice index : Nat} + (h : Bounds p pass lane slice index) (random : Addr) : + chosenLane p pass lane slice random < 2 ^ 64 := + Nat.lt_trans (chosenLane_bound p pass lane slice random h.lanesPositive h.laneBound) + (Nat.lt_trans h.lanesBound (by decide)) + +theorem Bounds.lane_bound64 {p : Spec.Argon2.Params} {pass lane slice index : Nat} + (h : Bounds p pass lane slice index) : lane < 2 ^ 64 := + Nat.lt_trans h.laneBound (Nat.lt_trans h.lanesBound (by decide)) + +theorem Bounds.windowSize_positive {p : Spec.Argon2.Params} {pass lane slice index : Nat} + (h : Bounds p pass lane slice index) (random : Addr) : + 0 < windowSize p pass lane slice index random := by + apply Proof.Argon2.reference_count_positive p h.lanesPositive h.memoryMinimum + pass slice index _ h.active + intro firstPass firstSlice + simp only [chosenLane, firstPass, firstSlice, and_self, ite_true] + exact beq_iff_eq.mpr rfl + +theorem Bounds.windowSize_bound32 {p : Spec.Argon2.Params} {pass lane slice index : Nat} + (h : Bounds p pass lane slice index) (random : Addr) : + windowSize p pass lane slice index random < 2 ^ 32 := + Proof.Argon2.reference_count_32 p h.lanesPositive h.memoryMinimum h.memoryBound + pass slice index _ h.sliceBound h.indexBound + +theorem Bounds.windowSize_lt_lane {p : Spec.Argon2.Params} {pass lane slice index : Nat} + (h : Bounds p pass lane slice index) (random : Addr) : + windowSize p pass lane slice index random < p.laneLen := + Proof.Argon2.reference_count_lt_lane p h.lanesPositive h.memoryMinimum + pass slice index _ h.sliceBound h.indexBound + +theorem Bounds.laneLength_positive {p : Spec.Argon2.Params} {pass lane slice index : Nat} + (h : Bounds p pass lane slice index) : 0 < p.laneLen := by + have seg := Proof.Argon2.segmentLen_ge_two p h.lanesPositive h.memoryMinimum + have len := Proof.Argon2.laneLen_segments p h.lanesPositive + omega + +theorem Bounds.windowStart_lt_lane {p : Spec.Argon2.Params} {pass lane slice index : Nat} + (h : Bounds p pass lane slice index) : windowStart p pass slice < p.laneLen := by + unfold windowStart + split + · exact h.laneLength_positive + · exact Nat.mod_lt _ h.laneLength_positive + +theorem Bounds.sum_bound {p : Spec.Argon2.Params} {pass lane slice index : Nat} + (h : Bounds p pass lane slice index) (random : Addr) : + windowStart p pass slice + relativeValue p pass lane slice index random < 2 * p.laneLen := by + have start := h.windowStart_lt_lane + have relative := Proof.Argon2.reference_relative_bound _ (random &&& 0xffffffff).toNat + (h.windowSize_positive random) + have count := h.windowSize_lt_lane random + change relativeValue p pass lane slice index random < windowSize p pass lane slice index random at relative + omega + +end VG.Proof.Argon2.X86_64.ReferenceMap diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapWindow.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapWindow.lean new file mode 100644 index 000000000..705491c15 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapWindow.lean @@ -0,0 +1,52 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapLane +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceStart + +/-! The selected eligible window and its chronological starting column. -/ + +namespace VG.Proof.Argon2.X86_64.ReferenceMap + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.ReferenceMap + +structure Counted (p : Spec.Argon2.Params) (pass lane slice index : Nat) (s t : State) : Prop where + selected : t.gpr .rdi = BitVec.ofNat 64 (chosenLane p pass lane slice (s.gpr .rdi)) + current : t.gpr .rsi = BitVec.ofNat 64 lane + count : t.gpr .r8 = BitVec.ofNat 64 (windowSize p pass lane slice index (s.gpr .rdi)) + start : t.gpr .r10 = BitVec.ofNat 64 (windowStart p pass slice) + original : t.gpr .r11 = s.gpr .rdi + position : Position p lane slice index t + keeps : Divide.Keeps changed s t + +theorem window_ok (s a : State) (p : Spec.Argon2.Params) (pass lane slice index : Nat) + (bounds : Bounds p pass lane slice index) (prepared : Prepared p pass lane slice index s a) : + WP isa window a (Counted p pass lane slice index s) := by + have segmentPositive : 0 < p.segmentLen := + Nat.lt_of_lt_of_le (by decide : 0 < 2) + (Proof.Argon2.segmentLen_ge_two p bounds.lanesPositive bounds.memoryMinimum) + unfold window + refine WP.seq ((ReferenceStart.code_nat_ok a p pass slice bounds.lanesPositive + segmentPositive bounds.sliceBound prepared.pass prepared.position.slice + prepared.position.segmentLength).mono ?_) + rintro b ⟨startWord, kb⟩ + have kb' : Divide.Keeps changed a b := kb.mono (by decide) + have pb := prepared.position.of_keeps kb' + have passB : (b.gpr .r9).toNat = pass := by + rw [kb.regs .r9 (by decide), prepared.pass] + have same : decide (b.gpr .rdi = b.gpr .rsi) = + (chosenLane p pass lane slice (s.gpr .rdi) == lane) := by + apply Bool.eq_iff_iff.mpr + simp only [decide_eq_true_eq, beq_iff_eq] + rw [kb.regs .rdi (by decide), kb.regs .rsi (by decide), prepared.selected, prepared.current] + exact word_eq _ _ (bounds.chosenLane_bound64 _) bounds.lane_bound64 + refine (ReferenceCount.code_nat_ok b p pass slice index passB pb.laneLength + pb.segmentLength pb.slice pb.index bounds.segment_le_lane bounds.index_bound64 + bounds.window_positive.1 bounds.window_positive.2).mono ?_ + rintro t ⟨countWord, kt⟩ + have kt' : Divide.Keeps changed b t := kt.mono (by decide) + refine ⟨?_, ?_, ?_, ?_, ?_, pb.of_keeps kt', prepared.keeps.trans (kb'.trans kt')⟩ + · exact (kt.regs .rdi (by decide)).trans ((kb.regs .rdi (by decide)).trans prepared.selected) + · exact (kt.regs .rsi (by decide)).trans ((kb.regs .rsi (by decide)).trans prepared.current) + · simpa only [windowSize, same] using countWord + · exact (kt.regs .r10 (by decide)).trans startWord + · exact (kt.regs .r11 (by decide)).trans ((kb.regs .r11 (by decide)).trans prepared.original) + +end VG.Proof.Argon2.X86_64.ReferenceMap diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapWindowCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapWindowCT.lean new file mode 100644 index 000000000..84f7f741e --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/ReferenceMapWindowCT.lean @@ -0,0 +1,24 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapState +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceStart +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceStartCT +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceCount + +/-! The chronological window branches only on the public pass and slice. -/ + +namespace VG.Proof.Argon2.X86_64.ReferenceMap + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.ReferenceMap + +def PublicPosition (s t : State) : Prop := + ∀ r ∈ [Reg.r9, .r14, .r13], s.gpr r = t.gpr r + +theorem window_rel : RelCT isa PublicPosition window (fun _ _ => True) := by + have start := ReferenceStart.code_rel.wpDep (fun s t _ => + ⟨ReferenceStart.code_ok s, ReferenceStart.code_ok t⟩) + refine start.seq (ReferenceCount.code_rel.mono ?_ (fun _ _ h => h)) + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + exact (ha.2.regs .r9 (by decide)).trans + ((hp .r9 (by simp)).trans (hb.2.regs .r9 (by decide)).symm) + +end VG.Proof.Argon2.X86_64.ReferenceMap diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetup.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetup.lean new file mode 100644 index 000000000..0362c2dd5 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetup.lean @@ -0,0 +1,43 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.SegmentSetupCheck +import VerifiedGarbage.Proof.Argon2.SegmentStart + +/-! Fill one complete segment, including the initialized prefix and empty suffix. -/ + +namespace VG.Proof.Argon2.X86_64.SegmentSetup + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.SegmentSetup + +theorem code_ok (s : State) (p : Params) (pass lane slice : Nat) + (h : Ready p pass lane slice s) (state : FillState) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks state.memory) : + WP isa code s (FillSegment.Finished s · p pass lane slice + (Proof.Argon2.segment p pass lane slice 0 p.segmentLen state)) := by + rw [Proof.Argon2.segment_start p pass lane slice state h.parameters.segment_bound.1] + change WP isa code s (FillSegment.Finished s · p pass lane slice + (Proof.Argon2.segment p pass lane slice (start pass slice) (p.segmentLen - start pass slice) state)) + unfold code + refine WP.seq ((prepare_ok s p pass lane slice h).mono ?_) + intro a prepared + refine WP.seq ((check_prepared_ok prepared).mono ?_) + rintro b ⟨prepared, flag⟩ + have matrix := prepared.represents h state.memory represented + refine WP.ite (decide (start pass slice < p.segmentLen)) (by simp only [eval, flag]) ?_ ?_ + · intro taken + have bound := of_decide_eq_true taken + have active := prepared.context.activate bound (start_active pass slice) + refine (FillSegment.loop_ok (p.segmentLen - start pass slice) b p pass lane slice (start pass slice) 0 + active state matrix (by omega) (by omega)).mono ?_ + intro t finished + exact finished_prepared prepared finished + · intro skipped + have bound := of_decide_eq_false skipped + have minimum := h.parameters.segment_bound.1 + have last : start pass slice = p.segmentLen := by have := start_le pass slice p.segmentLen minimum; omega + have finished : FillSegment.Finished b b p pass lane slice state := + ⟨matrix, rfl, rfl, last ▸ prepared.context.position, prepared.context.layout, + ⟨0, prepared.context.cache⟩, prepared.context.matrixWork, prepared.context.cache.words.passWord, + prepared.context.lanesWord, fun _ _ _ => rfl, rfl, rfl, Frame.refl _ _, rfl⟩ + rw [last, Nat.sub_self, Proof.Argon2.segment_zero] + exact WP.block_nil (finished_prepared prepared finished) + +end VG.Proof.Argon2.X86_64.SegmentSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupCT.lean new file mode 100644 index 000000000..7fa183e8d --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupCT.lean @@ -0,0 +1,120 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.SegmentSetup +import VerifiedGarbage.Proof.Argon2.X86_64.SegmentSetupTrace +import VerifiedGarbage.Proof.Argon2.X86_64.FillSegmentCT + +/-! Complete segment setup and filling expose only the specified reference log. -/ + +namespace VG.Proof.Argon2.X86_64.SegmentSetup + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.SegmentSetup + +structure Related (p : Params) (pass lane slice : Nat) (leftState rightState : FillState) (s t : State) : Prop where + ready : RelatedReady p pass lane slice s t + leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory + indices : (Proof.Argon2.segment p pass lane slice 0 p.segmentLen leftState).indices = + (Proof.Argon2.segment p pass lane slice 0 p.segmentLen rightState).indices + +structure PreparedRelated (p : Params) (pass lane slice : Nat) (leftState rightState : FillState) + (s t : State) : Prop where + left : FillContext.Ready p pass lane slice (start pass slice) 0 s + right : FillContext.Ready p pass lane slice (start pass slice) 0 t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + matrices : FillKernel.matrix s = FillKernel.matrix t + work : AddressCalls.work s = AddressCalls.work t + leftMatrix : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks leftState.memory + rightMatrix : Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks rightState.memory + indices : (Proof.Argon2.segment p pass lane slice (start pass slice) (p.segmentLen - start pass slice) leftState).indices = + (Proof.Argon2.segment p pass lane slice (start pass slice) (p.segmentLen - start pass slice) rightState).indices + +theorem prepare_public_rel (p : Params) (pass lane slice : Nat) (leftState rightState : FillState) : + RelCT isa (Related p pass lane slice leftState rightState) prepare + (PreparedRelated p pass lane slice leftState rightState) := by + have trace := (prepare_trace p pass lane slice).mono + (P' := Related p pass lane slice leftState rightState) (fun _ _ h => h.ready) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => ⟨prepare_ok s p pass lane slice h.ready.left, prepare_ok t p pass lane slice h.ready.right⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + refine ⟨ha.context, hb.context, ?_, ?_, ha.matrix.trans (hp.ready.matrices.trans hb.matrix.symm), + ha.work.trans (hp.ready.work.trans hb.work.symm), ha.represents hp.ready.left leftState.memory hp.leftMatrix, + hb.represents hp.ready.right rightState.memory hp.rightMatrix, ?_⟩ + · rw [ha.regs .rbp (by simp [calleeSaved]) (by decide), hb.regs .rbp (by simp [calleeSaved]) (by decide)] + exact hp.ready.bases + · rw [ha.regs .rsp (by simp [calleeSaved]) (by decide), hb.regs .rsp (by simp [calleeSaved]) (by decide)] + exact hp.ready.stacks + · have indices := hp.indices + rw [Proof.Argon2.segment_start p pass lane slice leftState hp.ready.left.parameters.segment_bound.1, + Proof.Argon2.segment_start p pass lane slice rightState hp.ready.right.parameters.segment_bound.1] at indices + exact indices + +theorem PreparedRelated.of_keeps {p : Params} {pass lane slice : Nat} {leftState rightState : FillState} + {s t a b : State} (h : PreparedRelated p pass lane slice leftState rightState s t) + (ka : Divide.Keeps [] s a) (kb : Divide.Keeps [] t b) : + PreparedRelated p pass lane slice leftState rightState a b := by + refine ⟨h.left.of_keeps (ka.mono (by decide)), h.right.of_keeps (kb.mono (by decide)), ?_, ?_, ?_, ?_, ?_, ?_, h.indices⟩ + · rw [ka.regs .rbp (by simp), kb.regs .rbp (by simp)]; exact h.bases + · rw [ka.regs .rsp (by simp), kb.regs .rsp (by simp)]; exact h.stacks + · unfold FillKernel.matrix + rw [ka.mem, kb.mem, ka.regs .rbp (by simp), kb.regs .rbp (by simp)]; exact h.matrices + · unfold AddressCalls.work + rw [ka.mem, kb.mem, ka.regs .rbp (by simp), kb.regs .rbp (by simp)]; exact h.work + · unfold FillKernel.matrix; rw [ka.mem, ka.regs .rbp (by simp)]; exact h.leftMatrix + · unfold FillKernel.matrix; rw [kb.mem, kb.regs .rbp (by simp)]; exact h.rightMatrix + +theorem check_context_ok (s : State) (p : Params) (pass lane slice : Nat) + (h : FillContext.Ready p pass lane slice (start pass slice) 0 s) : WP isa (.block check) s fun t => + t.cf = decide (start pass slice < p.segmentLen) ∧ Divide.Keeps [] s t := by + refine (check_ok s).mono ?_ + rintro t ⟨flag, keeps⟩ + have minimum := h.parameters.segment_bound + refine ⟨?_, keeps⟩ + rw [flag, h.position.index, h.position.segmentLength, + ReferenceMap.word_nat (start pass slice) (Nat.lt_of_le_of_lt (start_le _ _ _ minimum.1) minimum.2), + ReferenceMap.word_nat p.segmentLen minimum.2] + +theorem check_trace : RelCT isa (fun _ _ : State => True) (.block check) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by intro r hr; simp at hr)) (by taint_decide) + +theorem check_public_rel (p : Params) (pass lane slice : Nat) (leftState rightState : FillState) : + RelCT isa (PreparedRelated p pass lane slice leftState rightState) (.block check) + (fun s t => PreparedRelated p pass lane slice leftState rightState s t ∧ + s.cf = decide (start pass slice < p.segmentLen) ∧ t.cf = decide (start pass slice < p.segmentLen)) := by + have trace := check_trace.mono (P' := PreparedRelated p pass lane slice leftState rightState) + (fun _ _ _ => trivial) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => ⟨check_context_ok s p pass lane slice h.left, check_context_ok t p pass lane slice h.right⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ⟨fa, ka⟩, ⟨fb, kb⟩⟩ := h + exact ⟨hp.of_keeps ka kb, fa, fb⟩ + +theorem code_rel (p : Params) (pass lane slice : Nat) (leftState rightState : FillState) : + RelCT isa (Related p pass lane slice leftState rightState) code (fun _ _ => True) := by + have branches : RelCT isa + (fun s t => PreparedRelated p pass lane slice leftState rightState s t ∧ + s.cf = decide (start pass slice < p.segmentLen) ∧ t.cf = decide (start pass slice < p.segmentLen)) + (.ite .b Impl.Argon2.X86_64.FillSegment.loop (.block [])) (fun _ _ => True) := by + refine RelCT.ite (by intro s t h; simp only [eval, h.2.1, h.2.2]) ?_ ?_ + · intro s t ts tt a b hp ea eb + have active : start pass slice < p.segmentLen := by + have taken := hp.2 + simp only [eval, hp.1.2.1, Option.some.injEq, decide_eq_true_eq] at taken + exact taken + have left := hp.1.1.left.activate active (start_active pass slice) + have right := hp.1.1.right.activate active (start_active pass slice) + have related : FillSegment.Related p pass lane slice (start pass slice) + (p.segmentLen - start pass slice) 0 leftState rightState s t := + ⟨⟨left, right, hp.1.1.bases, hp.1.1.stacks, hp.1.1.matrices, hp.1.1.work⟩, + hp.1.1.leftMatrix, hp.1.1.rightMatrix, hp.1.1.indices⟩ + exact FillSegment.loop_rel p pass lane slice (start pass slice) (p.segmentLen - start pass slice) 0 + leftState rightState (by omega) (by omega) _ _ _ _ _ _ related ea eb + · have noop : RelCT isa (fun _ _ : State => True) (.block []) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by intro r hr; simp at hr)) (by taint_decide) + exact noop.mono (fun _ _ _ => trivial) (fun _ _ h => h) + exact (prepare_public_rel p pass lane slice leftState rightState).seq + ((check_public_rel p pass lane slice leftState rightState).seq branches) + +end VG.Proof.Argon2.X86_64.SegmentSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupCheck.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupCheck.lean new file mode 100644 index 000000000..48fcc0e76 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupCheck.lean @@ -0,0 +1,49 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.SegmentSetupPrepare + +/-! Check the prepared index before entering the nonempty segment loop. -/ + +namespace VG.Proof.Argon2.X86_64.SegmentSetup + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.SegmentSetup + +theorem Prepared.of_keeps {p : Params} {pass lane slice : Nat} {s a t : State} + (h : Prepared s a p pass lane slice) (k : Divide.Keeps [] a t) : Prepared s t p pass lane slice := by + have bp := k.regs .rbp (by decide) + have base : FillKernel.matrix t = FillKernel.matrix a := by unfold FillKernel.matrix; rw [k.mem, bp] + have work : AddressCalls.work t = AddressCalls.work a := by unfold AddressCalls.work; rw [k.mem, bp] + refine ⟨h.context.of_keeps (k.mono (by decide)), base.trans h.matrix, work.trans h.work, + ?_, k.rd.trans h.rd, k.wr.trans h.wr, ?_, k.mxcsr.trans h.mxcsr⟩ + · intro r hr ne; exact (k.regs r (by simp)).trans (h.regs r hr ne) + · rw [k.mem]; exact h.frame + +theorem check_prepared_ok {p : Params} {pass lane slice : Nat} {s a : State} + (h : Prepared s a p pass lane slice) : WP isa (.block check) a fun t => + Prepared s t p pass lane slice ∧ t.cf = decide (start pass slice < p.segmentLen) := by + refine (check_ok a).mono ?_ + rintro t ⟨flag, keeps⟩ + have minimum := h.context.parameters.segment_bound + refine ⟨h.of_keeps keeps, ?_⟩ + rw [flag, h.context.position.index, h.context.position.segmentLength, + ReferenceMap.word_nat (start pass slice) (Nat.lt_of_le_of_lt (start_le _ _ _ minimum.1) minimum.2), + ReferenceMap.word_nat p.segmentLen minimum.2] + +theorem finished_prepared {p : Params} {pass lane slice : Nat} {s a t : State} {state : FillState} + (prepared : Prepared s a p pass lane slice) (finished : FillSegment.Finished a t p pass lane slice state) : + FillSegment.Finished s t p pass lane slice state := by + refine ⟨finished.represented, finished.matrix.trans prepared.matrix, finished.work.trans prepared.work, + finished.position, finished.layout, finished.cache, finished.matrixWork, finished.passWord, finished.lanesWord, + ?_, finished.rd.trans prepared.rd, finished.wr.trans prepared.wr, ?_, finished.mxcsr.trans prepared.mxcsr⟩ + · intro r hr ne; exact (finished.regs r hr ne).trans (prepared.regs r hr ne) + · have firstFrame : Frame (FillBlock.writes s p) s.mem a.mem := by + apply prepared.frame.sub + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact ⟨⟨off (s.gpr .rbp) 8, 16⟩, by simp [FillBlock.writes], Region.sub_prefix (by decide)⟩ + have rest := finished.frame + rw [FillBlock.writes, prepared.matrix, prepared.work, + prepared.regs .rsp (by simp [calleeSaved]) (by decide), + prepared.regs .rbp (by simp [calleeSaved]) (by decide)] at rest + exact firstFrame.trans rest + +end VG.Proof.Argon2.X86_64.SegmentSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupPrepare.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupPrepare.lean new file mode 100644 index 000000000..650c27c48 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupPrepare.lean @@ -0,0 +1,68 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.SegmentSetupReset + +/-! The prepared context covers ordinary and empty first-segment suffixes. -/ + +namespace VG.Proof.Argon2.X86_64.SegmentSetup + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.SegmentSetup + +structure Prepared (s t : State) (p : Params) (pass lane slice : Nat) : Prop where + context : FillContext.Ready p pass lane slice (start pass slice) 0 t + matrix : FillKernel.matrix t = FillKernel.matrix s + work : AddressCalls.work t = AddressCalls.work s + regs : ∀ r ∈ calleeSaved, r ≠ .r15 → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨off (s.gpr .rbp) 8, 8⟩] s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + +theorem prepare_ok (s : State) (p : Params) (pass lane slice : Nat) (h : Ready p pass lane slice s) : + WP isa prepare s (Prepared s · p pass lane slice) := by + unfold prepare + refine WP.seq ((reset_ok s p pass lane slice h).mono ?_) + intro a reset + refine (index_ok a pass slice (reset.ready.reads 0 (by simp)) reset.words.passWord reset.words.sliceWord + (Nat.lt_trans h.parameters.passBound (by decide)) + (Nat.lt_trans h.parameters.sliceBound (by decide))).mono ?_ + rintro t ⟨value, keeps⟩ + have core := reset.ready.of_state (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl <;> exact keeps.regs _ (by decide)) keeps.mem keeps.rd keeps.wr + have cacheA : AddressCache.Invariant p pass lane slice 0 a := + ⟨reset.ready.addressLayout, reset.ready.reads, reset.ready.write, reset.words, by decide, Or.inl rfl⟩ + have cache := cacheA.of_state (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl <;> exact keeps.regs _ (by decide)) keeps.mem keeps.rd keeps.wr + have base : FillKernel.matrix t = FillKernel.matrix a := by + unfold FillKernel.matrix; rw [keeps.mem, keeps.regs .rbp (by decide)] + have work : AddressCalls.work t = AddressCalls.work a := by + unfold AddressCalls.work; rw [keeps.mem, keeps.regs .rbp (by decide)] + refine ⟨⟨core.parameters, core.layout, cache, core.matrixWork, + ⟨cache.words.laneWord, core.laneLength, core.segmentLength, cache.words.sliceWord, value⟩, core.lanesWord⟩, + base.trans reset.matrix, work.trans reset.work, ?_, keeps.rd.trans reset.rd, + keeps.wr.trans reset.wr, ?_, keeps.mxcsr.trans reset.mxcsr⟩ + · intro r hr ne + have outside : r ∉ [Reg.rcx, .r15] := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> simp_all + exact (keeps.regs r outside).trans (reset.regs r hr) + · rw [keeps.mem]; exact reset.frame + +theorem Prepared.represents {p : Params} {pass lane slice : Nat} {s t : State} + (ready : Ready p pass lane slice s) (h : Prepared s t p pass lane slice) (blocks : Array Block) + (represented : Proof.Argon2.Represents s.mem (FillKernel.matrix s) p.blocks blocks) : + Proof.Argon2.Represents t.mem (FillKernel.matrix t) p.blocks blocks := by + rw [h.matrix] + refine ⟨represented.size, ?_⟩ + intro k hk + apply Eq.trans _ (represented.block k hk) + apply FillCompress.block_frame h.frame + intro r hr + simp only [List.mem_singleton] at hr + subst r + exact (ready.layout.matrixFrame.sub_left (Proof.Argon2.matrixCell_sub _ _ _ hk)).sub_right + (Offset.sub_base _ (by decide)) + +end VG.Proof.Argon2.X86_64.SegmentSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupReady.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupReady.lean new file mode 100644 index 000000000..b2519da98 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupReady.lean @@ -0,0 +1,67 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.SegmentSetupSteps + +/-! Segment setup needs no previously valid cached block. -/ + +namespace VG.Proof.Argon2.X86_64.SegmentSetup + +open VG VG.X86_64 VG.Spec.Argon2 + +structure Ready (p : Params) (pass lane slice : Nat) (s : State) : Prop where + parameters : FillContext.Parameters p pass lane slice + layout : FillKernel.Layout p s + addressLayout : AddressCalls.Ready s + reads : ∀ d ∈ [0, 8, 72, 112, 240], InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) d) 8 + write : InRegions s.wr (off (s.gpr .rbp) 8) 8 + words : ∃ old, AddressHeader.Words p pass lane slice old s + matrixWork : (⟨FillKernel.matrix s, p.blocks * 1024⟩ : Region).Disjoint ⟨AddressCalls.work s, 8192⟩ + laneLength : s.gpr .r12 = BitVec.ofNat 64 p.laneLen + segmentLength : s.gpr .r13 = BitVec.ofNat 64 p.segmentLen + lanesWord : s.mem.readW (off (s.gpr .rbp) 184) 64 = BitVec.ofNat 64 p.lanes + +theorem Ready.of_state {p : Params} {pass lane slice : Nat} {s t : State} + (h : Ready p pass lane slice s) + (regs : ∀ r ∈ [Reg.rbp, .rsp, .rbx, .r12, .r13, .r14], t.gpr r = s.gpr r) + (mem : t.mem = s.mem) (rd : t.rd = s.rd) (wr : t.wr = s.wr) : Ready p pass lane slice t := by + have bp := regs .rbp (by simp) + have sp := regs .rsp (by simp) + have base : FillKernel.matrix t = FillKernel.matrix s := by unfold FillKernel.matrix; rw [mem, bp] + have work : AddressCalls.work t = AddressCalls.work s := by unfold AddressCalls.work; rw [mem, bp] + refine ⟨h.parameters, h.layout.of_preserved bp sp base work rd wr, ?_, ?_, ?_, ?_, ?_, + (regs .r12 (by simp)).trans h.laneLength, (regs .r13 (by simp)).trans h.segmentLength, ?_⟩ + · constructor + · rw [rd, wr, bp]; exact h.addressLayout.frameRead + · rw [wr, work]; exact h.addressLayout.workWrite + · rw [bp, work]; exact h.addressLayout.frameWork + · rw [bp, sp]; exact h.addressLayout.frameStack + · rw [sp, work]; exact h.addressLayout.stackWork + · rw [rd, wr, bp]; exact h.reads + · rw [wr, bp]; exact h.write + · obtain ⟨old, words⟩ := h.words + refine ⟨old, ?_, (regs .rbx (by simp)).trans words.laneWord, + (regs .r14 (by simp)).trans words.sliceWord, ?_, ?_, ?_, ?_⟩ + all_goals rw [mem, bp] + · exact words.passWord + · exact words.blocksWord + · exact words.passesWord + · exact words.variantWord + · exact words.counterWord + · rw [base, work]; exact h.matrixWork + · rw [mem, bp]; exact h.lanesWord + +theorem Ready.saved {p : Params} {pass lane slice : Nat} {s t : State} + (h : Ready p pass lane slice s) (saved : AddressCache.Saved s t) : Ready p pass lane slice t := by + have bp := congrFun saved.regs Reg.rbp + have sp := congrFun saved.regs Reg.rsp + have base : FillKernel.matrix t = FillKernel.matrix s := saved.read 232 (by decide) (by decide) + refine ⟨h.parameters, h.layout.of_preserved bp sp base saved.work_eq saved.rd saved.wr, + saved.ready, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · rw [saved.rd, saved.wr, bp]; exact h.reads + · rw [saved.wr, bp]; exact h.write + · obtain ⟨old, words⟩ := h.words + exact ⟨(s.gpr .rax).toNat, saved.words words (by simp)⟩ + · rw [base, saved.work_eq]; exact h.matrixWork + · rw [saved.regs]; exact h.laneLength + · rw [saved.regs]; exact h.segmentLength + · exact (saved.read 184 (by decide) (by decide)).trans h.lanesWord + +end VG.Proof.Argon2.X86_64.SegmentSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupReset.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupReset.lean new file mode 100644 index 000000000..0f25ef537 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupReset.lean @@ -0,0 +1,48 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.SegmentSetupReady + +/-! Reset the counter while preserving the segment header and matrix allocation. -/ + +namespace VG.Proof.Argon2.X86_64.SegmentSetup + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.SegmentSetup + +structure Reset (s t : State) (p : Params) (pass lane slice : Nat) : Prop where + ready : Ready p pass lane slice t + words : AddressHeader.Words p pass lane slice 0 t + matrix : FillKernel.matrix t = FillKernel.matrix s + work : AddressCalls.work t = AddressCalls.work s + regs : ∀ r ∈ calleeSaved, t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame [⟨off (s.gpr .rbp) 8, 8⟩] s.mem t.mem + mxcsr : t.mxcsr = s.mxcsr + +theorem reset_ok (s : State) (p : Params) (pass lane slice : Nat) (h : Ready p pass lane slice s) : + WP isa reset s (Reset s · p pass lane slice) := by + unfold reset + refine WP.seq ((register_ok s .rax 0).mono ?_) + rintro a ⟨value, keeps⟩ + have next := h.of_state (by + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl <;> exact keeps.regs _ (by decide)) keeps.mem keeps.rd keeps.wr + refine (AddressCache.save_ready a next.addressLayout next.write).mono ?_ + intro t saved + obtain ⟨old, words⟩ := next.words + have matrixA : FillKernel.matrix a = FillKernel.matrix s := by + unfold FillKernel.matrix; rw [keeps.mem, keeps.regs .rbp (by decide)] + have workA : AddressCalls.work a = AddressCalls.work s := by + unfold AddressCalls.work; rw [keeps.mem, keeps.regs .rbp (by decide)] + refine ⟨next.saved saved, saved.words words value, + (saved.read 232 (by decide) (by decide)).trans matrixA, saved.work_eq.trans workA, + ?_, saved.rd.trans keeps.rd, saved.wr.trans keeps.wr, ?_, saved.mxcsr.trans keeps.mxcsr⟩ + · intro r hr + have ne : r ∉ [Reg.rax] := by + simp only [calleeSaved, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide + exact (congrFun saved.regs r).trans (keeps.regs r ne) + · have frame := saved.frame + rw [keeps.mem, keeps.regs .rbp (by decide)] at frame + exact frame + +end VG.Proof.Argon2.X86_64.SegmentSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupSteps.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupSteps.lean new file mode 100644 index 000000000..ae237bed1 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupSteps.lean @@ -0,0 +1,88 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.SegmentSetup +import VerifiedGarbage.Proof.Argon2.X86_64.FillContext + +/-! Select index two only in slice zero of pass zero. -/ + +namespace VG.Proof.Argon2.X86_64.SegmentSetup + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.SegmentSetup + +def start (pass slice : Nat) : Nat := if pass = 0 ∧ slice = 0 then 2 else 0 + +theorem start_active (pass slice : Nat) : pass ≠ 0 ∨ slice ≠ 0 ∨ 2 ≤ start pass slice := by + unfold start; split <;> omega + +theorem start_le (pass slice : Nat) (g : Nat) (minimum : 2 ≤ g) : start pass slice ≤ g := by + unfold start; split <;> omega + +theorem first_ok (s : State) (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 0) 8) : + WP isa (.block first) s fun t => + t.zf = decide (s.mem.readW (off (s.gpr .rbp) 0) 64 = 0#64 ∧ s.gpr .r14 = 0#64) ∧ + Divide.Keeps [.rcx] s t := by + apply WP.of_runBlock + simp only [first, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, State.load64, + ea_at, hr, execAlu, RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, RegUpd.zf_arithFlags, + reduceCtorEq, ite_true, ite_false, + show BitVec.signExtend 64 (0 : BitVec 32) = (0 : Addr) from rfl, + Option.map_some, Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨?_, ?_⟩ + · change ((s.mem.readW (off (s.gpr .rbp) 0) 64 ||| s.gpr .r14) - 0#64 == 0#64) = _ + rw [BitVec.sub_zero] + apply Bool.eq_iff_iff.mpr + simp only [beq_iff_eq, BitVec.or_eq_zero_iff, decide_eq_true_eq] + · constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, RegUpd.gpr_arithFlags, hr, ite_false] + all_goals rfl + +theorem register_ok (s : State) (register : Reg) (value : BitVec 32) : + WP isa (.block [.mov register (.imm value)]) s fun t => + t.gpr register = value.signExtend 64 ∧ Divide.Keeps [register] s t := by + apply WP.of_runBlock + simp only [runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, RegUpd.gpr_setReg, + ite_true, Option.map_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, ?_⟩ + constructor + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + simp only [RegUpd.gpr_setReg, hr, ite_false] + all_goals rfl + +theorem index_ok (s : State) (pass slice : Nat) + (hr : InRegions (s.rd ++ s.wr) (off (s.gpr .rbp) 0) 8) + (passWord : s.mem.readW (off (s.gpr .rbp) 0) 64 = BitVec.ofNat 64 pass) + (sliceWord : s.gpr .r14 = BitVec.ofNat 64 slice) + (passBound : pass < 2 ^ 64) (sliceBound : slice < 2 ^ 64) : WP isa index s fun t => + t.gpr .r15 = BitVec.ofNat 64 (start pass slice) ∧ Divide.Keeps [.rcx, .r15] s t := by + unfold index + refine WP.seq ((first_ok s hr).mono ?_) + rintro a ⟨flag, keeps⟩ + have firstFlag : a.zf = decide (pass = 0 ∧ slice = 0) := by + rw [flag, passWord, sliceWord] + have passZero : BitVec.ofNat 64 pass = 0#64 ↔ pass = 0 := ReferenceMap.word_zero pass passBound + have sliceZero : BitVec.ofNat 64 slice = 0#64 ↔ slice = 0 := ReferenceMap.word_zero slice sliceBound + simp only [passZero, sliceZero] + refine WP.ite (decide (pass = 0 ∧ slice = 0)) (by simp only [eval, firstFlag]) ?_ ?_ + · intro mode + refine (register_ok a .r15 2).mono ?_ + rintro t ⟨value, changed⟩ + refine ⟨?_, (keeps.mono (by decide)).trans (changed.mono (by decide))⟩ + unfold start; simp only [of_decide_eq_true mode]; exact value + · intro mode + refine (register_ok a .r15 0).mono ?_ + rintro t ⟨value, changed⟩ + refine ⟨?_, (keeps.mono (by decide)).trans (changed.mono (by decide))⟩ + unfold start; simp only [of_decide_eq_false mode, ite_false]; exact value + +theorem check_ok (s : State) : WP isa (.block check) s fun t => + t.cf = decide ((s.gpr .r15).toNat < (s.gpr .r13).toNat) ∧ Divide.Keeps [] s t := by + apply WP.of_runBlock + simp only [check, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, execAlu, + RegUpd.cf_arithFlags, Option.bind_some, Option.some.injEq, exists_eq_left'] + refine ⟨trivial, ?_⟩ + constructor + · intro r _; exact congrFun (RegUpd.gpr_arithFlags _ _ _ _) r + all_goals rfl + +end VG.Proof.Argon2.X86_64.SegmentSetup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupTrace.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupTrace.lean new file mode 100644 index 000000000..5dc69d065 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/SegmentSetupTrace.lean @@ -0,0 +1,100 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.SegmentSetupPrepare +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! Cache reset and initial-index selection branch only on public parameters. -/ + +namespace VG.Proof.Argon2.X86_64.SegmentSetup + +open VG VG.X86_64 VG.Spec.Argon2 VG.Impl.Argon2.X86_64.SegmentSetup + +structure RelatedReady (p : Params) (pass lane slice : Nat) (s t : State) : Prop where + left : Ready p pass lane slice s + right : Ready p pass lane slice t + bases : s.gpr .rbp = t.gpr .rbp + stacks : s.gpr .rsp = t.gpr .rsp + matrices : FillKernel.matrix s = FillKernel.matrix t + work : AddressCalls.work s = AddressCalls.work t + +theorem RelatedReady.of_keeps {p : Params} {pass lane slice : Nat} {s t a b : State} + (h : RelatedReady p pass lane slice s t) + (ka : Divide.Keeps [.rax, .rcx, .r15] s a) (kb : Divide.Keeps [.rax, .rcx, .r15] t b) : + RelatedReady p pass lane slice a b := by + have protectedRegs : ∀ r ∈ [Reg.rbp, .rsp, .rbx, .r12, .r13, .r14], r ∉ [Reg.rax, .rcx, .r15] := by decide + refine ⟨h.left.of_state (fun r hr => ka.regs r (protectedRegs r hr)) ka.mem ka.rd ka.wr, + h.right.of_state (fun r hr => kb.regs r (protectedRegs r hr)) kb.mem kb.rd kb.wr, ?_, ?_, ?_, ?_⟩ + · rw [ka.regs .rbp (by decide), kb.regs .rbp (by decide)]; exact h.bases + · rw [ka.regs .rsp (by decide), kb.regs .rsp (by decide)]; exact h.stacks + · unfold FillKernel.matrix + rw [ka.mem, kb.mem, ka.regs .rbp (by decide), kb.regs .rbp (by decide)] + exact h.matrices + · unfold AddressCalls.work + rw [ka.mem, kb.mem, ka.regs .rbp (by decide), kb.regs .rbp (by decide)] + exact h.work + +theorem reset_trace : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) reset (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr; simp only [List.mem_cons, List.not_mem_nil, or_false] at hr; subst r; exact h)) (by taint_decide) + +theorem reset_public_rel (p : Params) (pass lane slice : Nat) : + RelCT isa (RelatedReady p pass lane slice) reset (RelatedReady p pass lane slice) := by + have trace := reset_trace.mono (P' := RelatedReady p pass lane slice) + (fun _ _ h => h.bases) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => ⟨reset_ok s p pass lane slice h.left, reset_ok t p pass lane slice h.right⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ha, hb⟩ := h + refine ⟨ha.ready, hb.ready, ?_, ?_, ha.matrix.trans (hp.matrices.trans hb.matrix.symm), + ha.work.trans (hp.work.trans hb.work.symm)⟩ + · rw [ha.regs .rbp (by simp [calleeSaved]), hb.regs .rbp (by simp [calleeSaved])]; exact hp.bases + · rw [ha.regs .rsp (by simp [calleeSaved]), hb.regs .rsp (by simp [calleeSaved])]; exact hp.stacks + +theorem first_spec_ok (s : State) (p : Params) (pass lane slice : Nat) (h : Ready p pass lane slice s) : + WP isa (.block first) s fun t => t.zf = decide (pass = 0 ∧ slice = 0) ∧ Divide.Keeps [.rcx] s t := by + obtain ⟨old, words⟩ := h.words + refine (first_ok s (h.reads 0 (by simp))).mono ?_ + rintro t ⟨flag, keeps⟩ + refine ⟨?_, keeps⟩ + rw [flag, words.passWord, words.sliceWord] + have passZero : BitVec.ofNat 64 pass = 0#64 ↔ pass = 0 := + ReferenceMap.word_zero pass (Nat.lt_trans h.parameters.passBound (by decide)) + have sliceZero : BitVec.ofNat 64 slice = 0#64 ↔ slice = 0 := + ReferenceMap.word_zero slice (Nat.lt_trans h.parameters.sliceBound (by decide)) + simp only [passZero, sliceZero] + +theorem first_trace : RelCT isa (fun s t => s.gpr .rbp = t.gpr .rbp) (.block first) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbp]) + (fun _ _ h => Taint.agree_ofRegs (by + intro r hr; simp only [List.mem_cons, List.not_mem_nil, or_false] at hr; subst r; exact h)) (by taint_decide) + +theorem first_public_rel (p : Params) (pass lane slice : Nat) : + RelCT isa (RelatedReady p pass lane slice) (.block first) + (fun s t => RelatedReady p pass lane slice s t ∧ s.zf = t.zf) := by + have trace := first_trace.mono (P' := RelatedReady p pass lane slice) + (fun _ _ h => h.bases) (fun _ _ h => h) + have full := trace.wpDep (fun s t h => ⟨first_spec_ok s p pass lane slice h.left, first_spec_ok t p pass lane slice h.right⟩) + refine full.mono (fun _ _ h => h) ?_ + intro a b h + obtain ⟨_, s, t, hp, ⟨fa, ka⟩, ⟨fb, kb⟩⟩ := h + exact ⟨hp.of_keeps (ka.mono (by decide)) (kb.mono (by decide)), fa.trans fb.symm⟩ + +theorem index_trace (p : Params) (pass lane slice : Nat) : + RelCT isa (RelatedReady p pass lane slice) index (fun _ _ => True) := by + have two : RelCT isa (fun _ _ : State => True) (.block [.mov .r15 (.imm 2)]) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by intro r hr; simp at hr)) (by taint_decide) + have zero : RelCT isa (fun _ _ : State => True) (.block [.mov .r15 (.imm 0)]) (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by intro r hr; simp at hr)) (by taint_decide) + have branches : RelCT isa (fun s t => RelatedReady p pass lane slice s t ∧ s.zf = t.zf) + (.ite .e (.block [.mov .r15 (.imm 2)]) (.block [.mov .r15 (.imm 0)])) (fun _ _ => True) := + RelCT.ite (by intro s t h; simp only [eval, h.2]) + (two.mono (fun _ _ _ => trivial) (fun _ _ h => h)) + (zero.mono (fun _ _ _ => trivial) (fun _ _ h => h)) + exact (first_public_rel p pass lane slice).seq branches + +theorem prepare_trace (p : Params) (pass lane slice : Nat) : + RelCT isa (RelatedReady p pass lane slice) prepare (fun _ _ => True) := + (reset_public_rel p pass lane slice).seq (index_trace p pass lane slice) + +end VG.Proof.Argon2.X86_64.SegmentSetup From 8548bb059404e23676899a7bbdcbe9a4cf02d359 Mon Sep 17 00:00:00 2001 From: Paul Kehrer <161495+reaperhulk@users.noreply.github.com> Date: Thu, 1 Oct 2026 22:00:46 +0000 Subject: [PATCH 2/8] Prove complete derivation inside the ABI frame --- .../Impl/Argon2/X86_64/InitialBody.lean | 13 ++++ .../Proof/Argon2/X86_64/InitialBody.lean | 60 +++++++++++++++++ .../Proof/Argon2/X86_64/InitialBodyReady.lean | 65 +++++++++++++++++++ .../Proof/Argon2/X86_64/InitialCTState.lean | 57 ++++++++++++++++ .../Proof/Argon2/X86_64/InitialMetadata.lean | 34 ++++++++++ 5 files changed, 229 insertions(+) create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/InitialBody.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBody.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyReady.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialCTState.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialMetadata.lean diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/InitialBody.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/InitialBody.lean new file mode 100644 index 000000000..4aec223c7 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/InitialBody.lean @@ -0,0 +1,13 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.Initial +import VerifiedGarbage.Impl.Argon2.X86_64.InitFill + +/-! Complete derivation inside its enclosing argument and register-save frame. -/ + +namespace VG.Impl.Argon2.X86_64.InitialBody + +open VG VG.X86_64 + +def code (name : String) (hash : HPrime.Hash) : Prog isa := + .seq (Initial.code hash) (InitFill.code name hash) + +end VG.Impl.Argon2.X86_64.InitialBody diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBody.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBody.lean new file mode 100644 index 000000000..e015a6214 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBody.lean @@ -0,0 +1,60 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.InitialBody +import VerifiedGarbage.Proof.Argon2.X86_64.InitialBodyReady + +/-! H₀, initialization, every filling pass, and finalization agree with derive. -/ + +namespace VG.Proof.Argon2.X86_64.InitialBody + +open VG VG.X86_64 VG.Spec.Argon2 +open VG.Impl.Argon2.X86_64.Initial +open VG.Spec.Blake2 (bytesAt) + +structure Ready (p : Params) (s : State) : Prop where + hashSpace : Initial.Space s + inputs : ∀ input ∈ Initial.inputs, Initial.InputReady s input.1 input.2 + header : Initial.headerBytes s = Proof.Argon2.initialHeader p + filling : InitFill.Ready p s + +structure Done (s t : State) (p : Params) : Prop where + digest : bytesAt t.mem (FinalOutput.output s) p.tagLen = derive p + (Initial.inputBytes s passwordOffset passwordLenOffset) + (Initial.inputBytes s saltOffset saltLenOffset) + (Initial.inputBytes s secretOffset secretLenOffset) + (Initial.inputBytes s adOffset adLenOffset) + bp : t.gpr .rbp = s.gpr .rbp + sp : t.gpr .rsp = s.gpr .rsp + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (InitFill.writes s p) s.mem t.mem + +theorem hash_frame {s t : State} {p : Params} (h : InitFill.Ready p s) (done : Initial.Finished s t) : + Frame (InitFill.writes s p) s.mem t.mem := by + apply done.frame.sub + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · exact ⟨⟨FinalOutput.work s, 16384⟩, by simp [InitFill.writes], by + rw [h.scratch]; exact Region.sub_prefix (by decide)⟩ + · exact ⟨below (s.gpr .rsp) 24, by simp [InitFill.writes], below_sub (by decide) (by decide)⟩ + · exact ⟨⟨s.gpr .rbp, 72⟩, by simp [InitFill.writes], Region.sub_prefix (by decide)⟩ + +theorem code_ok (v : Proof.Blake2.X86_64.Backend) (name : String) (s : State) (p : Params) (h : Ready p s) : + WP isa (Impl.Argon2.X86_64.InitialBody.code name (HPrime.hash v)) s (Done s · p) := by + unfold Impl.Argon2.X86_64.InitialBody.code + refine WP.seq ((Initial.initialHash_ok v s h.hashSpace h.inputs p h.header).mono ?_) + rintro a ⟨digest, hashed⟩ + refine (InitFill.code_ok v name a p (hashed_ready h.filling h.hashSpace hashed)).mono ?_ + intro t filled + have base : FillKernel.matrix a = FillKernel.matrix s := hashed.frame_word h.hashSpace 232 (by decide) (by decide) + have work : FinalOutput.work a = FinalOutput.work s := hashed.frame_word h.hashSpace 248 (by decide) (by decide) + have output : FinalOutput.output a = FinalOutput.output s := hashed.frame_word h.hashSpace 256 (by decide) (by decide) + refine ⟨?_, filled.bp.trans hashed.rbp, filled.sp.trans hashed.rsp, + filled.rd.trans hashed.rd, filled.wr.trans hashed.wr, ?_⟩ + · have result := filled.digest + rw [output, hashed.rbp, digest, InitFill.result_derive] at result + exact result + · have frame := filled.frame + rw [InitFill.writes_eq s a p hashed.rbp hashed.rsp base work output] at frame + exact (hash_frame h.filling hashed).trans frame + +end VG.Proof.Argon2.X86_64.InitialBody diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyReady.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyReady.lean new file mode 100644 index 000000000..080fa023f --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyReady.lean @@ -0,0 +1,65 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.InitialMetadata +import VerifiedGarbage.Proof.Argon2.X86_64.InitFill + +/-! H₀ retains the allocation and parameter environment of complete derivation. -/ + +namespace VG.Proof.Argon2.X86_64.InitialBody + +open VG VG.X86_64 VG.Spec.Argon2 + +theorem hashed_environment {s t : State} {p : Params} (h : InitFill.Ready p s) (space : Initial.Space s) (done : Initial.Finished s t) : FillSetup.Environment p t := by + have base : FillKernel.matrix t = FillKernel.matrix s := done.frame_word space 232 (by decide) (by decide) + have work : AddressCalls.work t = AddressCalls.work s := done.frame_word space 248 (by decide) (by decide) + have e := h.environment + refine ⟨e.parameters, e.passesBound, e.layout.of_preserved done.rbp done.rsp base work done.rd done.wr, + ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · constructor + · rw [done.rd, done.wr, done.rbp]; exact e.addressLayout.frameRead + · rw [done.wr, work]; exact e.addressLayout.workWrite + · rw [done.rbp, work]; exact e.addressLayout.frameWork + · rw [done.rbp, done.rsp]; exact e.addressLayout.frameStack + · rw [done.rsp, work]; exact e.addressLayout.stackWork + · rw [done.rd, done.wr, done.rbp]; exact e.reads + · rw [done.wr, done.rbp]; exact e.counterWrite + · rw [done.wr, done.rbp]; exact e.passWrite + · rw [base, work]; exact e.matrixWork + · exact (done.frame_word space 240 (by decide) (by decide)).trans e.blocksWord + · exact (done.frame_word space 72 (by decide) (by decide)).trans e.passesWord + · exact (done.frame_word space 112 (by decide) (by decide)).trans e.variantWord + · exact (done.frame_word space 184 (by decide) (by decide)).trans e.lanesWord + +theorem hashed_output {s t : State} {p : Params} (h : InitFill.Ready p s) (space : Initial.Space s) (done : Initial.Finished s t) : FinalOutput.Ready p t := by + have base : ReductionState.matrix t = ReductionState.matrix s := done.frame_word space 232 (by decide) (by decide) + have output : FinalOutput.output t = FinalOutput.output s := done.frame_word space 256 (by decide) (by decide) + have work : FinalOutput.work t = FinalOutput.work s := done.frame_word space 248 (by decide) (by decide) + refine ⟨h.output.positive, h.output.bound, ?_, + (done.frame_word space 264 (by decide) (by decide)).trans h.output.tagWord, + ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · rw [done.rd, done.wr, done.rbp]; exact h.output.reads + · rw [base, done.rd, done.wr]; exact h.output.input + · rw [output, done.wr]; exact h.output.outputWrite + · rw [work, done.wr]; exact h.output.workWrite + · rw [base, work]; exact h.output.inputWork + · rw [output, work]; exact h.output.outputWork + · rw [done.rsp, base]; exact h.output.stackInput + · rw [done.rsp, output]; exact h.output.stackOutput + · rw [done.rsp, work]; exact h.output.stackWork + +theorem hashed_ready {s t : State} {p : Params} (h : InitFill.Ready p s) + (space : Initial.Space s) (done : Initial.Finished s t) : InitFill.Ready p t := by + have base : FillKernel.matrix t = FillKernel.matrix s := done.frame_word space 232 (by decide) (by decide) + have work : FinalOutput.work t = FinalOutput.work s := done.frame_word space 248 (by decide) (by decide) + refine ⟨?_, hashed_environment h space done, hashed_output h space done, h.positive, ?_⟩ + · constructor + · rw [base] + exact h.initializing.space.same done.wr done.rbp done.rbx done.rsp + · rw [done.rd, done.wr, done.rbp]; exact h.initializing.memoryRead + · rw [done.rd, done.wr, done.rbp]; exact h.initializing.lanesRead + · rw [done.rd, done.wr, done.rbp]; exact h.initializing.blocksRead + · exact (done.frame_word space 232 (by decide) (by decide)).trans h.initializing.memoryWord |>.trans base.symm + · exact (done.frame_word space 184 (by decide) (by decide)).trans h.initializing.lanesWord + · exact (done.frame_word space 240 (by decide) (by decide)).trans h.initializing.blocksWord + · exact (done.regs .r13 (by decide) (by decide) (by decide)).trans h.initializing.laneLength + · rw [done.rbx, work]; exact h.scratch + +end VG.Proof.Argon2.X86_64.InitialBody diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialCTState.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialCTState.lean new file mode 100644 index 000000000..443c109d6 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialCTState.lean @@ -0,0 +1,57 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.Initial +import VerifiedGarbage.Proof.Argon2.X86_64.InitialBlocksCT +import VerifiedGarbage.Proof.Argon2.X86_64.HPrime.FixedCT + +/-! Public input metadata survives every hash call without relating input bytes. -/ + +namespace VG.Proof.Argon2.X86_64.Initial + +open VG VG.X86_64 + +structure Ready (s : State) : Prop where + space : Space s + inputs : ∀ input ∈ inputs, InputReady s input.1 input.2 + +theorem Ready.keeps {s t : State} (h : Ready s) (k : Keeps s t) : Ready t := + ⟨h.space.keeps k, fun p hp => (h.inputs p hp).keeps k⟩ + +structure Related (s t : State) : Prop where + left : Ready s + right : Ready t + bp : s.gpr .rbp = t.gpr .rbp + bx : s.gpr .rbx = t.gpr .rbx + sp : s.gpr .rsp = t.gpr .rsp + words : ∀ d ∈ slots, wordAt s d = wordAt t d + +theorem Related.keeps {s₁ s₂ t₁ t₂ : State} (h : Related s₁ s₂) + (k₁ : Keeps s₁ t₁) (k₂ : Keeps s₂ t₂) : Related t₁ t₂ := by + refine ⟨h.left.keeps k₁, h.right.keeps k₂, ?_, ?_, ?_, ?_⟩ + · rw [k₁.rbp, k₂.rbp, h.bp] + · rw [k₁.rbx, k₂.rbx, h.bx] + · rw [k₁.rsp, k₂.rsp, h.sp] + · intro d hd + have bound : ∀ d ∈ slots, d + 8 ≤ 272 := by decide + rw [h.left.space.word_keeps k₁ d (bound d hd), h.right.space.word_keeps k₂ d (bound d hd)] + exact h.words d hd + +def RelatedRegs (rs : List Reg) (s t : State) : Prop := + Related s t ∧ HPrime.AgreeRegs rs s t + +theorem hash_keeps_rel {P : State → State → Prop} {c : Prog isa} (rs : List Reg) + (saved : ∀ r ∈ rs, r ∈ calleeSaved) + (ct : RelCT isa P c (fun _ _ => True)) + (pre : ∀ s t, P s t → RelatedRegs rs s t) + (wp : ∀ s t, P s t → WP isa c s (HPrime.Keeps s) ∧ WP isa c t (HPrime.Keeps t)) : + RelCT isa P c (RelatedRegs rs) := by + apply (ct.wpDep wp).mono (fun _ _ h => h) + rintro a b ⟨_, s, t, hp, ha, hb⟩ + have h := pre s t hp + refine ⟨h.1.keeps (Keeps.of_hash ha) (Keeps.of_hash hb), ?_⟩ + intro r hr + rw [ha.regs r (saved r hr), hb.regs r (saved r hr)] + exact h.2 r hr + +theorem finalize_ready {s : State} (h : Ready s) : HPrime.FinalizeReady s := + ⟨h.space.work, h.space.stackWork⟩ + +end VG.Proof.Argon2.X86_64.Initial diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialMetadata.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialMetadata.lean new file mode 100644 index 000000000..0251d6b4e --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialMetadata.lean @@ -0,0 +1,34 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.Initial + +/-! H₀ writes its digest into the frame while retaining all enclosing arguments. -/ + +namespace VG.Proof.Argon2.X86_64.Initial + +open VG VG.X86_64 + +theorem Finished.rbp {s t : State} (h : Finished s t) : t.gpr .rbp = s.gpr .rbp := + h.regs _ (by decide) (by decide) (by decide) + +theorem Finished.rbx {s t : State} (h : Finished s t) : t.gpr .rbx = s.gpr .rbx := + h.regs _ (by decide) (by decide) (by decide) + +theorem Finished.rsp {s t : State} (h : Finished s t) : t.gpr .rsp = s.gpr .rsp := + h.regs _ (by decide) (by decide) (by decide) + +theorem Finished.frame_word {s t : State} (h : Finished s t) (space : Space s) + (d : Nat) (bound : d + 8 ≤ 272) (afterDigest : 64 ≤ d) : wordAt t d = wordAt s d := by + unfold wordAt + rw [h.rbp] + apply h.frame.readW (r := ⟨s.gpr .rbp + BitVec.ofNat 64 d, 8⟩) + (Region.contains_self _ _) ?_ (by decide) + intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · exact space.frameWork.sub_left (Offset.sub_base _ bound) |>.sub_right + (Region.sub_prefix (by decide)) + · exact space.frameStack.sub_left (Offset.sub_base _ bound) + · simpa only [BitVec.add_zero] using + Offset.disjoint (s.gpr .rbp) (d := d) (n := 8) (e := 0) (k := 64) + (Or.inr afterDigest) (by omega) (by decide) + +end VG.Proof.Argon2.X86_64.Initial From 77fab2dc61d97f3965a92285f315e6164a4a6ebd Mon Sep 17 00:00:00 2001 From: Paul Kehrer <161495+reaperhulk@users.noreply.github.com> Date: Thu, 1 Oct 2026 22:08:28 +0000 Subject: [PATCH 3/8] Prove whole Argon2 body leakage for every hash backend --- .../Proof/Argon2/X86_64/InitialAbsorbCT.lean | 119 ++++++++++++++++++ .../Proof/Argon2/X86_64/InitialBodyCT.lean | 51 ++++++++ .../Proof/Argon2/X86_64/InitialCT.lean | 20 +++ .../Proof/Argon2/X86_64/InitialFinishCT.lean | 58 +++++++++ .../Proof/Argon2/X86_64/InitialStartCT.lean | 74 +++++++++++ .../Argon2/X86_64/InitialUpdateReady.lean | 55 ++++++++ 6 files changed, 377 insertions(+) create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialAbsorbCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialFinishCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialStartCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialUpdateReady.lean diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialAbsorbCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialAbsorbCT.lean new file mode 100644 index 000000000..768132ccb --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialAbsorbCT.lean @@ -0,0 +1,119 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.InitialUpdateReady +import VerifiedGarbage.Proof.Framework.RelCTAssoc + +/-! H₀ updates depend on public lengths and pointers, never on input contents. -/ + +namespace VG.Proof.Argon2.X86_64.Initial + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.Initial + +def PrefixRelated (lo : Nat) (a b : State) : Prop := + True ∧ ∃ s t, RelatedRegs [.r12] s t ∧ LengthArgs s lo a ∧ LengthArgs t lo b + +theorem prefix_rel (v : Proof.Blake2.X86_64.Backend) (lo : Nat) (slot : lo ∈ slots) + (bound : lo + 8 ≤ 272) + (check : ∃ hint, (taint.check (Taint.ofRegs [.rbp, .rbx]) (.block (lengthArgs lo)) hint).isSome = true) : + RelCT isa (RelatedRegs [.r12]) + (.seq (.block (lengthArgs lo)) (Impl.Argon2.X86_64.HPrime.update (HPrime.hash v))) (LengthRelated lo) := by + have args := ((lengthArgs_rel lo check).mono (P' := RelatedRegs [.r12]) + (fun _ _ h => ⟨h.1.bp, h.1.bx⟩) (fun _ _ h => h)).wpDep + (fun s t h => ⟨lengthArgs_ok s lo (h.1.left.space.readable lo slot) + (by simpa using h.1.left.space.write 792 4 (by decide)), + lengthArgs_ok t lo (h.1.right.space.readable lo slot) + (by simpa using h.1.right.space.write 792 4 (by decide))⟩) + have call := HPrime.update_rel v (P := PrefixRelated lo) (fun a b ⟨_, s, t, hp, ha, hb⟩ => + ⟨prefix_update_ready hp.1.left.space ha, prefix_update_ready hp.1.right.space hb, + by rw [ha.keeps.rbx, hb.keeps.rbx, hp.1.bx], by rw [ha.count, hb.count]; exact hp.2 _ (by simp), + by rw [ha.pointer, hb.pointer, hp.1.bx], by rw [ha.size, hb.size], + by rw [ha.keeps.rsp, hb.keeps.rsp, hp.1.sp]⟩) + have called := call.wpDep (fun a b ⟨_, s, t, hp, ha, hb⟩ => + ⟨HPrime.update_keeps v a (prefix_update_ready hp.1.left.space ha), + HPrime.update_keeps v b (prefix_update_ready hp.1.right.space hb)⟩) + have finished := called.mono (fun _ _ h => h) (fun a b h => by + obtain ⟨_, x, y, ⟨_, s, t, hp, ha, hb⟩, ka, kb⟩ := h + have rel := hp.1.keeps ha.keeps hb.keeps + have prepared : LengthRelated lo x y := by + refine ⟨⟨rel, ?_⟩, ?_, ?_⟩ + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · rw [ha.other _ (by decide) (by decide) (by decide) (by decide), + hb.other _ (by decide) (by decide) (by decide) (by decide)] + exact hp.2 _ (by simp) + · rw [ha.length, hb.length]; exact hp.1.words lo slot + · rw [hp.1.left.space.word_keeps ha.keeps lo bound]; exact ha.length + · rw [hp.1.right.space.word_keeps hb.keeps lo bound]; exact hb.length + exact prepared.hash_keeps bound ka kb) + exact args.seq finished + +def InputRelated (lo po : Nat) (a b : State) : Prop := + True ∧ ∃ s t, LengthRelated lo s t ∧ InputArgs s a po ∧ InputArgs t b po + +theorem input_rel (v : Proof.Blake2.X86_64.Backend) (po lo : Nat) (input : (po, lo) ∈ inputs) + (check : ∃ hint, (taint.check (Taint.ofRegs [.rbp]) (.block (inputArgs po)) hint).isSome = true) : + RelCT isa (LengthRelated lo) + (.seq (.block (inputArgs po)) (Impl.Argon2.X86_64.HPrime.update (HPrime.hash v))) (LengthRelated lo) := by + have args := ((inputArgs_rel po check).mono (P' := LengthRelated lo) + (fun _ _ h => h.related.1.bp) (fun _ _ h => h)).wpDep + (fun s t h => ⟨inputArgs_ok s po (h.related.1.left.space.readable po (h.related.1.left.inputs _ input).pointerSlot), + inputArgs_ok t po (h.related.1.right.space.readable po (h.related.1.right.inputs _ input).pointerSlot)⟩) + have call := HPrime.update_rel v (P := InputRelated lo po) (fun a b ⟨_, s, t, hp, ha, hb⟩ => + ⟨input_update_ready (hp.related.1.left.inputs _ input) hp.leftLength ha, + input_update_ready (hp.related.1.right.inputs _ input) hp.rightLength hb, + by rw [ha.keeps.rbx, hb.keeps.rbx, hp.related.1.bx], + by rw [ha.count, hb.count, hp.related.2 .r12 (by simp)], + by rw [ha.pointer, hb.pointer]; exact hp.related.1.words po (hp.related.1.left.inputs _ input).pointerSlot, + by rw [ha.length, hb.length]; exact hp.related.2 .r14 (by simp), + by rw [ha.keeps.rsp, hb.keeps.rsp, hp.related.1.sp]⟩) + have called := call.wpDep (fun a b ⟨_, s, t, hp, ha, hb⟩ => + ⟨HPrime.update_keeps v a (input_update_ready (hp.related.1.left.inputs _ input) hp.leftLength ha), + HPrime.update_keeps v b (input_update_ready (hp.related.1.right.inputs _ input) hp.rightLength hb)⟩) + have finished := called.mono (fun _ _ h => h) (fun a b h => by + obtain ⟨_, x, y, ⟨_, s, t, hp, ha, hb⟩, ka, kb⟩ := h + have left := hp.related.1.left.inputs _ input + have right := hp.related.1.right.inputs _ input + have prepared : LengthRelated lo x y := by + refine ⟨⟨hp.related.1.keeps ha.keeps hb.keeps, ?_⟩, ?_, ?_⟩ + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · rw [ha.total, hb.total, hp.related.2 .r12 (by simp)] + · rw [ha.other _ (by decide) (by decide) (by decide) (by decide), + hb.other _ (by decide) (by decide) (by decide) (by decide)] + exact hp.related.2 .r14 (by simp) + · rw [left.space.word_keeps ha.keeps lo left.lengthBound, + ha.other _ (by decide) (by decide) (by decide) (by decide)] + exact hp.leftLength + · rw [right.space.word_keeps hb.keeps lo right.lengthBound, + hb.other _ (by decide) (by decide) (by decide) (by decide)] + exact hp.rightLength + exact prepared.hash_keeps left.lengthBound ka kb) + exact args.seq finished + +theorem addCount_rel (lo : Nat) : + RelCT isa (LengthRelated lo) (.block [.alu .add .r12 (.reg .r14)]) (RelatedRegs [.r12]) := by + have ct := (RelCT.taint (A := taint) (P := LengthRelated lo) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by simp)) + (c := .block [.alu .add .r12 (.reg .r14)]) (by taint_decide)).wpDep + (fun s t _ => ⟨addCount_ok s, addCount_ok t⟩) + apply ct.mono (fun _ _ h => h) + rintro a b ⟨_, s, t, hp, ⟨ca, _, ka⟩, ⟨cb, _, kb⟩⟩ + refine ⟨hp.related.1.keeps ka kb, ?_⟩ + intro r hr + simp only [List.mem_singleton] at hr; subst r + rw [ca, cb, hp.related.2 .r12 (by simp), hp.related.2 .r14 (by simp)] + +theorem absorb_rel (v : Proof.Blake2.X86_64.Backend) (po lo : Nat) (input : (po, lo) ∈ inputs) + (lengthCheck : ∃ hint, (taint.check (Taint.ofRegs [.rbp, .rbx]) (.block (lengthArgs lo)) hint).isSome = true) + (pointerCheck : ∃ hint, (taint.check (Taint.ofRegs [.rbp]) (.block (inputArgs po)) hint).isSome = true) : + RelCT isa (RelatedRegs [.r12]) (absorb (HPrime.hash v) po lo) (RelatedRegs [.r12]) := by + have slot : lo ∈ slots := by + have all : ∀ p ∈ inputs, p.2 ∈ slots := by decide + exact all _ input + have bound : lo + 8 ≤ 272 := by + have all : ∀ d ∈ slots, d + 8 ≤ 272 := by decide + exact all lo slot + exact ((prefix_rel v lo slot bound lengthCheck).seq + (((input_rel v po lo input pointerCheck).seq (addCount_rel lo)).assoc)).assoc + +end VG.Proof.Argon2.X86_64.Initial diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyCT.lean new file mode 100644 index 000000000..fe4419256 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyCT.lean @@ -0,0 +1,51 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.InitialBody +import VerifiedGarbage.Proof.Argon2.X86_64.InitialCT +import VerifiedGarbage.Proof.Argon2.X86_64.InitFillCT + +/-! Complete derivation reveals only its reviewed filling reference sequence. -/ + +namespace VG.Proof.Argon2.X86_64.InitialBody + +open VG VG.X86_64 VG.Spec.Argon2 +open VG.Impl.Argon2.X86_64.Initial +open VG.Spec.Blake2 (bytesAt) + +def initial (p : Params) (s : State) : FillState := initMemory p (initialHash p + (Initial.inputBytes s passwordOffset passwordLenOffset) + (Initial.inputBytes s saltOffset saltLenOffset) + (Initial.inputBytes s secretOffset secretLenOffset) + (Initial.inputBytes s adOffset adLenOffset)) + +structure Related (p : Params) (s t : State) : Prop where + left : Ready p s + right : Ready p t + hashing : Initial.Related s t + matrices : FillKernel.matrix s = FillKernel.matrix t + outputs : FinalOutput.output s = FinalOutput.output t + works : FinalOutput.work s = FinalOutput.work t + indices : (Proof.Argon2.iterations p 0 p.passes (initial p s)).indices = + (Proof.Argon2.iterations p 0 p.passes (initial p t)).indices + +theorem code_rel (v : Proof.Blake2.X86_64.Backend) (name : String) (p : Params) : + RelCT isa (Related p) (Impl.Argon2.X86_64.InitialBody.code name (HPrime.hash v)) (fun _ _ => True) := by + have hashed := ((Initial.code_rel v).mono (P' := Related p) (fun _ _ h => h.hashing) + (fun _ _ h => h)).wpDep (fun s t h => + ⟨Initial.initialHash_ok v s h.left.hashSpace h.left.inputs p h.left.header, + Initial.initialHash_ok v t h.right.hashSpace h.right.inputs p h.right.header⟩) + refine hashed.seq ((InitFill.code_rel v name p).mono ?_ (fun _ _ h => h)) + rintro a b ⟨_, s, t, hp, ⟨da, ha⟩, ⟨db, hb⟩⟩ + have baseA : FillKernel.matrix a = FillKernel.matrix s := ha.frame_word hp.left.hashSpace 232 (by decide) (by decide) + have baseB : FillKernel.matrix b = FillKernel.matrix t := hb.frame_word hp.right.hashSpace 232 (by decide) (by decide) + have outputA : FinalOutput.output a = FinalOutput.output s := ha.frame_word hp.left.hashSpace 256 (by decide) (by decide) + have outputB : FinalOutput.output b = FinalOutput.output t := hb.frame_word hp.right.hashSpace 256 (by decide) (by decide) + have workA : FinalOutput.work a = FinalOutput.work s := ha.frame_word hp.left.hashSpace 248 (by decide) (by decide) + have workB : FinalOutput.work b = FinalOutput.work t := hb.frame_word hp.right.hashSpace 248 (by decide) (by decide) + refine ⟨hashed_ready hp.left.filling hp.left.hashSpace ha, hashed_ready hp.right.filling hp.right.hashSpace hb, + ha.rbp.trans (hp.hashing.bp.trans hb.rbp.symm), ha.rsp.trans (hp.hashing.sp.trans hb.rsp.symm), + baseA.trans (hp.matrices.trans baseB.symm), outputA.trans (hp.outputs.trans outputB.symm), + workA.trans (hp.works.trans workB.symm), ?_⟩ + unfold InitFill.initial + rw [ha.rbp, hb.rbp, da, db] + exact hp.indices + +end VG.Proof.Argon2.X86_64.InitialBody diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialCT.lean new file mode 100644 index 000000000..c92bd5f1f --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialCT.lean @@ -0,0 +1,20 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.InitialStartCT +import VerifiedGarbage.Proof.Argon2.X86_64.InitialAbsorbCT +import VerifiedGarbage.Proof.Argon2.X86_64.InitialFinishCT + +/-! Complete H₀ is constant time for every verified BLAKE2b backend. -/ + +namespace VG.Proof.Argon2.X86_64.Initial + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.Initial + +theorem code_rel (v : Proof.Blake2.X86_64.Backend) : + RelCT isa Related (code (HPrime.hash v)) (fun _ _ => True) := + (start_rel v).seq + ((absorb_rel v passwordOffset passwordLenOffset (by decide) ⟨_, by taint_decide⟩ ⟨_, by taint_decide⟩).seq + ((absorb_rel v saltOffset saltLenOffset (by decide) ⟨_, by taint_decide⟩ ⟨_, by taint_decide⟩).seq + ((absorb_rel v secretOffset secretLenOffset (by decide) ⟨_, by taint_decide⟩ ⟨_, by taint_decide⟩).seq + ((absorb_rel v adOffset adLenOffset (by decide) ⟨_, by taint_decide⟩ ⟨_, by taint_decide⟩).seq + (finish_rel v))))) + +end VG.Proof.Argon2.X86_64.Initial diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialFinishCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialFinishCT.lean new file mode 100644 index 000000000..c0cc0dba6 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialFinishCT.lean @@ -0,0 +1,58 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.InitialCTState + +/-! H₀ finalization and its fixed-size digest copy reveal no input contents. -/ + +namespace VG.Proof.Argon2.X86_64.Initial + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.Initial + +theorem finishCount_rel : RelCT isa (RelatedRegs [.r12]) (.block [.mov .rsi (.reg .r12)]) + (fun s t => RelatedRegs [.r12] s t ∧ s.gpr .rsi = t.gpr .rsi) := by + have ct := (RelCT.taint (A := taint) (P := RelatedRegs [.r12]) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by simp)) + (c := .block [.mov .rsi (.reg .r12)]) (by taint_decide)).wpDep + (fun s t _ => ⟨finishCount_ok s, finishCount_ok t⟩) + apply ct.mono (fun _ _ h => h) + rintro a b ⟨_, s, t, hp, ⟨ca, _, ka⟩, ⟨cb, _, kb⟩⟩ + refine ⟨⟨hp.1.keeps (Keeps.of_hash ka) (Keeps.of_hash kb), ?_⟩, ?_⟩ + · intro r hr + simp only [List.mem_singleton] at hr; subst r + rw [ka.regs .r12 (by decide), kb.regs .r12 (by decide)] + exact hp.2 _ (by simp) + · rw [ca, cb]; exact hp.2 _ (by simp) + +theorem finalize_hash_rel (v : Proof.Blake2.X86_64.Backend) : + RelCT isa (fun s t => RelatedRegs [.r12] s t ∧ s.gpr .rsi = t.gpr .rsi) + (Impl.Argon2.X86_64.HPrime.finalize (HPrime.hash v)) Related := by + have ct := HPrime.finalize_rel v (P := fun s t => RelatedRegs [.r12] s t ∧ s.gpr .rsi = t.gpr .rsi) + (fun s t h => ⟨finalize_ready h.1.1.left, finalize_ready h.1.1.right, h.1.1.bx, h.2, h.1.1.sp⟩) + have result := hash_keeps_rel [] (by simp) ct (fun _ _ h => ⟨h.1.1, by simp [HPrime.AgreeRegs]⟩) + (fun s t h => ⟨HPrime.finalize_keeps v s (finalize_ready h.1.1.left), + HPrime.finalize_keeps v t (finalize_ready h.1.1.right)⟩) + exact result.mono (fun _ _ h => h) (fun _ _ h => h.1) + +theorem finishOutput_rel : RelCT isa Related + (.block [.mov .r14 (.reg .rbp), .mov32 .rax (.imm 64)]) + (HPrime.AgreeRegs [.rbx, .r14, .rax]) := by + have ct := (RelCT.taint (A := taint) (P := Related) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by simp)) + (c := .block [.mov .r14 (.reg .rbp), .mov32 .rax (.imm 64)]) (by taint_decide)).wpDep + (fun s t _ => ⟨finishOutput_ok s, finishOutput_ok t⟩) + apply ct.mono (fun _ _ h => h) + rintro a b ⟨_, s, t, hp, ⟨da, la, _, ka⟩, ⟨db, lb, _, kb⟩⟩ r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl + · rw [ka.rbx, kb.rbx, hp.bx] + · rw [da, db, hp.bp] + · rw [la, lb] + +theorem copy_digest_rel : RelCT isa (HPrime.AgreeRegs [.rbx, .r14, .rax]) + Impl.Argon2.X86_64.HPrime.copy (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rbx, .r14, .rax]) + (fun _ _ h => Taint.agree_ofRegs h) (by taint_decide) + +theorem finish_rel (v : Proof.Blake2.X86_64.Backend) : + RelCT isa (RelatedRegs [.r12]) (finish (HPrime.hash v)) (fun _ _ => True) := + finishCount_rel.seq ((finalize_hash_rel v).seq (finishOutput_rel.seq copy_digest_rel)) + +end VG.Proof.Argon2.X86_64.Initial diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialStartCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialStartCT.lean new file mode 100644 index 000000000..42a7be946 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialStartCT.lean @@ -0,0 +1,74 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.InitialCTState + +/-! H₀ initialization and its fixed parameter header have input-independent traces. -/ + +namespace VG.Proof.Argon2.X86_64.Initial + +open VG VG.X86_64 VG.Impl.Argon2.X86_64.Initial + +theorem digestLength_rel : RelCT isa Related (.block [.mov32 .rsi (.imm 64)]) + (fun s t => Related s t ∧ s.gpr .rsi = 64 ∧ t.gpr .rsi = 64) := by + have ct := (RelCT.taint (A := taint) (P := Related) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by simp)) + (c := .block [.mov32 .rsi (.imm 64)]) (by taint_decide)).wpDep + (fun s t _ => ⟨digestLength_ok s, digestLength_ok t⟩) + apply ct.mono (fun _ _ h => h) + rintro a b ⟨_, s, t, hp, ⟨la, _, ka⟩, ⟨lb, _, kb⟩⟩ + exact ⟨hp.keeps (Keeps.of_hash ka) (Keeps.of_hash kb), la, lb⟩ + +theorem init_hash_rel (v : Proof.Blake2.X86_64.Backend) : + RelCT isa (fun s t => Related s t ∧ s.gpr .rsi = 64 ∧ t.gpr .rsi = 64) + (Impl.Argon2.X86_64.HPrime.init (HPrime.hash v)) Related := by + have ready (s : State) (h : Ready s) (len : s.gpr .rsi = 64) : HPrime.InitReady s := + ⟨by rw [len]; decide, h.space.work, + (h.space.stackWork.sub_left (below_sub (by decide) (by decide))).sub_right (Region.sub_prefix (by decide))⟩ + have ct := HPrime.init_rel v (P := fun s t => Related s t ∧ s.gpr .rsi = 64 ∧ t.gpr .rsi = 64) + (fun s t ⟨h, ls, lt⟩ => ⟨ready s h.left ls, ready t h.right lt, + h.bx, ls.trans lt.symm, h.sp⟩) + have result := hash_keeps_rel [] (by simp) ct (fun _ _ h => ⟨h.1, by simp [HPrime.AgreeRegs]⟩) + (fun s t ⟨h, ls, lt⟩ => ⟨HPrime.init_keeps v s (ready s h.left ls), HPrime.init_keeps v t (ready t h.right lt)⟩) + exact result.mono (fun _ _ h => h) (fun _ _ h => h.1) + +theorem header_state_rel : RelCT isa Related headerCode Related := by + have ct := (header_rel.mono (P' := Related) (fun _ _ h => ⟨h.bp, h.bx⟩) + (fun _ _ h => h)).wpDep (fun s t h => + ⟨headerWords_ok s 6 (by decide) h.left.space, headerWords_ok t 6 (by decide) h.right.space⟩) + exact ct.mono (fun _ _ h => h) (fun _ _ ⟨_, _, _, hp, ⟨_, ka⟩, ⟨_, kb⟩⟩ => hp.keeps ka kb) + +theorem fixed_header_rel (v : Proof.Blake2.X86_64.Backend) : + RelCT isa Related (Impl.Argon2.X86_64.HPrime.absorbFixed (HPrime.hash v) 768 24) Related := by + have args := (RelCT.taint (A := taint) (P := Related) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by simp)) + (c := .block (Impl.Argon2.X86_64.HPrime.fixedArgs 768 24)) (by taint_decide)).wpDep + (fun s t _ => ⟨HPrime.fixedArgs_ok s 768 24 (by decide) (by decide), + HPrime.fixedArgs_ok t 768 24 (by decide) (by decide)⟩) + have call := HPrime.update_rel v (P := fun a b => True ∧ ∃ s t, Related s t ∧ + HPrime.FixedArgs s a 768 24 ∧ HPrime.FixedArgs t b 768 24) + (fun a b ⟨_, s, t, hp, ha, hb⟩ => ⟨HPrime.fixed_ready (finalize_ready hp.left) ha (by decide) (by decide), + HPrime.fixed_ready (finalize_ready hp.right) hb (by decide) (by decide), + by rw [ha.keeps.rbx, hb.keeps.rbx, hp.bx], by rw [ha.count, hb.count], + by rw [ha.data, hb.data, hp.bx], by rw [ha.size, hb.size], by rw [ha.keeps.rsp, hb.keeps.rsp, hp.sp]⟩) + have ct := args.seq call + have result := hash_keeps_rel [] (by simp) ct (fun _ _ h => ⟨h, by simp [HPrime.AgreeRegs]⟩) + (fun s t h => ⟨HPrime.absorbFixed_keeps v s 768 24 (finalize_ready h.left) (by decide) (by decide), + HPrime.absorbFixed_keeps v t 768 24 (finalize_ready h.right) (by decide) (by decide)⟩) + exact result.mono (fun _ _ h => h) (fun _ _ h => h.1) + +theorem initialCount_rel : RelCT isa Related (.block [.mov32 .r12 (.imm 24)]) (RelatedRegs [.r12]) := by + have ct := (RelCT.taint (A := taint) (P := Related) (Taint.ofRegs []) + (fun _ _ _ => Taint.agree_ofRegs (by simp)) + (c := .block [.mov32 .r12 (.imm 24)]) (by taint_decide)).wpDep + (fun s t _ => ⟨initialCount_ok s, initialCount_ok t⟩) + apply ct.mono (fun _ _ h => h) + rintro a b ⟨_, s, t, hp, ⟨la, _, ka⟩, ⟨lb, _, kb⟩⟩ + refine ⟨hp.keeps ka kb, ?_⟩ + intro r hr + simp only [List.mem_singleton] at hr; subst r + exact la.trans lb.symm + +theorem start_rel (v : Proof.Blake2.X86_64.Backend) : + RelCT isa Related (start (HPrime.hash v)) (RelatedRegs [.r12]) := + digestLength_rel.seq ((init_hash_rel v).seq (header_state_rel.seq + ((fixed_header_rel v).seq initialCount_rel))) + +end VG.Proof.Argon2.X86_64.Initial diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialUpdateReady.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialUpdateReady.lean new file mode 100644 index 000000000..8caa3a2b7 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialUpdateReady.lean @@ -0,0 +1,55 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.InitialCTState + +/-! Permissions for the two updates of each length-prefixed H₀ input. -/ + +namespace VG.Proof.Argon2.X86_64.Initial + +open VG VG.X86_64 + +theorem prefix_update_ready {s t : State} {lo : Nat} (h : Space s) (args : LengthArgs s lo t) : + HPrime.UpdateReady t := by + have k := args.keeps + have ht := h.keeps k + have len : (t.gpr .rcx).toNat = 4 := by rw [args.size]; rfl + refine ⟨ht.work, ?_, ?_, ?_, ht.stackWork, ?_⟩ + · rw [args.pointer, len, ← k.rbx] + apply Covers.of_sub + intro r hr + simp only [List.mem_singleton] at hr; subst r + exact ⟨⟨t.gpr .rbx, 16384⟩, List.mem_append_right _ ht.work, 792, rfl, by change 792 + 4 ≤ 16384; decide⟩ + · rw [args.pointer, len, k.rbx] + exact (Offset.base_disjoint _ (by decide) (by decide)).symm + · rw [args.pointer, len, k.rbx] + exact Offset.disjoint _ (d := 792) (n := 4) (e := 192) (k := 576) (by decide) (by decide) (by decide) + · rw [args.pointer, len, ← k.rbx] + exact ht.stackWork.sub_right (Offset.sub_base _ (by decide)) + +theorem input_update_ready {s t : State} {po lo : Nat} (h : InputReady s po lo) + (length : s.gpr .r14 = wordAt s lo) (args : InputArgs s t po) : HPrime.UpdateReady t := by + have k := args.keeps + have ptr : t.gpr .rdx = wordAt s po := args.pointer + have len : t.gpr .rcx = wordAt s lo := args.length.trans length + refine ⟨(h.space.keeps k).work, ?_, ?_, ?_, (h.space.keeps k).stackWork, ?_⟩ + · rw [ptr, len, k.rd, k.wr]; exact h.cover + · rw [ptr, len, k.rbx]; exact h.work.sub_right (Region.sub_prefix (by decide)) + · rw [ptr, len, k.rbx]; exact h.work.sub_right (Offset.sub_base _ (by decide)) + · rw [ptr, len, k.rsp]; exact h.stack.symm + +structure LengthRelated (lo : Nat) (s t : State) : Prop where + related : RelatedRegs [.r12, .r14] s t + leftLength : s.gpr .r14 = wordAt s lo + rightLength : t.gpr .r14 = wordAt t lo + +theorem LengthRelated.hash_keeps {lo : Nat} {s t a b : State} (h : LengthRelated lo s t) + (bound : lo + 8 ≤ 272) (ka : HPrime.Keeps s a) (kb : HPrime.Keeps t b) : LengthRelated lo a b := by + refine ⟨⟨h.related.1.keeps (Keeps.of_hash ka) (Keeps.of_hash kb), ?_⟩, ?_, ?_⟩ + · intro r hr + have saved : ∀ r ∈ ([.r12, .r14] : List Reg), r ∈ calleeSaved := by decide + rw [ka.regs r (saved r hr), kb.regs r (saved r hr)] + exact h.related.2 r hr + · rw [ka.regs .r14 (by decide), h.related.1.left.space.word_keeps (Keeps.of_hash ka) lo bound] + exact h.leftLength + · rw [kb.regs .r14 (by decide), h.related.1.right.space.word_keeps (Keeps.of_hash kb) lo bound] + exact h.rightLength + +end VG.Proof.Argon2.X86_64.Initial From 9aa21911c173b5d1aaea47de338c2cd47b48ccc0 Mon Sep 17 00:00:00 2001 From: Paul Kehrer <161495+reaperhulk@users.noreply.github.com> Date: Thu, 1 Oct 2026 22:23:41 +0000 Subject: [PATCH 4/8] Normalize ABI arguments and recover the reviewed reference log --- .../Impl/Argon2/X86_64/Derive.lean | 37 ++++++++ .../Proof/Argon2/References.lean | 86 +++++++++++++++++++ .../Proof/Argon2/X86_64/DeriveNormalize.lean | 49 +++++++++++ .../Proof/Argon2/X86_64/DerivePrepareCT.lean | 16 ++++ 4 files changed, 188 insertions(+) create mode 100644 lean/VerifiedGarbage/Impl/Argon2/X86_64/Derive.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/References.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveNormalize.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrepareCT.lean diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/Derive.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/Derive.lean new file mode 100644 index 000000000..6cec26a39 --- /dev/null +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/Derive.lean @@ -0,0 +1,37 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.InitialBody +import VerifiedGarbage.Impl.Argon2.X86_64.Parameters + +/-! The complete System V entry point, including u32 argument normalization. -/ + +namespace VG.Impl.Argon2.X86_64.Derive + +open VG VG.X86_64 +open VG.Impl.Argon2.X86_64.HPrime (at_) + +def saved : List Reg := [.rbx, .rbp, .r12, .r13, .r14, .r15] + +def normalize (offset : Nat) : List Instr := + [.mov .rax (.mem (at_ .rbp offset)), .mov32 .rax (.reg .rax), .store (at_ .rbp offset) .rax] + +def setup : List Instr := + [.mov .rbp (.reg .rsp), .mov32 .rdi (.reg .rdi), .mov32 .r9 (.reg .r9), + .store (at_ .rbp 72) .r9, .store (at_ .rbp 80) .r8, + .store (at_ .rbp 88) .rcx, .store (at_ .rbp 96) .rdx, + .store (at_ .rbp 104) .rsi, .store (at_ .rbp 112) .rdi, + .mov .rbx (.mem (at_ .rbp 248))] + +def prepare : Prog isa := .seq (.block setup) + (.seq (.block (normalize 176)) (.seq (.block (normalize 184)) (.block (normalize 192)))) + +/-- One nested frame per saved register restores every register separately. +The inner fifteen words reserve the 120-byte local argument/hash frame. -/ +def frame (body : Prog isa) : List Reg → Prog isa + | [] => .frame (.push (List.replicate 15 .rax)) body (.pop .rax 15) + | r :: rs => .frame (.push [r]) (frame body rs) (.pop r 1) + +def body (name : String) (hash : HPrime.Hash) : Prog isa := + .seq prepare (.seq Parameters.code (InitialBody.code name hash)) + +def code (name : String) (hash : HPrime.Hash) : Prog isa := frame (body name hash) saved + +end VG.Impl.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/References.lean b/lean/VerifiedGarbage/Proof/Argon2/References.lean new file mode 100644 index 000000000..b2b3d6241 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/References.lean @@ -0,0 +1,86 @@ +import VerifiedGarbage.Proof.Argon2.Iterations +import VerifiedGarbage.Proof.Argon2.FillStep + +/-! The reviewed flattened reference log determines every lane/column pair. -/ + +namespace VG.Proof.Argon2 + +open VG.Spec.Argon2 + +def Columns (q : Nat) (s : FillState) : Prop := ∀ ref ∈ s.indices, ref.2 < q + +theorem fold_columns {α : Type} (q : Nat) (f : FillState → α → FillState) + (step : ∀ s a, Columns q s → Columns q (f s a)) (xs : List α) (s : FillState) (h : Columns q s) : + Columns q (xs.foldl f s) := by + induction xs generalizing s with + | nil => exact h + | cons x xs ih => exact ih (f s x) (step s x h) + +theorem fillBlock_columns (p : Params) (positive : 0 < p.laneLen) + (pass slice lane index : Nat) (s : FillState) (h : Columns p.laneLen s) : + Columns p.laneLen (fillBlock p pass slice lane index s) := by + by_cases skipped : pass = 0 ∧ slice = 0 ∧ index < 2 + · rw [fillBlock, ite_eq_left skipped]; exact h + · unfold Columns + rw [FillStep.indices p pass lane slice index s (by omega)] + split + · exact h + · intro ref hr + simp only [List.mem_cons] at hr + rcases hr with rfl | hr + · change _ % p.laneLen < p.laneLen + exact Nat.mod_lt _ positive + · exact h ref hr + +theorem fillPass_columns (p : Params) (positive : 0 < p.laneLen) (s : FillState) (pass : Nat) + (h : Columns p.laneLen s) : Columns p.laneLen (fillPass p s pass) := by + unfold fillPass + apply fold_columns + · intro s slice hs + apply fold_columns + · intro s lane hs + exact fold_columns _ _ (fun s index hs => fillBlock_columns p positive pass slice lane index s hs) _ s hs + · exact hs + · exact h + +theorem fill_columns (p : Params) (positive : 0 < p.laneLen) (password salt secret ad : List Byte) : + Columns p.laneLen (fill p password salt secret ad) := by + unfold fill + apply fold_columns _ _ (fun s pass hs => fillPass_columns p positive s pass hs) + intro ref hr + exact False.elim (List.not_mem_nil hr) + +def flattenRef (q : Nat) (ref : Nat × Nat) : Nat := ref.1 * q + ref.2 + +def decodeRef (q n : Nat) : Nat × Nat := (n / q, n % q) + +theorem decode_flatten (q : Nat) (positive : 0 < q) (ref : Nat × Nat) (bound : ref.2 < q) : + decodeRef q (flattenRef q ref) = ref := by + unfold decodeRef flattenRef + rw [Nat.mul_comm ref.1 q, Nat.mul_add_div positive, + Nat.div_eq_of_lt bound, Nat.add_zero, Nat.mul_add_mod_self_left, Nat.mod_eq_of_lt bound] + +theorem decode_list (q : Nat) (positive : 0 < q) (xs : List (Nat × Nat)) + (bound : ∀ ref ∈ xs, ref.2 < q) : (xs.map (flattenRef q)).map (decodeRef q) = xs := by + induction xs with + | nil => rfl + | cons ref xs ih => + simp only [List.map_cons] + rw [decode_flatten q positive ref (bound ref (List.mem_cons_self ..)), + ih (fun r hr => bound r (List.mem_cons_of_mem ref hr))] + +theorem references_injective (p : Params) (positive : 0 < p.laneLen) + (password₁ salt₁ secret₁ ad₁ password₂ salt₂ secret₂ ad₂ : List Byte) + (same : references p password₁ salt₁ secret₁ ad₁ = references p password₂ salt₂ secret₂ ad₂) : + (fill p password₁ salt₁ secret₁ ad₁).indices = (fill p password₂ salt₂ secret₂ ad₂).indices := by + apply List.reverse_inj.mp + have left := fill_columns p positive password₁ salt₁ secret₁ ad₁ + have right := fill_columns p positive password₂ salt₂ secret₂ ad₂ + have decoded := congrArg (List.map (decodeRef p.laneLen)) same + change ((fill p password₁ salt₁ secret₁ ad₁).indices.reverse.map (flattenRef p.laneLen)).map _ = + ((fill p password₂ salt₂ secret₂ ad₂).indices.reverse.map (flattenRef p.laneLen)).map _ at decoded + rw [decode_list p.laneLen positive _ (fun ref hr => left ref (List.mem_reverse.mp hr)), + decode_list p.laneLen positive _ (fun ref hr => right ref (List.mem_reverse.mp hr))] at decoded + exact decoded + +end VG.Proof.Argon2 diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveNormalize.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveNormalize.lean new file mode 100644 index 000000000..1b9b1067f --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveNormalize.lean @@ -0,0 +1,49 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.Derive +import VerifiedGarbage.Proof.Argon2.X86_64.InitialArgs +import VerifiedGarbage.Proof.Framework.Offset + +/-! Normalize u32 stack arguments without assuming anything about their upper bits. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 +open VG.Impl.Argon2.X86_64.Derive + +structure Normalized (s t : State) (d : Nat) : Prop where + mem : t.mem = s.mem.writeW (s.gpr .rbp + BitVec.ofNat 64 d) + (((s.mem.readW (s.gpr .rbp + BitVec.ofNat 64 d) 64).setWidth 32).setWidth 64) + regs : ∀ r, r ≠ .rax → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + +theorem normalize_ok (s : State) (d : Nat) + (read : InRegions (s.rd ++ s.wr) (s.gpr .rbp + BitVec.ofNat 64 d) 8) + (write : InRegions s.wr (s.gpr .rbp + BitVec.ofNat 64 d) 8) : + WP isa (.block (normalize d)) s (Normalized s · d) := by + apply WP.of_runBlock + simp only [normalize, runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, + readSrc32, State.load64, State.store64, State.setReg32, State.ea, Impl.Argon2.X86_64.at_, BitVec.ofInt_natCast, + RegUpd.gpr_setReg, RegUpd.mem_setReg, RegUpd.rd_setReg, RegUpd.wr_setReg, + read, write, reduceCtorEq, ite_true, ite_false, Option.map_some, Option.some.injEq, exists_eq_left'] + refine ⟨rfl, ?_, rfl, rfl, rfl⟩ + intro r hr + simp only [RegUpd.gpr_setReg, hr, ite_false] + +theorem Normalized.word {s t : State} {d : Nat} (h : Normalized s t d) : + t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 d) 64 = + (((s.mem.readW (s.gpr .rbp + BitVec.ofNat 64 d) 64).setWidth 32).setWidth 64) := by + rw [h.regs .rbp (by decide), h.mem, Mem.readW_writeW_self64] + +theorem Normalized.frame {s t : State} {d : Nat} (h : Normalized s t d) : + Frame [⟨s.gpr .rbp + BitVec.ofNat 64 d, 8⟩] s.mem t.mem := by + rw [h.mem] + exact (Frame.refl _ _).writeW (List.mem_singleton_self _) _ (Region.contains_self _ _) + +theorem Normalized.other_word {s t : State} {d : Nat} (h : Normalized s t d) + (e : Nat) (separate : e + 8 ≤ d ∨ d + 8 ≤ e) (ed : e + 8 < 2 ^ 64) (dd : d + 8 < 2 ^ 64) : + t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 e) 64 = s.mem.readW (s.gpr .rbp + BitVec.ofNat 64 e) 64 := by + rw [h.regs .rbp (by decide), h.mem] + exact Mem.readW_writeW_sep (Offset.sep _ separate (Nat.le_of_lt ed) (Nat.le_of_lt dd)) (by decide) + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrepareCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrepareCT.lean new file mode 100644 index 000000000..fba199fbe --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrepareCT.lean @@ -0,0 +1,16 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveNormalize +import VerifiedGarbage.Proof.Framework.X86_64.RelCT + +/-! ABI argument preparation accesses only fixed offsets of the public stack. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem prepare_rel : RelCT isa (fun s t => s.gpr .rsp = t.gpr .rsp) + Impl.Argon2.X86_64.Derive.prepare (fun _ _ => True) := + RelCT.taint (A := taint) (Taint.ofRegs [.rsp]) (fun _ _ h => + Taint.agree_ofRegs (by intro r hr; simp only [List.mem_singleton] at hr; subst r; exact h)) + (by taint_decide) + +end VG.Proof.Argon2.X86_64.Derive From 95a7868bc7400d10a34b0f9c8709e6cbbb07936c Mon Sep 17 00:00:00 2001 From: Paul Kehrer <161495+reaperhulk@users.noreply.github.com> Date: Thu, 1 Oct 2026 22:29:13 +0000 Subject: [PATCH 5/8] Connect reviewed leakage and prove ABI argument stores --- .../Proof/Argon2/X86_64/DeriveEntry.lean | 29 +++++++++++++ .../Proof/Argon2/X86_64/DeriveStore.lean | 40 ++++++++++++++++++ .../Proof/Argon2/X86_64/DeriveStores.lean | 37 ++++++++++++++++ .../Argon2/X86_64/InitialBodyReviewedCT.lean | 42 +++++++++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveEntry.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveStore.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveStores.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyReviewedCT.lean diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveEntry.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveEntry.lean new file mode 100644 index 000000000..8b5eb5518 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveEntry.lean @@ -0,0 +1,29 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveStore + +/-! Establish the local frame base and normalize register-passed u32 arguments. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +structure Entered (s t : State) : Prop where + bp : t.gpr .rbp = s.gpr .rsp + kind : t.gpr .rdi = ((s.gpr .rdi).setWidth 32).setWidth 64 + passes : t.gpr .r9 = ((s.gpr .r9).setWidth 32).setWidth 64 + regs : ∀ r, r ≠ .rbp → r ≠ .rdi → r ≠ .r9 → t.gpr r = s.gpr r + mem : t.mem = s.mem + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + +theorem entry_ok (s : State) : + WP isa (.block [.mov .rbp (.reg .rsp), .mov32 .rdi (.reg .rdi), .mov32 .r9 (.reg .r9)]) s (Entered s) := by + apply WP.of_runBlock + simp only [runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, readSrc32, + State.setReg32, RegUpd.gpr_setReg, reduceCtorEq, + ite_false, Option.map_some, Option.some.injEq, exists_eq_left'] + refine ⟨rfl, rfl, rfl, ?_, rfl, rfl, rfl, rfl⟩ + intro r hb hd h9 + simp only [RegUpd.gpr_setReg, hb, hd, h9, ite_false] + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveStore.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveStore.lean new file mode 100644 index 000000000..cd9a91675 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveStore.lean @@ -0,0 +1,40 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveNormalize + +/-! Save each incoming argument with one short symbolic execution. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +structure Stored (s t : State) (d : Nat) (r : Reg) : Prop where + mem : t.mem = s.mem.writeW (s.gpr .rbp + BitVec.ofNat 64 d) (s.gpr r) + regs : t.gpr = s.gpr + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + +theorem store_ok (s : State) (d : Nat) (r : Reg) + (write : InRegions s.wr (s.gpr .rbp + BitVec.ofNat 64 d) 8) : + WP isa (.block [.store (Impl.Argon2.X86_64.at_ .rbp d) r]) s (Stored s · d r) := by + apply WP.of_runBlock + simp only [runBlock_cons, runStep_some, runBlock_nil, exec, State.store64, + State.ea, Impl.Argon2.X86_64.at_, BitVec.ofInt_natCast, write, + ite_true, Option.some.injEq, exists_eq_left'] + exact ⟨rfl, rfl, rfl, rfl, rfl⟩ + +theorem Stored.word {s t : State} {d : Nat} {r : Reg} (h : Stored s t d r) : + t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 d) 64 = s.gpr r := by + rw [h.regs, h.mem, Mem.readW_writeW_self64] + +theorem Stored.frame {s t : State} {d : Nat} {r : Reg} (h : Stored s t d r) : + Frame [⟨s.gpr .rbp + BitVec.ofNat 64 d, 8⟩] s.mem t.mem := by + rw [h.mem] + exact (Frame.refl _ _).writeW (List.mem_singleton_self _) _ (Region.contains_self _ _) + +theorem Stored.other_word {s t : State} {d : Nat} {r : Reg} (h : Stored s t d r) + (e : Nat) (separate : e + 8 ≤ d ∨ d + 8 ≤ e) (ed : e + 8 ≤ 2 ^ 64) (dd : d + 8 ≤ 2 ^ 64) : + t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 e) 64 = s.mem.readW (s.gpr .rbp + BitVec.ofNat 64 e) 64 := by + rw [h.regs, h.mem] + exact Mem.readW_writeW_sep (Offset.sep _ separate ed dd) (by decide) + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveStores.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveStores.lean new file mode 100644 index 000000000..abf54c1cf --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveStores.lean @@ -0,0 +1,37 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveStore + +/-! Compose argument stores without re-executing a growing symbolic memory state. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def saveMemory (s : State) (args : List (Nat × Reg)) : Mem := + args.foldl (fun m arg => m.writeW (s.gpr .rbp + BitVec.ofNat 64 arg.1) (s.gpr arg.2)) s.mem + +structure Saved (s t : State) (args : List (Nat × Reg)) : Prop where + mem : t.mem = saveMemory s args + regs : t.gpr = s.gpr + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + +theorem stores_ok (args : List (Nat × Reg)) (s : State) + (write : ∀ arg ∈ args, InRegions s.wr (s.gpr .rbp + BitVec.ofNat 64 arg.1) 8) : + WP isa (.block (args.map fun arg => .store (Impl.Argon2.X86_64.at_ .rbp arg.1) arg.2)) s (Saved s · args) := by + induction args generalizing s with + | nil => exact WP.block_nil ⟨rfl, rfl, rfl, rfl, rfl⟩ + | cons arg args ih => + rw [List.map_cons] + change WP isa (.block (([.store (Impl.Argon2.X86_64.at_ .rbp arg.1) arg.2] : List Instr) ++ _)) s _ + rw [WP.block_append_iff] + refine (store_ok s arg.1 arg.2 (write arg (List.mem_cons_self ..))).mono ?_ + intro t ht + refine (ih t (fun a ha => by rw [ht.wr, ht.regs]; exact write a (List.mem_cons_of_mem arg ha))).mono ?_ + intro u hu + refine ⟨?_, hu.regs.trans ht.regs, hu.rd.trans ht.rd, hu.wr.trans ht.wr, hu.mxcsr.trans ht.mxcsr⟩ + rw [hu.mem] + unfold saveMemory + rw [ht.regs, ht.mem, List.foldl_cons] + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyReviewedCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyReviewedCT.lean new file mode 100644 index 000000000..ac230e336 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyReviewedCT.lean @@ -0,0 +1,42 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.InitialBodyCT +import VerifiedGarbage.Proof.Argon2.References + +/-! Use exactly the flattened leakage allowance of the shared derive contract. -/ + +namespace VG.Proof.Argon2.X86_64.InitialBody + +open VG VG.X86_64 VG.Spec.Argon2 +open VG.Impl.Argon2.X86_64.Initial + +def references (p : Params) (s : State) : List Nat := Spec.Argon2.references p + (Initial.inputBytes s passwordOffset passwordLenOffset) + (Initial.inputBytes s saltOffset saltLenOffset) + (Initial.inputBytes s secretOffset secretLenOffset) + (Initial.inputBytes s adOffset adLenOffset) + +structure ReviewedRelated (p : Params) (s t : State) : Prop where + left : Ready p s + right : Ready p t + hashing : Initial.Related s t + matrices : FillKernel.matrix s = FillKernel.matrix t + outputs : FinalOutput.output s = FinalOutput.output t + works : FinalOutput.work s = FinalOutput.work t + references : references p s = references p t + +theorem ReviewedRelated.related {p : Params} {s t : State} (h : ReviewedRelated p s t) : Related p s t := by + refine ⟨h.left, h.right, h.hashing, h.matrices, h.outputs, h.works, ?_⟩ + have parameters := h.left.filling.environment.parameters + have positive : 0 < p.laneLen := by + have segments := Proof.Argon2.laneLen_segments p parameters.lanesPositive + have minimum := parameters.segment_bound.1 + omega + have indices := Proof.Argon2.references_injective p positive _ _ _ _ _ _ _ _ h.references + unfold initial + rw [Proof.Argon2.iterations_fill, Proof.Argon2.iterations_fill] + exact indices + +theorem reviewed_rel (v : Proof.Blake2.X86_64.Backend) (name : String) (p : Params) : + RelCT isa (ReviewedRelated p) (Impl.Argon2.X86_64.InitialBody.code name (HPrime.hash v)) (fun _ _ => True) := + (code_rel v name p).mono (fun _ _ h => h.related) (fun _ _ h => h) + +end VG.Proof.Argon2.X86_64.InitialBody From 094d71ed1203f04bfce01d5523f55287c4fd3fcf Mon Sep 17 00:00:00 2001 From: Paul Kehrer <161495+reaperhulk@users.noreply.github.com> Date: Fri, 2 Oct 2026 01:19:01 +0000 Subject: [PATCH 6/8] Prove Argon2 argument preparation and nested ABI frames --- .../Impl/Argon2/X86_64/Derive.lean | 8 +- .../Proof/Argon2/X86_64/DeriveFrame.lean | 60 ++++++++++++ .../Proof/Argon2/X86_64/DeriveFrameCT.lean | 25 +++++ .../Proof/Argon2/X86_64/DeriveFrameState.lean | 73 ++++++++++++++ .../Argon2/X86_64/DeriveNormalizeArgs.lean | 75 ++++++++++++++ .../Proof/Argon2/X86_64/DeriveParameters.lean | 67 +++++++++++++ .../Argon2/X86_64/DeriveParametersCT.lean | 31 ++++++ .../Proof/Argon2/X86_64/DerivePrepare.lean | 77 +++++++++++++++ .../Proof/Argon2/X86_64/DeriveRestore.lean | 72 ++++++++++++++ .../Proof/Argon2/X86_64/DeriveSaved.lean | 46 +++++++++ .../Proof/Argon2/X86_64/DeriveScratch.lean | 29 ++++++ .../Proof/Argon2/X86_64/DeriveSetup.lean | 87 +++++++++++++++++ .../Proof/Argon2/X86_64/DeriveStores.lean | 65 ++++++++++++- .../X86_64/InitialBodyReviewedState.lean | 34 +++++++ .../Proof/Argon2/X86_64/InitialBodyState.lean | 97 +++++++++++++++++++ 15 files changed, 839 insertions(+), 7 deletions(-) create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrame.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrameCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrameState.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveNormalizeArgs.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveParameters.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveParametersCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrepare.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveRestore.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSaved.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveScratch.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSetup.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyReviewedState.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyState.lean diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/Derive.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/Derive.lean index 6cec26a39..74cd402e2 100644 --- a/lean/VerifiedGarbage/Impl/Argon2/X86_64/Derive.lean +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/Derive.lean @@ -20,8 +20,12 @@ def setup : List Instr := .store (at_ .rbp 104) .rsi, .store (at_ .rbp 112) .rdi, .mov .rbx (.mem (at_ .rbp 248))] -def prepare : Prog isa := .seq (.block setup) - (.seq (.block (normalize 176)) (.seq (.block (normalize 184)) (.block (normalize 192)))) +def normalizeArgs : List Nat → Prog isa + | [] => .block [] + | [d] => .block (normalize d) + | d :: e :: ds => .seq (.block (normalize d)) (normalizeArgs (e :: ds)) + +def prepare : Prog isa := .seq (.block setup) (normalizeArgs [176, 184, 192]) /-- One nested frame per saved register restores every register separately. The inner fifteen words reserve the 120-byte local argument/hash frame. -/ diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrame.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrame.lean new file mode 100644 index 000000000..206a3096f --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrame.lean @@ -0,0 +1,60 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.Derive +import VerifiedGarbage.Proof.Framework.X86_64.Frame + +/-! Compose the nested saved-register frames and the 120-byte local allocation. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def frameStart (s : State) : List Reg → State + | [] => pushed (List.replicate 15 .rax) s + | r :: rs => frameStart (pushed [r] s) rs + +def frameEnd (s : State) : List Reg → State + | [] => popped .rax 15 s + | r :: rs => popped r 1 (frameEnd s rs) + +theorem frameEnd_metadata (s t : State) (rs : List Reg) + (sp : t.gpr .rsp = (frameStart s rs).gpr .rsp) + (wr : t.wr = (frameStart s rs).wr) : + (frameEnd t rs).gpr .rsp = s.gpr .rsp ∧ (frameEnd t rs).wr = s.wr := by + induction rs generalizing s with + | nil => + constructor + · rw [frameEnd, popped_rsp, sp, frameStart, pushed_rsp] + simp only [List.length_replicate, Nat.reduceMul, BitVec.sub_add_cancel] + · rw [frameEnd, popped_wr, wr, frameStart, pushed_wr]; rfl + | cons r rs ih => + obtain ⟨innerSp, innerWr⟩ := ih (pushed [r] s) sp wr + constructor + · rw [frameEnd, popped_rsp, innerSp, pushed_rsp] + simp only [List.length_singleton, Nat.mul_one, BitVec.sub_add_cancel] + · rw [frameEnd, popped_wr, innerWr, pushed_wr]; rfl + +theorem frame_ok (s : State) (rs : List Reg) (body : Prog isa) (Q : State → Prop) + (notSp : .rsp ∉ rs) (space : 120 + 8 * rs.length ≤ (s.gpr .rsp).toNat) + (run : WP isa body (frameStart s rs) fun t => + t.gpr .rsp = (frameStart s rs).gpr .rsp ∧ t.wr = (frameStart s rs).wr ∧ Q (frameEnd t rs)) : + WP isa (Impl.Argon2.X86_64.Derive.frame body rs) s Q := by + induction rs generalizing s Q with + | nil => + exact WP.frame (by decide) (by decide) (by decide) (by simpa using space) run + | cons r rs ih => + simp only [List.mem_cons, not_or] at notSp + have pushedBound : ((pushed [r] s).gpr .rsp).toNat = (s.gpr .rsp).toNat - 8 := by + rw [pushed_rsp] + simp only [List.length_singleton, Nat.mul_one] + exact toNat_sub_ofNat (by simp only [List.length_cons] at space; omega) + have innerSpace : 120 + 8 * rs.length ≤ ((pushed [r] s).gpr .rsp).toNat := by + rw [pushedBound]; simp only [List.length_cons] at space; omega + apply WP.frame (by simp) (by simpa using notSp.1) (Ne.symm notSp.1) + (by simp only [List.length_cons] at space; simp only [List.length_singleton, Nat.mul_one]; omega) + apply ih (pushed [r] s) (fun t => t.gpr .rsp = (pushed [r] s).gpr .rsp ∧ + t.wr = (pushed [r] s).wr ∧ Q (popped r 1 t)) notSp.2 innerSpace + apply run.mono + rintro t ⟨sp, wr, result⟩ + obtain ⟨endSp, endWr⟩ := frameEnd_metadata (pushed [r] s) t rs sp wr + exact ⟨sp, wr, endSp, endWr, result⟩ + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrameCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrameCT.lean new file mode 100644 index 000000000..b3a931b99 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrameCT.lean @@ -0,0 +1,25 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveFrame + +/-! Saving and restoring the ABI frame leaks only the public stack pointer. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem frame_rel (rs : List Reg) (body : Prog isa) (P : State → State → Prop) + (sp : ∀ s t, P s t → s.gpr .rsp = t.gpr .rsp) + (run : RelCT isa (fun a b => ∃ s t, P s t ∧ a = frameStart s rs ∧ b = frameStart t rs) + body (fun _ _ => True)) : + RelCT isa P (Impl.Argon2.X86_64.Derive.frame body rs) (fun _ _ => True) := by + induction rs generalizing P with + | nil => exact RelCT.frame sp run + | cons r rs ih => + apply RelCT.frame sp + apply ih (fun a b => ∃ s t, P s t ∧ a = pushed [r] s ∧ b = pushed [r] t) ?_ ?_ + · rintro a b ⟨s, t, hp, rfl, rfl⟩ + rw [pushed_rsp, pushed_rsp, sp s t hp] + · apply run.mono ?_ (fun _ _ h => h) + rintro a b ⟨u, v, ⟨s, t, hp, rfl, rfl⟩, rfl, rfl⟩ + exact ⟨s, t, hp, rfl, rfl⟩ + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrameState.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrameState.lean new file mode 100644 index 000000000..1f73720aa --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrameState.lean @@ -0,0 +1,73 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveFrame +import VerifiedGarbage.Proof.Framework.Offset + +/-! Exact stack depth and memory modified by the entry-point prologue. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem frameStart_sp (s : State) (rs : List Reg) : + (frameStart s rs).gpr .rsp = s.gpr .rsp - BitVec.ofNat 64 (120 + 8 * rs.length) := by + induction rs generalizing s with + | nil => rw [frameStart, pushed_rsp]; rfl + | cons r rs ih => + rw [frameStart, ih, pushed_rsp] + simp only [List.length_cons, List.length_nil, Nat.zero_add, Nat.mul_one] + rw [BitVec.sub_sub, ← BitVec.ofNat_add] + exact congrArg (fun n => s.gpr .rsp - BitVec.ofNat 64 n) (by omega) + +theorem frameStart_reg (s : State) (rs : List Reg) (r : Reg) (notSp : r ≠ .rsp) : + (frameStart s rs).gpr r = s.gpr r := by + induction rs generalizing s with + | nil => exact pushed_gpr _ _ notSp + | cons x xs ih => exact (ih (pushed [x] s)).trans (pushed_gpr _ _ notSp) + +theorem frameStart_rd (s : State) (rs : List Reg) : (frameStart s rs).rd = s.rd := by + induction rs generalizing s with + | nil => exact pushed_rd .. + | cons r rs ih => exact (ih (pushed [r] s)).trans (pushed_rd ..) + +theorem frameStart_frame (s : State) (rs : List Reg) (notSp : .rsp ∉ rs) + (space : 120 + 8 * rs.length ≤ (s.gpr .rsp).toNat) : + Frame [below (s.gpr .rsp) (120 + 8 * rs.length)] s.mem (frameStart s rs).mem := by + induction rs generalizing s with + | nil => + exact (pushRegs_mem s (List.replicate 15 .rax) (by decide) (by simpa using space)).1 + | cons r rs ih => + simp only [List.mem_cons, not_or] at notSp + have enough : 8 ≤ (s.gpr .rsp).toNat := by simp only [List.length_cons] at space; omega + have innerSpace : 120 + 8 * rs.length ≤ ((pushed [r] s).gpr .rsp).toNat := by + rw [pushed_rsp] + simp only [List.length_singleton, Nat.mul_one] + rw [toNat_sub_ofNat enough] + simp only [List.length_cons] at space; omega + have outer := (pushRegs_mem s [r] (by simpa using notSp.1) + (by simpa using enough)).1 + have inner := ih (pushed [r] s) notSp.2 innerSpace + apply (outer.sub ?_).trans (inner.sub ?_) + · intro region hr + simp only [List.mem_singleton] at hr; subst region + refine ⟨_, List.mem_singleton_self _, ?_⟩ + exact Offset.sub_below (s.gpr .rsp) (by simp only [List.length_cons, List.length_nil]; omega) + (by simp only [List.length_singleton, Nat.mul_one]; omega) + · intro region hr + simp only [List.mem_singleton] at hr; subst region + refine ⟨_, List.mem_singleton_self _, ?_⟩ + rw [pushed_rsp] + simp only [List.length_cons, List.length_nil, Nat.zero_add, Nat.mul_one, below] + rw [BitVec.sub_sub, ← BitVec.ofNat_add, + show 8 + (120 + 8 * rs.length) = 120 + 8 * (rs.length + 1) by omega] + exact Region.sub_prefix (by omega) + +theorem frameEnd_mem (s : State) (rs : List Reg) : (frameEnd s rs).mem = s.mem := by + induction rs with + | nil => exact popped_mem .. + | cons r rs ih => exact (popped_mem ..).trans ih + +theorem frameEnd_rd (s : State) (rs : List Reg) : (frameEnd s rs).rd = s.rd := by + induction rs with + | nil => exact popped_rd .. + | cons r rs ih => exact (popped_rd ..).trans ih + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveNormalizeArgs.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveNormalizeArgs.lean new file mode 100644 index 000000000..3a0a507db --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveNormalizeArgs.lean @@ -0,0 +1,75 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveNormalize + +/-! Normalize distinct stack slots while retaining every other frame word. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def normalizedWord (s : State) (d : Nat) : Addr := + (((s.mem.readW (s.gpr .rbp + BitVec.ofNat 64 d) 64).setWidth 32).setWidth 64) + +structure NormalizedArgs (s t : State) (ds : List Nat) : Prop where + values : ∀ d ∈ ds, t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 d) 64 = normalizedWord s d + regs : ∀ r, r ≠ .rax → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + frame : Frame (ds.map fun d => (⟨s.gpr .rbp + BitVec.ofNat 64 d, 8⟩ : Region)) s.mem t.mem + +theorem NormalizedArgs.other_word {s t : State} {ds : List Nat} (h : NormalizedArgs s t ds) + (e : Nat) (bound : e + 8 < 2 ^ 64) + (separate : ∀ d ∈ ds, e + 8 ≤ d ∨ d + 8 ≤ e) + (bounds : ∀ d ∈ ds, d + 8 < 2 ^ 64) : + t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 e) 64 = s.mem.readW (s.gpr .rbp + BitVec.ofNat 64 e) 64 := by + rw [h.regs .rbp (by decide)] + apply h.frame.readW (r := ⟨s.gpr .rbp + BitVec.ofNat 64 e, 8⟩) (Region.contains_self _ _) ?_ (by decide) + intro region hr + obtain ⟨d, hd, rfl⟩ := List.mem_map.mp hr + exact Offset.disjoint _ (separate d hd) (Nat.le_of_lt bound) (Nat.le_of_lt (bounds d hd)) + +theorem normalizeArgs_ok (ds : List Nat) (s : State) + (read : ∀ d ∈ ds, InRegions (s.rd ++ s.wr) (s.gpr .rbp + BitVec.ofNat 64 d) 8) + (write : ∀ d ∈ ds, InRegions s.wr (s.gpr .rbp + BitVec.ofNat 64 d) 8) + (separate : ds.Pairwise fun d e => d + 8 ≤ e ∨ e + 8 ≤ d) + (bounds : ∀ d ∈ ds, d + 8 < 2 ^ 64) : + WP isa (Impl.Argon2.X86_64.Derive.normalizeArgs ds) s (NormalizedArgs s · ds) := by + induction ds generalizing s with + | nil => exact WP.block_nil ⟨by simp, fun _ _ => rfl, rfl, rfl, rfl, Frame.refl _ _⟩ + | cons d ds ih => + cases ds with + | nil => + refine (normalize_ok s d (read d (by simp)) (write d (by simp))).mono ?_ + intro t ht + exact ⟨fun e he => by simp only [List.mem_singleton] at he; subst e; exact ht.word, + ht.regs, ht.rd, ht.wr, ht.mxcsr, ht.frame⟩ + | cons e ds => + obtain ⟨headSep, tailSep⟩ := List.pairwise_cons.mp separate + refine WP.seq ((normalize_ok s d (read d (List.mem_cons_self ..)) + (write d (List.mem_cons_self ..))).mono ?_) + intro t ht + refine (ih t + (fun x hx => by rw [ht.rd, ht.wr, ht.regs .rbp (by decide)]; exact read x (List.mem_cons_of_mem d hx)) + (fun x hx => by rw [ht.wr, ht.regs .rbp (by decide)]; exact write x (List.mem_cons_of_mem d hx)) + tailSep (fun x hx => bounds x (List.mem_cons_of_mem d hx))).mono ?_ + intro u hu + refine ⟨?_, fun r hr => (hu.regs r hr).trans (ht.regs r hr), + hu.rd.trans ht.rd, hu.wr.trans ht.wr, hu.mxcsr.trans ht.mxcsr, ?_⟩ + · intro x hx + rcases List.mem_cons.mp hx with rfl | hx + · rw [hu.other_word x (bounds x (List.mem_cons_self ..)) headSep + (fun a ha => bounds a (List.mem_cons_of_mem x ha))] + exact ht.word + · rw [hu.values x hx] + unfold normalizedWord + have sep : x + 8 ≤ d ∨ d + 8 ≤ x := (headSep x hx).symm + rw [ht.other_word x sep (bounds x (List.mem_cons_of_mem d hx)) (bounds d (List.mem_cons_self ..))] + · apply (ht.frame.mono ?_).trans + · have frame := hu.frame + rw [ht.regs .rbp (by decide)] at frame + exact frame.mono (fun _ h => List.mem_cons_of_mem _ h) + · intro region hr + simp only [List.mem_singleton] at hr; subst region + exact List.mem_cons_self .. + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveParameters.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveParameters.lean new file mode 100644 index 000000000..2ec676de1 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveParameters.lean @@ -0,0 +1,67 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.Parameters +import VerifiedGarbage.Proof.Argon2.X86_64.InitialBodyState + +/-! Compute the rounded lane length before entering the complete Argon2 body. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 VG.Spec.Argon2 +open VG.Impl.Argon2.X86_64.Initial + +/-- A specification state for the body's readiness predicate, not executable code. -/ +def dimensionState (s : State) (p : Params) : State := + s.setReg .r13 (BitVec.ofNat 64 p.laneLen) + +theorem dimension_frame (s : State) (p : Params) : InitialBody.SameFrame s (dimensionState s p) := by + refine ⟨?_, ?_, ?_, ?_, ?_, ?_⟩ + · exact RegUpd.gpr_setReg_of_ne _ _ (by decide) + · exact RegUpd.gpr_setReg_of_ne _ _ (by decide) + · exact RegUpd.gpr_setReg_of_ne _ _ (by decide) + · exact RegUpd.mem_setReg .. + · exact RegUpd.rd_setReg .. + · exact RegUpd.wr_setReg .. + +theorem parameters_frame {s t : State} (p : Params) + (keeps : Divide.Keeps Parameters.changed s t) : + InitialBody.SameFrame (dimensionState s p) t := by + have frame := dimension_frame s p + refine ⟨?_, ?_, ?_, keeps.mem.trans frame.mem.symm, + keeps.rd.trans frame.rd.symm, keeps.wr.trans frame.wr.symm⟩ + · exact (keeps.regs .rbp (by decide)).trans frame.bp.symm + · exact (keeps.regs .rbx (by decide)).trans frame.bx.symm + · exact (keeps.regs .rsp (by decide)).trans frame.sp.symm + +theorem parameters_ready {s t : State} {p : Params} + (h : InitialBody.Ready p (dimensionState s p)) + (length : t.gpr .r13 = BitVec.ofNat 64 p.laneLen) + (keeps : Divide.Keeps Parameters.changed s t) : InitialBody.Ready p t := + h.of_state (parameters_frame p keeps) length + +theorem parameters_body_ok (v : Proof.Blake2.X86_64.Backend) (name : String) + (s : State) (p : Params) (parameters : Parameters.Ready p s) + (body : InitialBody.Ready p (dimensionState s p)) : + WP isa (.seq Impl.Argon2.X86_64.Parameters.code + (Impl.Argon2.X86_64.InitialBody.code name (HPrime.hash v))) s (InitialBody.Done s · p) := by + refine WP.seq ((Parameters.code_ok s p parameters).mono ?_) + rintro a ⟨length, keeps⟩ + refine (InitialBody.code_ok v name a p (parameters_ready body length keeps)).mono ?_ + intro t done + have bp := keeps.regs .rbp (by decide) + have sp := keeps.regs .rsp (by decide) + have base : FillKernel.matrix a = FillKernel.matrix s := by + unfold FillKernel.matrix; rw [keeps.mem, bp] + have work : FinalOutput.work a = FinalOutput.work s := by + unfold FinalOutput.work; rw [keeps.mem, bp] + have output : FinalOutput.output a = FinalOutput.output s := by + unfold FinalOutput.output; rw [keeps.mem, bp] + refine ⟨?_, done.bp.trans bp, done.sp.trans sp, + done.rd.trans keeps.rd, done.wr.trans keeps.wr, ?_⟩ + · have digest := done.digest + simp only [Initial.inputBytes, Initial.wordAt, keeps.mem, bp, output] at digest + exact digest + · have frame := done.frame + rw [InitFill.writes_eq s a p bp sp base work output] at frame + rw [keeps.mem] at frame + exact frame + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveParametersCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveParametersCT.lean new file mode 100644 index 000000000..7721e7209 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveParametersCT.lean @@ -0,0 +1,31 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveParameters +import VerifiedGarbage.Proof.Argon2.X86_64.ParametersCT +import VerifiedGarbage.Proof.Argon2.X86_64.InitialBodyReviewedState + +/-! Parameter calculation followed by the complete body obeys the reviewed leakage. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 VG.Spec.Argon2 + +structure ParametersRelated (p : Params) (s t : State) : Prop where + left : Parameters.Ready p s + right : Parameters.Ready p t + body : InitialBody.ReviewedRelated p (dimensionState s p) (dimensionState t p) + +theorem parameters_body_rel (v : Proof.Blake2.X86_64.Backend) (name : String) (p : Params) : + RelCT isa (ParametersRelated p) + (.seq Impl.Argon2.X86_64.Parameters.code + (Impl.Argon2.X86_64.InitialBody.code name (HPrime.hash v))) (fun _ _ => True) := by + have preparation := (Parameters.code_rel.mono (P' := ParametersRelated p) + (fun s t h => by + have left := dimension_frame s p + have right := dimension_frame t p + exact left.bp.symm.trans (h.body.hashing.bp.trans right.bp)) + (fun _ _ h => h)).wpDep (fun s t h => + ⟨Parameters.code_ok s p h.left, Parameters.code_ok t p h.right⟩) + refine preparation.seq ((InitialBody.reviewed_rel v name p).mono ?_ (fun _ _ h => h)) + rintro a b ⟨_, s, t, h, ⟨length₁, keeps₁⟩, ⟨length₂, keeps₂⟩⟩ + exact h.body.of_state (parameters_frame p keeps₁) (parameters_frame p keeps₂) length₁ length₂ + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrepare.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrepare.lean new file mode 100644 index 000000000..e715d3e20 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrepare.lean @@ -0,0 +1,77 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveSetup +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveNormalizeArgs + +/-! Complete ABI preparation retains the inputs and exposes normalized public arguments. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def normalizedOffsets : List Nat := [176, 184, 192] + +def prepareWrites (s : State) : List Region := + ⟨s.gpr .rsp, 120⟩ :: normalizedOffsets.map fun d => ⟨s.gpr .rsp + BitVec.ofNat 64 d, 8⟩ + +theorem SetupDone.other_word {s t : State} (h : SetupDone s t) (d : Nat) + (afterFrame : 120 ≤ d) (bound : d + 8 < 2 ^ 64) : + t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 d) 64 = s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 d) 64 := by + rw [h.bp] + apply h.frame.readW (r := ⟨s.gpr .rsp + BitVec.ofNat 64 d, 8⟩) (Region.contains_self _ _) ?_ (by decide) + intro region hr + simp only [List.mem_singleton] at hr; subst region + simpa only [BitVec.add_zero] using Offset.disjoint (s.gpr .rsp) (d := d) (n := 8) (e := 0) (k := 120) + (Or.inr afterFrame) (Nat.le_of_lt bound) (by decide) + +structure Prepared (s t : State) : Prop where + bp : t.gpr .rbp = s.gpr .rsp + sp : t.gpr .rsp = s.gpr .rsp + scratch : t.gpr .rbx = s.mem.readW (s.gpr .rsp + 248) 64 + values : ∀ arg ∈ arguments, t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 arg.1) 64 = argumentValue s arg.2 + normalized : ∀ d ∈ normalizedOffsets, t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 d) 64 = + (((s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 d) 64).setWidth 32).setWidth 64) + regs : ∀ r ∈ calleeSaved, r ≠ .rbp → r ≠ .rbx → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + frame : Frame (prepareWrites s) s.mem t.mem + +theorem prepare_ok (s : State) (frameWrite : Covers [⟨s.gpr .rsp, 120⟩] s.wr) + (read : ∀ d ∈ 248 :: normalizedOffsets, InRegions (s.rd ++ s.wr) (s.gpr .rsp + BitVec.ofNat 64 d) 8) + (write : ∀ d ∈ normalizedOffsets, InRegions s.wr (s.gpr .rsp + BitVec.ofNat 64 d) 8) : + WP isa Impl.Argon2.X86_64.Derive.prepare s (Prepared s) := by + unfold Impl.Argon2.X86_64.Derive.prepare + have scratchRead : InRegions (s.rd ++ s.wr) (s.gpr .rsp + 248) 8 := read 248 (List.mem_cons_self ..) + refine WP.seq ((setup_ok s frameWrite scratchRead).mono ?_) + intro a setup + refine (normalizeArgs_ok normalizedOffsets a + (fun d hd => by rw [setup.rd, setup.wr, setup.bp]; exact read d (List.mem_cons_of_mem _ hd)) + (fun d hd => by rw [setup.wr, setup.bp]; exact write d hd) (by decide) (by decide)).mono ?_ + intro t normalized + refine ⟨(normalized.regs .rbp (by decide)).trans setup.bp, + (normalized.regs .rsp (by decide)).trans setup.sp, + (normalized.regs .rbx (by decide)).trans setup.scratch, ?_, ?_, ?_, + normalized.rd.trans setup.rd, normalized.wr.trans setup.wr, normalized.mxcsr.trans setup.mxcsr, ?_⟩ + · intro arg ha + have bound : ∀ arg ∈ arguments, arg.1 + 8 < 2 ^ 64 := by decide + have separate : ∀ arg ∈ arguments, ∀ d ∈ normalizedOffsets, arg.1 + 8 ≤ d ∨ d + 8 ≤ arg.1 := by decide + rw [normalized.other_word arg.1 (bound arg ha) (separate arg ha) (by decide)] + exact setup.values arg ha + · intro d hd + rw [normalized.values d hd] + unfold normalizedWord + have afterFrame : ∀ d ∈ normalizedOffsets, 120 ≤ d := by decide + have bound : ∀ d ∈ normalizedOffsets, d + 8 < 2 ^ 64 := by decide + rw [setup.other_word d (afterFrame d hd) (bound d hd)] + · intro r hr hb hx + have notAx : ∀ r ∈ calleeSaved, r ≠ .rax := by decide + exact (normalized.regs r (notAx r hr)).trans (setup.regs r hr hb hx) + · apply (setup.frame.mono (by + intro region hr + simp only [List.mem_singleton] at hr + subst region + exact List.mem_cons_self ..)).trans + have frame := normalized.frame + rw [setup.bp] at frame + exact frame.mono (fun _ h => List.mem_cons_of_mem _ h) + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveRestore.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveRestore.lean new file mode 100644 index 000000000..2cf651be0 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveRestore.lean @@ -0,0 +1,72 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveSaved +import VerifiedGarbage.Proof.Framework.X86_64.RegUpd + +/-! Reload all saved registers from their unchanged stack slots. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem frameEnd_sp (s : State) (rs : List Reg) : + (frameEnd s rs).gpr .rsp = s.gpr .rsp + BitVec.ofNat 64 (120 + 8 * rs.length) := by + induction rs with + | nil => rw [frameEnd, popped_rsp]; rfl + | cons r rs ih => + rw [frameEnd, popped_rsp, ih, BitVec.add_assoc, + ← BitVec.ofNat_add] + exact congrArg (fun n => s.gpr .rsp + BitVec.ofNat 64 n) + (by simp only [List.length_cons]; omega) + +theorem popped_one_reg (s : State) (r : Reg) (notSp : r ≠ .rsp) : + (popped r 1 s).gpr r = s.mem.readW (s.gpr .rsp) 64 := by + change ((s.setReg r (s.mem.readW (s.gpr .rsp) 64)).setReg .rsp (s.gpr .rsp + 8)).gpr r = _ + rw [RegUpd.gpr_setReg_of_ne _ _ notSp, RegUpd.gpr_setReg_self] + +theorem frameEnd_restore (s : State) (rs : List Reg) (values : Reg → Addr) + (notSp : .rsp ∉ rs) (distinct : rs.Nodup) + (words : ∀ j (hj : j < rs.length), + s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 (120 + 8 * rs.length - 8 * (j + 1))) 64 = values rs[j]) : + ∀ r ∈ rs, (frameEnd s rs).gpr r = values r := by + induction rs with + | nil => intro r hr; exact False.elim (List.not_mem_nil hr) + | cons r rs ih => + simp only [List.mem_cons, not_or] at notSp + have nodup := List.nodup_cons.mp distinct + have innerWords : ∀ j (hj : j < rs.length), + s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 (120 + 8 * rs.length - 8 * (j + 1))) 64 = values rs[j] := by + intro j hj + have word := words (j + 1) (by simp only [List.length_cons]; omega) + have offset : 120 + 8 * (r :: rs).length - 8 * (j + 1 + 1) = + 120 + 8 * rs.length - 8 * (j + 1) := by + simp only [List.length_cons]; omega + rw [offset] at word + exact word + have inner := ih notSp.2 nodup.2 innerWords + intro x hx + simp only [List.mem_cons] at hx + rcases hx with rfl | hx + · rw [frameEnd, popped_one_reg _ _ (Ne.symm notSp.1), frameEnd_mem, frameEnd_sp] + have word := words 0 (by simp) + have offset : 120 + 8 * (x :: rs).length - 8 * (0 + 1) = 120 + 8 * rs.length := by + simp only [List.length_cons]; omega + rw [offset] at word + exact word + · rw [frameEnd, popped_gpr r 1 (frameEnd s rs) (r' := x) (fun h => notSp.2 (h ▸ hx)) + (fun h => nodup.1 (h ▸ hx))] + exact inner x hx + +theorem frame_restored (s t : State) (rs : List Reg) (notSp : .rsp ∉ rs) + (distinct : rs.Nodup) (space : 120 + 8 * rs.length ≤ (s.gpr .rsp).toNat) + (sp : t.gpr .rsp = (frameStart s rs).gpr .rsp) + (unchanged : ∀ j (_hj : j < rs.length), + t.mem.readW (s.gpr .rsp - BitVec.ofNat 64 (8 * (j + 1))) 64 = + (frameStart s rs).mem.readW (s.gpr .rsp - BitVec.ofNat 64 (8 * (j + 1))) 64) : + ∀ r ∈ rs, (frameEnd t rs).gpr r = s.gpr r := by + apply frameEnd_restore t rs s.gpr notSp distinct + intro j hj + have offsetBound : 8 * (j + 1) ≤ 120 + 8 * rs.length := by omega + rw [sp, frameStart_sp, ← Offset.ofNat_sub_ofNat offsetBound, Offset.sub_add_sub_cancel, + unchanged j hj] + exact frameStart_word s rs notSp space j hj + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSaved.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSaved.lean new file mode 100644 index 000000000..d06ce51a0 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSaved.lean @@ -0,0 +1,46 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveFrameState + +/-! The nested prologue stores every callee-saved register at its exact ABI slot. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem frameStart_word (s : State) (rs : List Reg) (notSp : .rsp ∉ rs) + (space : 120 + 8 * rs.length ≤ (s.gpr .rsp).toNat) (j : Nat) (bound : j < rs.length) : + (frameStart s rs).mem.readW (s.gpr .rsp - BitVec.ofNat 64 (8 * (j + 1))) 64 = s.gpr rs[j] := by + induction rs generalizing s j with + | nil => exact absurd bound (Nat.not_lt_zero _) + | cons r rs ih => + simp only [List.mem_cons, not_or] at notSp + have enough : 8 ≤ (s.gpr .rsp).toNat := by simp only [List.length_cons] at space; omega + have innerSpace : 120 + 8 * rs.length ≤ ((pushed [r] s).gpr .rsp).toNat := by + rw [pushed_rsp] + simp only [List.length_singleton, Nat.mul_one] + rw [toNat_sub_ofNat enough] + simp only [List.length_cons] at space; omega + cases j with + | zero => + have stored := (pushRegs_mem s [r] (by simpa using notSp.1) + (by simpa using enough)).2 0 (by simp) + have inner := frameStart_frame (pushed [r] s) rs notSp.2 innerSpace + have unchanged : (frameStart (pushed [r] s) rs).mem.readW ((pushed [r] s).gpr .rsp) 64 = + (pushed [r] s).mem.readW ((pushed [r] s).gpr .rsp) 64 := inner.readW + (r := ⟨(pushed [r] s).gpr .rsp, 8⟩) (Region.contains_self _ _) (by + intro region hr + simp only [List.mem_singleton] at hr; subst region + apply Offset.base_disjoint_below + have limit := (s.gpr .rsp).isLt + simp only [List.length_cons] at space; omega) (by decide) + rw [pushed_rsp] at unchanged + simp only [List.length_singleton, Nat.mul_one] at unchanged + exact unchanged.trans stored + | succ j => + have word := ih (pushed [r] s) notSp.2 innerSpace j (by simpa using bound) + rw [pushed_rsp, pushed_gpr _ _ (fun h => notSp.2 (h ▸ List.getElem_mem _))] at word + simp only [List.length_singleton, Nat.mul_one] at word + rw [BitVec.sub_sub, ← BitVec.ofNat_add, + show 8 + 8 * (j + 1) = 8 * (j + 1 + 1) by omega] at word + exact word + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveScratch.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveScratch.lean new file mode 100644 index 000000000..4f6f02886 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveScratch.lean @@ -0,0 +1,29 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveStore + +/-! Load the caller-supplied hash workspace after saving incoming arguments. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +structure ScratchLoaded (s t : State) : Prop where + scratch : t.gpr .rbx = s.mem.readW (s.gpr .rbp + 248) 64 + regs : ∀ r, r ≠ .rbx → t.gpr r = s.gpr r + mem : t.mem = s.mem + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + +theorem scratch_ok (s : State) (read : InRegions (s.rd ++ s.wr) (s.gpr .rbp + 248) 8) : + WP isa (.block [.mov .rbx (.mem (Impl.Argon2.X86_64.at_ .rbp 248))]) s (ScratchLoaded s) := by + have ea : s.ea (Impl.Argon2.X86_64.at_ .rbp 248) = s.gpr .rbp + 248 := rfl + apply WP.of_runBlock + simp only [runBlock_cons, runStep_some, runBlock_nil, exec, readSrc, State.load64, + ea, read, + ite_true, Option.map_some, Option.some.injEq, exists_eq_left'] + refine ⟨?_, ?_, rfl, rfl, rfl, rfl⟩ + · exact RegUpd.gpr_setReg_self .. + · intro r hr + exact RegUpd.gpr_setReg_of_ne _ _ hr + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSetup.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSetup.lean new file mode 100644 index 000000000..aa59b63ff --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSetup.lean @@ -0,0 +1,87 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveEntry +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveStores +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveScratch +import VerifiedGarbage.TCB.X86_64.Target +import VerifiedGarbage.Proof.Framework.X86_64.Inline + +/-! Save the register arguments into the local derivation frame. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def arguments : List (Nat × Reg) := + [(72, .r9), (80, .r8), (88, .rcx), (96, .rdx), (104, .rsi), (112, .rdi)] + +def argumentValue (s : State) (r : Reg) : Addr := + if r = .rdi ∨ r = .r9 then ((s.gpr r).setWidth 32).setWidth 64 else s.gpr r + +theorem Entered.argument {s t : State} (h : Entered s t) (r : Reg) (bp : r ≠ .rbp) : + t.gpr r = argumentValue s r := by + unfold argumentValue + by_cases di : r = .rdi + · subst r; rw [ite_eq_left (Or.inl rfl)]; exact h.kind + · by_cases nine : r = .r9 + · subst r; rw [ite_eq_left (Or.inr rfl)]; exact h.passes + · rw [ite_eq_right (by simp only [di, nine, or_self, not_false_eq_true])] + exact h.regs r bp di nine + +structure SetupDone (s t : State) : Prop where + bp : t.gpr .rbp = s.gpr .rsp + sp : t.gpr .rsp = s.gpr .rsp + scratch : t.gpr .rbx = s.mem.readW (s.gpr .rsp + 248) 64 + values : ∀ arg ∈ arguments, t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 arg.1) 64 = argumentValue s arg.2 + regs : ∀ r ∈ calleeSaved, r ≠ .rbp → r ≠ .rbx → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + frame : Frame [⟨s.gpr .rsp, 120⟩] s.mem t.mem + +theorem setup_ok (s : State) (frameWrite : Covers [⟨s.gpr .rsp, 120⟩] s.wr) + (read : InRegions (s.rd ++ s.wr) (s.gpr .rsp + 248) 8) : + WP isa (.block Impl.Argon2.X86_64.Derive.setup) s (SetupDone s) := by + change WP isa (.block (([.mov .rbp (.reg .rsp), .mov32 .rdi (.reg .rdi), .mov32 .r9 (.reg .r9)] : List Instr) ++ + (arguments.map fun arg => .store (Impl.Argon2.X86_64.at_ .rbp arg.1) arg.2) ++ + ([.mov .rbx (.mem (Impl.Argon2.X86_64.at_ .rbp 248))] : List Instr))) s _ + rw [List.append_assoc, WP.block_append_iff] + refine (entry_ok s).mono ?_ + intro a entered + rw [WP.block_append_iff] + have write : ∀ arg ∈ arguments, InRegions a.wr (a.gpr .rbp + BitVec.ofNat 64 arg.1) 8 := by + intro arg ha + rw [entered.wr, entered.bp] + apply frameWrite + have bounds : ∀ arg ∈ arguments, arg.1 + 8 ≤ 120 := by decide + exact ⟨_, List.mem_singleton_self _, Offset.contains_base _ (bounds arg ha) (by have := bounds arg ha; omega)⟩ + refine (stores_values_ok arguments a write (by decide) (by decide)).mono ?_ + rintro b ⟨saved, values⟩ + have bp : b.gpr .rbp = s.gpr .rsp := by rw [saved.regs, entered.bp] + have scratchWord : b.mem.readW (b.gpr .rbp + 248) 64 = s.mem.readW (s.gpr .rsp + 248) 64 := by + have kept := saved.other_word 248 (by decide) (by decide) (by decide) + change b.mem.readW (b.gpr .rbp + 248) 64 = a.mem.readW (a.gpr .rbp + 248) 64 at kept + rw [kept, entered.bp, entered.mem] + refine (scratch_ok b (by rw [saved.rd, saved.wr, bp, entered.rd, entered.wr]; exact read)).mono ?_ + intro t loaded + refine ⟨(loaded.regs .rbp (by decide)).trans bp, ?_, loaded.scratch.trans scratchWord, ?_, ?_, + loaded.rd.trans (saved.rd.trans entered.rd), loaded.wr.trans (saved.wr.trans entered.wr), + loaded.mxcsr.trans (saved.mxcsr.trans entered.mxcsr), ?_⟩ + · rw [loaded.regs .rsp (by decide), saved.regs] + exact entered.regs .rsp (by decide) (by decide) (by decide) + · intro arg ha + rw [loaded.mem, loaded.regs .rbp (by decide), values arg ha] + have notBp : ∀ arg ∈ arguments, arg.2 ≠ .rbp := by decide + exact entered.argument arg.2 (notBp arg ha) + · intro r hr hb hx + have other : ∀ r ∈ calleeSaved, r ≠ .rdi ∧ r ≠ .r9 := by decide + rw [loaded.regs r hx, saved.regs] + exact entered.regs r hb (other r hr).1 (other r hr).2 + · rw [loaded.mem, ← entered.mem] + have frame := saved.frame + rw [entered.bp] at frame + apply frame.sub + intro region hr + obtain ⟨arg, ha, rfl⟩ := List.mem_map.mp hr + have bounds : ∀ arg ∈ arguments, arg.1 + 8 ≤ 120 := by decide + exact ⟨_, List.mem_singleton_self _, Offset.sub_base _ (bounds arg ha)⟩ + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveStores.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveStores.lean index abf54c1cf..ec49efc27 100644 --- a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveStores.lean +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveStores.lean @@ -15,12 +15,13 @@ structure Saved (s t : State) (args : List (Nat × Reg)) : Prop where rd : t.rd = s.rd wr : t.wr = s.wr mxcsr : t.mxcsr = s.mxcsr + frame : Frame (args.map fun arg => (⟨s.gpr .rbp + BitVec.ofNat 64 arg.1, 8⟩ : Region)) s.mem t.mem theorem stores_ok (args : List (Nat × Reg)) (s : State) (write : ∀ arg ∈ args, InRegions s.wr (s.gpr .rbp + BitVec.ofNat 64 arg.1) 8) : WP isa (.block (args.map fun arg => .store (Impl.Argon2.X86_64.at_ .rbp arg.1) arg.2)) s (Saved s · args) := by induction args generalizing s with - | nil => exact WP.block_nil ⟨rfl, rfl, rfl, rfl, rfl⟩ + | nil => exact WP.block_nil ⟨rfl, rfl, rfl, rfl, rfl, Frame.refl _ _⟩ | cons arg args ih => rw [List.map_cons] change WP isa (.block (([.store (Impl.Argon2.X86_64.at_ .rbp arg.1) arg.2] : List Instr) ++ _)) s _ @@ -29,9 +30,63 @@ theorem stores_ok (args : List (Nat × Reg)) (s : State) intro t ht refine (ih t (fun a ha => by rw [ht.wr, ht.regs]; exact write a (List.mem_cons_of_mem arg ha))).mono ?_ intro u hu - refine ⟨?_, hu.regs.trans ht.regs, hu.rd.trans ht.rd, hu.wr.trans ht.wr, hu.mxcsr.trans ht.mxcsr⟩ - rw [hu.mem] - unfold saveMemory - rw [ht.regs, ht.mem, List.foldl_cons] + refine ⟨?_, hu.regs.trans ht.regs, hu.rd.trans ht.rd, hu.wr.trans ht.wr, hu.mxcsr.trans ht.mxcsr, ?_⟩ + · rw [hu.mem] + unfold saveMemory + rw [ht.regs, ht.mem, List.foldl_cons] + · apply (ht.frame.mono ?_).trans + · have frame := hu.frame + rw [ht.regs] at frame + exact frame.mono (fun _ h => List.mem_cons_of_mem _ h) + · intro region hr + simp only [List.mem_singleton] at hr; subst region + exact List.mem_cons_self .. + +theorem Saved.other_word {s t : State} {args : List (Nat × Reg)} (h : Saved s t args) + (e : Nat) (bound : e + 8 ≤ 2 ^ 64) + (separate : ∀ arg ∈ args, e + 8 ≤ arg.1 ∨ arg.1 + 8 ≤ e) + (bounds : ∀ arg ∈ args, arg.1 + 8 ≤ 2 ^ 64) : + t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 e) 64 = s.mem.readW (s.gpr .rbp + BitVec.ofNat 64 e) 64 := by + rw [h.regs] + apply h.frame.readW (r := ⟨s.gpr .rbp + BitVec.ofNat 64 e, 8⟩) (Region.contains_self _ _) ?_ (by decide) + intro region hr + obtain ⟨arg, member, rfl⟩ := List.mem_map.mp hr + exact Offset.disjoint _ (separate arg member) bound (bounds arg member) + +theorem stores_values_ok (args : List (Nat × Reg)) (s : State) + (write : ∀ arg ∈ args, InRegions s.wr (s.gpr .rbp + BitVec.ofNat 64 arg.1) 8) + (separate : args.Pairwise fun a b => a.1 + 8 ≤ b.1 ∨ b.1 + 8 ≤ a.1) + (bounds : ∀ arg ∈ args, arg.1 + 8 ≤ 2 ^ 64) : + WP isa (.block (args.map fun arg => .store (Impl.Argon2.X86_64.at_ .rbp arg.1) arg.2)) s fun t => + Saved s t args ∧ ∀ arg ∈ args, t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 arg.1) 64 = s.gpr arg.2 := by + induction args generalizing s with + | nil => exact WP.block_nil ⟨⟨rfl, rfl, rfl, rfl, rfl, Frame.refl _ _⟩, by simp⟩ + | cons arg args ih => + obtain ⟨headSep, tailSep⟩ := List.pairwise_cons.mp separate + rw [List.map_cons] + change WP isa (.block (([.store (Impl.Argon2.X86_64.at_ .rbp arg.1) arg.2] : List Instr) ++ _)) s _ + rw [WP.block_append_iff] + refine (store_ok s arg.1 arg.2 (write arg (List.mem_cons_self ..))).mono ?_ + intro t ht + refine (ih t (fun a ha => by rw [ht.wr, ht.regs]; exact write a (List.mem_cons_of_mem arg ha)) + tailSep (fun a ha => bounds a (List.mem_cons_of_mem arg ha))).mono ?_ + rintro u ⟨hu, values⟩ + have saved : Saved s u (arg :: args) := by + refine ⟨?_, hu.regs.trans ht.regs, hu.rd.trans ht.rd, hu.wr.trans ht.wr, hu.mxcsr.trans ht.mxcsr, ?_⟩ + · rw [hu.mem]; unfold saveMemory; rw [ht.regs, ht.mem, List.foldl_cons] + · apply (ht.frame.mono ?_).trans + · have frame := hu.frame + rw [ht.regs] at frame + exact frame.mono (fun _ h => List.mem_cons_of_mem _ h) + · intro region hr + simp only [List.mem_singleton] at hr; subst region + exact List.mem_cons_self .. + refine ⟨saved, ?_⟩ + intro a ha + rcases List.mem_cons.mp ha with rfl | ha + · rw [hu.other_word a.1 (bounds a (List.mem_cons_self ..)) headSep + (fun b hb => bounds b (List.mem_cons_of_mem a hb))] + exact ht.word + · rw [values a ha, ht.regs] end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyReviewedState.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyReviewedState.lean new file mode 100644 index 000000000..d2446dc82 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyReviewedState.lean @@ -0,0 +1,34 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.InitialBodyReviewedCT +import VerifiedGarbage.Proof.Argon2.X86_64.InitialBodyState + +/-! Preserve precisely the reviewed leakage relation across parameter computation. -/ + +namespace VG.Proof.Argon2.X86_64.InitialBody + +open VG VG.X86_64 VG.Spec.Argon2 +open VG.Impl.Argon2.X86_64.Initial + +theorem ReviewedRelated.of_state {s₁ s₂ t₁ t₂ : State} {p : Params} + (h : ReviewedRelated p s₁ s₂) (k₁ : SameFrame s₁ t₁) (k₂ : SameFrame s₂ t₂) + (length₁ : t₁.gpr .r13 = BitVec.ofNat 64 p.laneLen) + (length₂ : t₂.gpr .r13 = BitVec.ofNat 64 p.laneLen) : ReviewedRelated p t₁ t₂ := by + refine ⟨h.left.of_state k₁ length₁, h.right.of_state k₂ length₂, ?_, ?_, ?_, ?_, ?_⟩ + · refine ⟨⟨k₁.hashSpace h.hashing.left.space, + fun input hi => k₁.input (h.hashing.left.inputs input hi)⟩, + ⟨k₂.hashSpace h.hashing.right.space, + fun input hi => k₂.input (h.hashing.right.inputs input hi)⟩, ?_, ?_, ?_, ?_⟩ + · rw [k₁.bp, k₂.bp]; exact h.hashing.bp + · rw [k₁.bx, k₂.bx]; exact h.hashing.bx + · rw [k₁.sp, k₂.sp]; exact h.hashing.sp + · intro d hd; rw [k₁.word d, k₂.word d]; exact h.hashing.words d hd + · unfold FillKernel.matrix; rw [k₁.mem, k₂.mem, k₁.bp, k₂.bp]; exact h.matrices + · unfold FinalOutput.output; rw [k₁.mem, k₂.mem, k₁.bp, k₂.bp]; exact h.outputs + · unfold FinalOutput.work; rw [k₁.mem, k₂.mem, k₁.bp, k₂.bp]; exact h.works + · unfold VG.Proof.Argon2.X86_64.InitialBody.references + rw [k₁.inputBytes passwordOffset passwordLenOffset, k₂.inputBytes passwordOffset passwordLenOffset, + k₁.inputBytes saltOffset saltLenOffset, k₂.inputBytes saltOffset saltLenOffset, + k₁.inputBytes secretOffset secretLenOffset, k₂.inputBytes secretOffset secretLenOffset, + k₁.inputBytes adOffset adLenOffset, k₂.inputBytes adOffset adLenOffset] + exact h.references + +end VG.Proof.Argon2.X86_64.InitialBody diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyState.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyState.lean new file mode 100644 index 000000000..8b043079f --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/InitialBodyState.lean @@ -0,0 +1,97 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.InitialBody + +/-! The complete body depends on the frame, allocation, and computed lane length. -/ + +namespace VG.Proof.Argon2.X86_64.InitialBody + +open VG VG.X86_64 VG.Spec.Argon2 + +structure SameFrame (s t : State) : Prop where + bp : t.gpr .rbp = s.gpr .rbp + bx : t.gpr .rbx = s.gpr .rbx + sp : t.gpr .rsp = s.gpr .rsp + mem : t.mem = s.mem + rd : t.rd = s.rd + wr : t.wr = s.wr + +theorem SameFrame.word {s t : State} (k : SameFrame s t) (d : Nat) : + Initial.wordAt t d = Initial.wordAt s d := by + unfold Initial.wordAt; rw [k.mem, k.bp] + +theorem SameFrame.inputBytes {s t : State} (k : SameFrame s t) (po lo : Nat) : + Initial.inputBytes t po lo = Initial.inputBytes s po lo := by + unfold Initial.inputBytes; rw [k.word po, k.word lo, k.mem] + +theorem SameFrame.hashSpace {s t : State} (k : SameFrame s t) + (h : Initial.Space s) : Initial.Space t := by + constructor + · rw [k.bx, k.wr]; exact h.work + · rw [k.sp, k.bx]; exact h.stackWork + · rw [k.bp, k.bx]; exact h.frameWork + · rw [k.bp, k.sp]; exact h.frameStack + · rw [k.rd, k.wr, k.bp]; exact h.readable + · rw [k.wr, k.bp]; exact h.output + +theorem SameFrame.input {s t : State} (k : SameFrame s t) {po lo : Nat} + (h : Initial.InputReady s po lo) : Initial.InputReady t po lo := by + have word : ∀ d, Initial.wordAt t d = Initial.wordAt s d := by + intro d; unfold Initial.wordAt; rw [k.mem, k.bp] + have region : Initial.inputRegion t po lo = Initial.inputRegion s po lo := by + unfold Initial.inputRegion; rw [word po, word lo] + refine ⟨k.hashSpace h.space, h.pointerSlot, h.lengthSlot, h.pointerBound, + h.lengthBound, ?_, ?_, ?_, ?_⟩ + · rw [word lo]; exact h.length + · rw [region, k.rd, k.wr]; exact h.cover + · rw [region, k.bx]; exact h.work + · rw [region, k.sp]; exact h.stack + +theorem SameFrame.output {s t : State} (k : SameFrame s t) {p : Params} + (h : FinalOutput.Ready p s) : FinalOutput.Ready p t := by + have base : ReductionState.matrix t = ReductionState.matrix s := by + unfold ReductionState.matrix; rw [k.mem, k.bp] + have work : FinalOutput.work t = FinalOutput.work s := by + unfold FinalOutput.work; rw [k.mem, k.bp] + have output : FinalOutput.output t = FinalOutput.output s := by + unfold FinalOutput.output; rw [k.mem, k.bp] + refine ⟨h.positive, h.bound, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · rw [k.rd, k.wr, k.bp]; exact h.reads + · rw [k.mem, k.bp]; exact h.tagWord + · rw [base, k.rd, k.wr]; exact h.input + · rw [output, k.wr]; exact h.outputWrite + · rw [work, k.wr]; exact h.workWrite + · rw [base, work]; exact h.inputWork + · rw [output, work]; exact h.outputWork + · rw [k.sp, base]; exact h.stackInput + · rw [k.sp, output]; exact h.stackOutput + · rw [k.sp, work]; exact h.stackWork + +theorem SameFrame.filling {s t : State} (k : SameFrame s t) {p : Params} + (h : InitFill.Ready p s) (laneLength : t.gpr .r13 = BitVec.ofNat 64 p.laneLen) : + InitFill.Ready p t := by + have base : FillKernel.matrix t = FillKernel.matrix s := by + unfold FillKernel.matrix; rw [k.mem, k.bp] + have work : FinalOutput.work t = FinalOutput.work s := by + unfold FinalOutput.work; rw [k.mem, k.bp] + refine ⟨?_, h.environment.of_state k.bp k.sp k.mem k.rd k.wr, + k.output h.output, h.positive, ?_⟩ + · constructor + · rw [base]; exact h.initializing.space.same k.wr k.bp k.bx k.sp + · rw [k.rd, k.wr, k.bp]; exact h.initializing.memoryRead + · rw [k.rd, k.wr, k.bp]; exact h.initializing.lanesRead + · rw [k.rd, k.wr, k.bp]; exact h.initializing.blocksRead + · unfold Initial.wordAt; rw [k.mem, k.bp, base]; exact h.initializing.memoryWord + · unfold Initial.wordAt; rw [k.mem, k.bp]; exact h.initializing.lanesWord + · unfold Initial.wordAt; rw [k.mem, k.bp]; exact h.initializing.blocksWord + · exact laneLength + · rw [k.bx, work]; exact h.scratch + +theorem Ready.of_state {s t : State} {p : Params} (h : Ready p s) + (k : SameFrame s t) (laneLength : t.gpr .r13 = BitVec.ofNat 64 p.laneLen) : Ready p t := by + refine ⟨k.hashSpace h.hashSpace, fun input hi => k.input (h.inputs input hi), ?_, + k.filling h.filling laneLength⟩ + have header : Initial.headerBytes t = Initial.headerBytes s := by + unfold Initial.headerBytes + simp only [Initial.headerValue, Initial.wordAt, k.mem, k.bp] + exact header.trans h.header + +end VG.Proof.Argon2.X86_64.InitialBody From ddf04ab5239f1952e8fbd3f777913526ef978d6e Mon Sep 17 00:00:00 2001 From: Paul Kehrer <161495+reaperhulk@users.noreply.github.com> Date: Fri, 2 Oct 2026 11:13:48 +0000 Subject: [PATCH 7/8] Verify the complete Argon2 entry point and expose the x86-64 API --- README.md | 6 +- bench/benches/primitives/argon2.rs | 74 + bench/benches/primitives/main.rs | 2 + bench/tests/argon2.rs | 69 + .../Generic/Blake2b/X86_64/Argon2.lean | 15 +- .../Impl/Argon2/X86_64/Derive.lean | 15 +- .../Impl/Argon2/X86_64/Initial.lean | 4 +- .../Proof/Argon2/X86_64/DeriveAbi.lean | 55 + .../Argon2/X86_64/DeriveAllocations.lean | 64 + .../Argon2/X86_64/DeriveBodyCorrect.lean | 56 + .../Proof/Argon2/X86_64/DeriveBodyPost.lean | 38 + .../Proof/Argon2/X86_64/DeriveBodyReady.lean | 51 + .../Proof/Argon2/X86_64/DeriveBodySaved.lean | 50 + .../Proof/Argon2/X86_64/DeriveCT.lean | 43 + .../Proof/Argon2/X86_64/DeriveContract.lean | 35 + .../Proof/Argon2/X86_64/DeriveCopyArg.lean | 46 + .../Proof/Argon2/X86_64/DeriveCopyArgs.lean | 70 + .../Proof/Argon2/X86_64/DeriveCorrect.lean | 29 + .../Proof/Argon2/X86_64/DeriveFillLayout.lean | 62 + .../Argon2/X86_64/DeriveFinalLayout.lean | 56 + .../Proof/Argon2/X86_64/DeriveFrame.lean | 10 +- .../Proof/Argon2/X86_64/DeriveFrameState.lean | 12 +- .../Proof/Argon2/X86_64/DeriveHashInputs.lean | 53 + .../Proof/Argon2/X86_64/DeriveHashSpace.lean | 24 + .../Proof/Argon2/X86_64/DeriveHeader.lean | 25 + .../Proof/Argon2/X86_64/DeriveInputBytes.lean | 50 + .../Proof/Argon2/X86_64/DeriveLit.lean | 19 + .../Argon2/X86_64/DeriveMemorySpace.lean | 37 + .../Proof/Argon2/X86_64/DeriveMetadata.lean | 55 + .../Proof/Argon2/X86_64/DeriveMxcsr.lean | 52 + .../Proof/Argon2/X86_64/DerivePrepare.lean | 21 +- .../Argon2/X86_64/DerivePrivatePrepare.lean | 98 + .../Argon2/X86_64/DerivePrivatePublic.lean | 91 + .../Proof/Argon2/X86_64/DerivePrologue.lean | 66 + .../Proof/Argon2/X86_64/DerivePublic.lean | 42 + .../Proof/Argon2/X86_64/DeriveRegions.lean | 62 + .../Proof/Argon2/X86_64/DeriveRestore.lean | 16 +- .../Proof/Argon2/X86_64/DeriveReturn.lean | 68 + .../Proof/Argon2/X86_64/DeriveSaved.lean | 4 +- .../Proof/Argon2/X86_64/DeriveSeparation.lean | 40 + .../Proof/Argon2/X86_64/DeriveSpSafe.lean | 61 + .../Proof/Argon2/X86_64/DeriveVerified.lean | 17 + .../Proof/Argon2/X86_64/DeriveWords.lean | 69 + src/argon2.rs | 168 + src/asm/x86_64/argon2.rs | 2854 +++++++++++++++++ src/lib.rs | 1 + tests/rfc9106/main.rs | 132 + 47 files changed, 4954 insertions(+), 33 deletions(-) create mode 100644 bench/benches/primitives/argon2.rs create mode 100644 bench/tests/argon2.rs create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveAbi.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveAllocations.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodyCorrect.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodyPost.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodyReady.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodySaved.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCT.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveContract.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCopyArg.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCopyArgs.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCorrect.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFillLayout.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFinalLayout.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveHashInputs.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveHashSpace.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveHeader.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveInputBytes.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveLit.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveMemorySpace.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveMetadata.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveMxcsr.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrivatePrepare.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrivatePublic.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrologue.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePublic.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveRegions.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveReturn.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSeparation.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSpSafe.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveVerified.lean create mode 100644 lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveWords.lean create mode 100644 src/argon2.rs create mode 100644 tests/rfc9106/main.rs diff --git a/README.md b/README.md index 72aebac11..4cda70629 100644 --- a/README.md +++ b/README.md @@ -586,7 +586,7 @@ yours to keep: ✅ -❌ +✅ ❌ @@ -602,7 +602,7 @@ yours to keep: ✅ -❌ +✅ ❌ @@ -618,7 +618,7 @@ yours to keep: ✅ -❌ +✅ ❌ diff --git a/bench/benches/primitives/argon2.rs b/bench/benches/primitives/argon2.rs new file mode 100644 index 000000000..db9343a51 --- /dev/null +++ b/bench/benches/primitives/argon2.rs @@ -0,0 +1,74 @@ +//! Complete Argon2 derivations, including allocation and initialization. + +use criterion::Criterion; + +pub const USES: &[&str] = &["argon2", "blake2b"]; + +#[cfg(target_arch = "x86_64")] +pub fn bench(c: &mut Criterion) { + use std::hint::black_box; + + use criterion::BenchmarkId; + use verified_garbage::argon2::{Variant, derive}; + + use crate::{OPENSSL, VG}; + type Oracle = fn( + Option<&openssl::lib_ctx::LibCtxRef>, + &[u8], + &[u8], + Option<&[u8]>, + Option<&[u8]>, + u32, + u32, + u32, + &mut [u8], + ) -> Result<(), openssl::error::ErrorStack>; + for (variant, name, openssl) in [ + (Variant::Argon2d, "argon2d", openssl::kdf::argon2d as Oracle), + (Variant::Argon2i, "argon2i", openssl::kdf::argon2i), + (Variant::Argon2id, "argon2id", openssl::kdf::argon2id), + ] { + let mut g = c.benchmark_group(name); + g.sample_size(10); + for memory in [1024u32, 16384] { + let mut out = [0u8; 32]; + g.bench_function(BenchmarkId::new(VG, memory), |b| { + b.iter(|| { + derive( + variant, + black_box(b"password"), + black_box(b"saltsalt"), + 3, + memory, + 1, + 1, + b"", + b"", + &mut out, + ) + .unwrap() + }) + }); + g.bench_function(BenchmarkId::new(OPENSSL, memory), |b| { + b.iter(|| { + openssl( + None, + black_box(b"password"), + black_box(b"saltsalt"), + None, + None, + 3, + 1, + memory, + &mut out, + ) + .unwrap() + }) + }); + } + g.finish(); + } +} + +#[cfg(not(target_arch = "x86_64"))] +pub fn bench(_: &mut Criterion) {} diff --git a/bench/benches/primitives/main.rs b/bench/benches/primitives/main.rs index 7eb3b985c..0f3f12bda 100644 --- a/bench/benches/primitives/main.rs +++ b/bench/benches/primitives/main.rs @@ -17,6 +17,7 @@ use openssl::pkey::PKey; use openssl::sign::Signer; mod aes_gcm; +mod argon2; mod blake2b; mod blake2s; mod chacha20; @@ -236,6 +237,7 @@ const BENCHES: &[Bench] = &[ (poly1305::USES, poly1305::bench), (rc2_cbc::USES, rc2_cbc::bench), (triple_des_ecb::USES, triple_des_ecb::bench), + (argon2::USES, argon2::bench), (scrypt::USES, scrypt::bench), (sha1::USES, sha1::bench), (sha224::USES, sha224::bench), diff --git a/bench/tests/argon2.rs b/bench/tests/argon2.rs new file mode 100644 index 000000000..9f21d473f --- /dev/null +++ b/bench/tests/argon2.rs @@ -0,0 +1,69 @@ +//! Differential complete derivations against OpenSSL, including H′ boundaries. + +#![cfg(target_arch = "x86_64")] + +use verified_garbage::argon2::{Variant, derive}; + +type Oracle = fn( + Option<&openssl::lib_ctx::LibCtxRef>, + &[u8], + &[u8], + Option<&[u8]>, + Option<&[u8]>, + u32, + u32, + u32, + &mut [u8], +) -> Result<(), openssl::error::ErrorStack>; + +#[test] +fn matches_openssl() { + for (variant, oracle) in [ + (Variant::Argon2d, openssl::kdf::argon2d as Oracle), + (Variant::Argon2i, openssl::kdf::argon2i), + (Variant::Argon2id, openssl::kdf::argon2id), + ] { + for (lanes, memory) in [(1, 8), (1, 9), (2, 16), (3, 25), (2, 1040)] { + for iterations in [1, 2] { + for length in [4, 32, 64, 65, 96, 128] { + for (password, secret, ad) in [ + (&b""[..], &b""[..], &b""[..]), + (&b"password"[..], &b"secret"[..], &b"associated data"[..]), + ] { + let mut expected = vec![0; length]; + oracle( + None, + password, + b"saltsalt", + Some(ad), + Some(secret), + iterations, + lanes, + memory, + &mut expected, + ) + .unwrap(); + let mut actual = vec![0; length]; + derive( + variant, + password, + b"saltsalt", + iterations, + memory, + lanes, + 1, + secret, + ad, + &mut actual, + ) + .unwrap(); + assert_eq!( + actual, expected, + "{variant:?}, lanes={lanes}, memory={memory}, passes={iterations}, length={length}" + ); + } + } + } + } + } +} diff --git a/lean/VerifiedGarbage/Generic/Blake2b/X86_64/Argon2.lean b/lean/VerifiedGarbage/Generic/Blake2b/X86_64/Argon2.lean index 97092aebd..c0625664c 100644 --- a/lean/VerifiedGarbage/Generic/Blake2b/X86_64/Argon2.lean +++ b/lean/VerifiedGarbage/Generic/Blake2b/X86_64/Argon2.lean @@ -1,4 +1,4 @@ -import VerifiedGarbage.Proof.Argon2.X86_64.HPrime.Verified +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveVerified /-! # Argon2 H′ for every x86-64 BLAKE2b backend -/ @@ -16,6 +16,19 @@ def artifacts (v : Proof.Blake2.X86_64.Backend) : List Artifact := [ stack := 16 verified := Proof.Argon2.X86_64.HPrime.verified v spSafe := Proof.Argon2.X86_64.HPrime.spSafe v + features := v.features }, + { Spec.Argon2.deriveApi with + name := Spec.Argon2.deriveApi.name ++ v.suffix + target := VG.X86_64.target + doc := Spec.Argon2.deriveApi.doc + (notes := ["Serial lane evaluation honors every positive worker limit. All hashing uses \ + the selected BLAKE2b streaming backend, including H₀ and every H′ call."]) + code := Impl.Argon2.X86_64.Derive.code (Spec.Argon2.hPrimeApi.name ++ v.suffix) + (Proof.Argon2.X86_64.HPrime.hash v) + contract := Spec.Argon2.deriveContract VG.X86_64.abi 344 + stack := 344 + verified := Proof.Argon2.X86_64.Derive.verified v _ + spSafe := Proof.Argon2.X86_64.Derive.code_spSafe v _ features := v.features }] end VG.Generic.Blake2b.X86_64.Argon2 diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/Derive.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/Derive.lean index 74cd402e2..a60431a7b 100644 --- a/lean/VerifiedGarbage/Impl/Argon2/X86_64/Derive.lean +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/Derive.lean @@ -25,12 +25,21 @@ def normalizeArgs : List Nat → Prog isa | [d] => .block (normalize d) | d :: e :: ds => .seq (.block (normalize d)) (normalizeArgs (e :: ds)) -def prepare : Prog isa := .seq (.block setup) (normalizeArgs [176, 184, 192]) +def prepareLocal : Prog isa := .seq (.block setup) (normalizeArgs [176, 184, 192]) + +/-- Copy the twelve read-only caller stack arguments into private slots. +The original stack pointer is 320 bytes above this local frame. -/ +def copyArg (j : Nat) : List Instr := + [.mov .rax (.mem (at_ .rsp (328 + 8 * j))), .store (at_ .rsp (176 + 8 * j)) .rax] + +def copyArgs : List Instr := (List.range 12).flatMap copyArg + +def prepare : Prog isa := .seq (.block copyArgs) prepareLocal /-- One nested frame per saved register restores every register separately. -The inner fifteen words reserve the 120-byte local argument/hash frame. -/ +The inner thirty-four words reserve the 272-byte private argument/hash frame. -/ def frame (body : Prog isa) : List Reg → Prog isa - | [] => .frame (.push (List.replicate 15 .rax)) body (.pop .rax 15) + | [] => .frame (.push (List.replicate 34 .rax)) body (.pop .rax 34) | r :: rs => .frame (.push [r]) (frame body rs) (.pop r 1) def body (name : String) (hash : HPrime.Hash) : Prog isa := diff --git a/lean/VerifiedGarbage/Impl/Argon2/X86_64/Initial.lean b/lean/VerifiedGarbage/Impl/Argon2/X86_64/Initial.lean index a907bbb19..84546709f 100644 --- a/lean/VerifiedGarbage/Impl/Argon2/X86_64/Initial.lean +++ b/lean/VerifiedGarbage/Impl/Argon2/X86_64/Initial.lean @@ -18,8 +18,8 @@ namespace VG.Impl.Argon2.X86_64.Initial open VG.X86_64 open VG.Impl.Argon2.X86_64.HPrime (Hash at_) -/-- Frame offsets for the register arguments, followed by the caller's -stack arguments. The enclosing frame occupies 168 bytes. -/ +/-- Frame offsets for the saved register arguments and private copies of +the caller's stack arguments. The private frame occupies 272 bytes. -/ def passOffset : Nat := 72 def saltLenOffset : Nat := 80 def saltOffset : Nat := 88 diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveAbi.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveAbi.lean new file mode 100644 index 000000000..ac3cb6eff --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveAbi.lean @@ -0,0 +1,55 @@ +import VerifiedGarbage.Proof.Framework.Contract +import VerifiedGarbage.Proof.Framework.X86_64.Call +import VerifiedGarbage.Spec.Argon2.Contract +import VerifiedGarbage.TCB.X86_64.Target + +/-! Decode the reviewed System V contract without requiring normalized upper bits. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def abiWord (s : State) (d : Nat) : Addr := s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 d) 64 +def abiParams (s : State) : Spec.Argon2.Params := Spec.Argon2.params + ((s.gpr .rdi).setWidth 32).toNat ((s.gpr .r9).setWidth 32).toNat + ((abiWord s 8).setWidth 32).toNat ((abiWord s 16).setWidth 32).toNat (abiWord s 96).toNat + +def abiInputs (s : State) : List Region := + [⟨s.gpr .rsi, (s.gpr .rdx).toNat⟩, ⟨s.gpr .rcx, (s.gpr .r8).toNat⟩, + ⟨abiWord s 32, (abiWord s 40).toNat⟩, ⟨abiWord s 48, (abiWord s 56).toNat⟩] +def abiMatrix (s : State) : Region := ⟨abiWord s 64, (abiWord s 72).toNat * 1024⟩ +def abiWork (s : State) : Region := ⟨abiWord s 80, 16384⟩ +def abiOutput (s : State) : Region := ⟨abiWord s 88, (abiWord s 96).toNat⟩ +def abiArguments (s : State) : Region := ⟨s.gpr .rsp + BitVec.ofNat 64 8, 96⟩ +def abiBuffers (s : State) : List (Region × Bool) := + (abiInputs s).map (·, false) ++ [(abiMatrix s, true), (abiWork s, true), (abiOutput s, true)] + +structure AbiEnvironment (s : State) : Prop where + stack : 344 ≤ (s.gpr .rsp).toNat + wrap : (s.gpr .rsp).toNat + 104 ≤ 2 ^ 64 + rd : s.rd = abiInputs s ++ [abiArguments s] + wr : s.wr = [abiMatrix s, abiWork s, abiOutput s] + pairs : (abiBuffers s ++ [(abiArguments s, false)]).Pairwise + (fun a b => (a.2 || b.2) → a.1.Disjoint b.1) + reserved : ∀ r ∈ [⟨s.gpr .rsp, 8⟩, below (s.gpr .rsp) 344], + ∀ b ∈ abiBuffers s ++ [(abiArguments s, false)], r.Disjoint b.1 + bounds : ∀ b ∈ abiBuffers s, b.1.base.toNat + b.1.len ≤ 2 ^ 64 + kind : ((s.gpr .rdi).setWidth 32).toNat ≤ 2 + valid : Spec.Argon2.valid (abiParams s) (s.gpr .rdx).toNat (s.gpr .r8).toNat + (abiWord s 40).toNat (abiWord s 56).toNat + threads : 1 ≤ ((abiWord s 24).setWidth 32).toNat ∧ ((abiWord s 24).setWidth 32).toNat < 2 ^ 24 + blocks : (abiWord s 72).toNat = (abiParams s).blocks + +theorem abi_environment (s : State) (h : (Spec.Argon2.deriveContract X86_64.abi 344).pre s) : + AbiEnvironment s := by + sig_pre [Spec.Argon2.deriveContract, Spec.Argon2.deriveSig, X86_64.abi, + X86_64.argRegs, X86_64.stackArg, X86_64.stackArgAddr, List.range, List.range.loop] at h + sig_split h + constructor + all_goals sig_eval [abiInputs, abiArguments, abiMatrix, abiWork, abiOutput, abiBuffers, + abiWord, abiParams, below] + all_goals sig_and_intros + all_goals sig_close + all_goals with_reducible assumption + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveAllocations.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveAllocations.lean new file mode 100644 index 000000000..ad747fe2d --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveAllocations.lean @@ -0,0 +1,64 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveHashInputs + +/-! The exact matrix and output allocations of the signature remain writable. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem private_matrix_region {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : + (⟨FillKernel.matrix t, (abiParams s).blocks * 1024⟩ : Region) = abiMatrix s := by + have words := private_words h prepared + change (⟨Initial.wordAt t 232, (abiParams s).blocks * 1024⟩ : Region) = abiMatrix s + rw [words.matrix, ← h.blocks]; rfl + +theorem private_local_cover {s t : State} (prepared : PrivatePrepared (prologueState s) t) + (n : Nat) (bound : n ≤ 272) : Covers [⟨t.gpr .rbp, n⟩] t.wr := by + intro p k ⟨region, hr, hc⟩ + simp only [List.mem_singleton] at hr; subst region + refine ⟨⟨t.gpr .rbp, 272⟩, ?_, ?_⟩ + · rw [prepared.wr, prepared.bp] + exact frameStart_locals s _ + · unfold Region.Contains at hc ⊢ + exact Nat.le_trans hc bound + +theorem private_matrix_cover {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : + Covers [⟨FillKernel.matrix t, (abiParams s).blocks * 1024⟩] t.wr := by + have words := private_words h prepared + have member : abiMatrix s ∈ t.wr := private_wr_member prepared _ (by rw [h.wr]; exact List.mem_cons_self ..) + have matrix : (⟨FillKernel.matrix t, (abiParams s).blocks * 1024⟩ : Region) = abiMatrix s := by + change (⟨Initial.wordAt t 232, (abiParams s).blocks * 1024⟩ : Region) = abiMatrix s + rw [words.matrix, ← h.blocks]; rfl + intro p n ⟨region, hr, hc⟩ + simp only [List.mem_singleton] at hr; subst region + exact ⟨abiMatrix s, member, matrix ▸ hc⟩ + +theorem private_output_cover {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : + Covers [⟨FinalOutput.output t, (abiParams s).tagLen⟩] t.wr := by + have words := private_words h prepared + have member : abiOutput s ∈ t.wr := private_wr_member prepared _ (by + rw [h.wr]; exact List.mem_cons_of_mem _ (List.mem_cons_of_mem _ (List.mem_singleton_self _))) + have output : (⟨FinalOutput.output t, (abiParams s).tagLen⟩ : Region) = abiOutput s := by + change (⟨Initial.wordAt t 256, (abiParams s).tagLen⟩ : Region) = abiOutput s + rw [words.output]; rfl + intro p n ⟨region, hr, hc⟩ + simp only [List.mem_singleton] at hr; subst region + exact ⟨abiOutput s, member, output ▸ hc⟩ + +theorem private_work_cover {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) (n : Nat) (bound : n ≤ 16384) : + Covers [⟨FinalOutput.work t, n⟩] t.wr := by + have words := private_words h prepared + intro p k ⟨region, hr, hc⟩ + simp only [List.mem_singleton] at hr; subst region + refine ⟨abiWork s, private_work_member h prepared, ?_⟩ + change Region.Contains ⟨abiWord s 80, 16384⟩ p k + have pointer : FinalOutput.work t = abiWord s 80 := words.work + rw [pointer] at hc + unfold Region.Contains at hc ⊢ + exact Nat.le_trans hc bound + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodyCorrect.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodyCorrect.lean new file mode 100644 index 000000000..d98716586 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodyCorrect.lean @@ -0,0 +1,56 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveBodyPost + +/-! Preparation and the entire algorithm establish the shared API postcondition. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def bodyWrites (s : State) : List Region := + [abiMatrix s, abiWork s, abiOutput s, ⟨(prologueState s).gpr .rsp, 272⟩, + below ((prologueState s).gpr .rsp) 24] + +structure BodyDone (s t : State) : Prop where + post : (Spec.Argon2.deriveContract X86_64.abi 344).post s t + sp : t.gpr .rsp = (prologueState s).gpr .rsp + rd : t.rd = (prologueState s).rd + wr : t.wr = (prologueState s).wr + frame : Frame (bodyWrites s) (prologueState s).mem t.mem + +theorem private_body_frame {s t u : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) (done : InitialBody.Done t u (abiParams s)) : + Frame (bodyWrites s) (prologueState s).mem u.mem := by + have words := private_words h prepared + have matrix := private_matrix_region h prepared + have work : (⟨FinalOutput.work t, 16384⟩ : Region) = abiWork s := by + change (⟨Initial.wordAt t 248, 16384⟩ : Region) = abiWork s + rw [words.work]; rfl + have output : (⟨FinalOutput.output t, (abiParams s).tagLen⟩ : Region) = abiOutput s := by + change (⟨Initial.wordAt t 256, (abiParams s).tagLen⟩ : Region) = abiOutput s + rw [words.output]; rfl + have body := done.frame + rw [InitFill.writes, matrix, work, output, prepared.sp, prepared.bp] at body + apply ((private_prepare_frame prepared).mono ?_).trans (body.sub ?_) + · intro r hr + simp only [List.mem_singleton] at hr; subst r + simp only [bodyWrites, List.mem_cons, true_or, or_true] + · intro r hr + simp only [List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl + · exact ⟨abiMatrix s, by simp [bodyWrites], fun _ h => h⟩ + · exact ⟨abiWork s, by simp [bodyWrites], fun _ h => h⟩ + · exact ⟨abiOutput s, by simp [bodyWrites], fun _ h => h⟩ + · exact ⟨below ((prologueState s).gpr .rsp) 24, by simp [bodyWrites], fun _ h => h⟩ + · exact ⟨⟨(prologueState s).gpr .rsp, 272⟩, by simp [bodyWrites], Region.sub_prefix (by decide)⟩ + +theorem body_ok (v : Proof.Blake2.X86_64.Backend) (name : String) (s : State) (h : AbiEnvironment s) : + WP isa (Impl.Argon2.X86_64.Derive.body name (HPrime.hash v)) (prologueState s) (BodyDone s) := by + unfold Impl.Argon2.X86_64.Derive.body + refine WP.seq ((prologue_prepare s h).mono ?_) + intro t prepared + refine (private_pipeline_ok v name s t h prepared).mono ?_ + intro u done + exact ⟨private_done_post h prepared done, done.sp.trans prepared.sp, + done.rd.trans prepared.rd, done.wr.trans prepared.wr, private_body_frame h prepared done⟩ + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodyPost.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodyPost.lean new file mode 100644 index 000000000..0ca1f1968 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodyPost.lean @@ -0,0 +1,38 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveInputBytes + +/-! The complete body's digest is the public API postcondition on original input memory. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem private_done_post {s t u : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) (done : InitialBody.Done t u (abiParams s)) : + (Spec.Argon2.deriveContract X86_64.abi 344).post s u := by + have words := private_words h prepared + have password := private_input_bytes h prepared (104, 96) (by decide) + have salt := private_input_bytes h prepared (88, 80) (by decide) + have secret := private_input_bytes h prepared (200, 208) (by decide) + have ad := private_input_bytes h prepared (216, 224) (by decide) + change Initial.inputBytes t 104 96 = + Spec.Blake2.bytesAt s.mem (Initial.wordAt t 104) (Initial.wordAt t 96).toNat at password + change Initial.inputBytes t 88 80 = + Spec.Blake2.bytesAt s.mem (Initial.wordAt t 88) (Initial.wordAt t 80).toNat at salt + change Initial.inputBytes t 200 208 = + Spec.Blake2.bytesAt s.mem (Initial.wordAt t 200) (Initial.wordAt t 208).toNat at secret + change Initial.inputBytes t 216 224 = + Spec.Blake2.bytesAt s.mem (Initial.wordAt t 216) (Initial.wordAt t 224).toNat at ad + rw [words.password, words.passwordLength] at password + rw [words.salt, words.saltLength] at salt + rw [words.secret, words.secretLength] at secret + rw [words.ad, words.adLength] at ad + have digest := done.digest + change Spec.Blake2.bytesAt u.mem (Initial.wordAt t 256) (abiParams s).tagLen = + Spec.Argon2.derive (abiParams s) (Initial.inputBytes t 104 96) + (Initial.inputBytes t 88 80) (Initial.inputBytes t 200 208) (Initial.inputBytes t 216 224) at digest + rw [words.output, password, salt, secret, ad] at digest + sig_post [Spec.Argon2.deriveContract, Spec.Argon2.deriveSig, X86_64.abi, + X86_64.argRegs, X86_64.stackArg, X86_64.stackArgAddr, List.range, List.range.loop] + exact digest + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodyReady.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodyReady.lean new file mode 100644 index 000000000..a064fd1ac --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodyReady.lean @@ -0,0 +1,51 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveFinalLayout +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveHeader +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveParameters + +/-! The shared API contract supplies the complete body's precondition after preparation. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem DeriveWords.of_state {s a b : State} (h : DeriveWords s a) (k : InitialBody.SameFrame a b) : + DeriveWords s b := + ⟨(k.word 72).trans h.passes, (k.word 80).trans h.saltLength, (k.word 88).trans h.salt, + (k.word 96).trans h.passwordLength, (k.word 104).trans h.password, (k.word 112).trans h.kind, + (k.word 176).trans h.memory, (k.word 184).trans h.lanes, (k.word 200).trans h.secret, + (k.word 208).trans h.secretLength, (k.word 216).trans h.ad, (k.word 224).trans h.adLength, + (k.word 232).trans h.matrix, (k.word 240).trans h.blocks, (k.word 248).trans h.work, + (k.word 256).trans h.output, (k.word 264).trans h.tagLength⟩ + +theorem private_body_ready {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : + InitialBody.Ready (abiParams s) (dimensionState t (abiParams s)) := by + let a := dimensionState t (abiParams s) + have keeps : InitialBody.SameFrame t a := dimension_frame t (abiParams s) + have words : DeriveWords s a := (private_words h prepared).of_state keeps + have matrix : FillKernel.matrix a = FillKernel.matrix t := by + unfold FillKernel.matrix; rw [keeps.mem, keeps.bp] + have scratch : a.gpr .rbx = FinalOutput.work a := by + rw [keeps.bx, private_scratch h prepared] + exact words.work.symm + refine ⟨keeps.hashSpace (private_hash_space h prepared), + fun input hi => keeps.input (private_hash_inputs h prepared input hi), private_header words, ?_⟩ + refine ⟨?_, (private_fill_environment h prepared).of_state keeps.bp keeps.sp keeps.mem keeps.rd keeps.wr, + keeps.output (private_final_layout h prepared), h.valid.2.2.1, scratch⟩ + refine ⟨?_, ?_, ?_, ?_, ?_, words.lanes, ?_, ?_⟩ + · rw [matrix] + exact (private_memory_space h prepared).same keeps.wr keeps.bp keeps.bx keeps.sp + · rw [keeps.rd, keeps.wr, keeps.bp]; exact private_local_read prepared 232 8 (by decide) + · rw [keeps.rd, keeps.wr, keeps.bp]; exact private_local_read prepared 184 8 (by decide) + · rw [keeps.rd, keeps.wr, keeps.bp]; exact private_local_read prepared 240 8 (by decide) + · rfl + · rw [← Proof.Argon2.blocks_lanes (abiParams s) h.valid.1]; exact words.blocks + · exact RegUpd.gpr_setReg_self .. + +theorem private_pipeline_ok (v : Proof.Blake2.X86_64.Backend) (name : String) + (s t : State) (h : AbiEnvironment s) (prepared : PrivatePrepared (prologueState s) t) : + WP isa (.seq Impl.Argon2.X86_64.Parameters.code + (Impl.Argon2.X86_64.InitialBody.code name (HPrime.hash v))) t (InitialBody.Done t · (abiParams s)) := + parameters_body_ok v name t (abiParams s) (private_parameters h prepared) (private_body_ready h prepared) + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodySaved.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodySaved.lean new file mode 100644 index 000000000..a56afa660 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveBodySaved.lean @@ -0,0 +1,50 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveBodyCorrect + +/-! The whole body leaves the prologue's six saved-register slots untouched. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def savedSlot (s : State) (j : Nat) : Region := + ⟨s.gpr .rsp - BitVec.ofNat 64 (8 * (j + 1)), 8⟩ + +theorem saved_slot_sub (s : State) (j : Nat) (bound : j < 6) : + Region.Sub (savedSlot s j) (below (s.gpr .rsp) 344) := + Offset.sub_below _ (by omega) (by omega) + +theorem saved_slot_address (s : State) (j : Nat) (bound : j < 6) : + (savedSlot s j).base = (prologueState s).gpr .rsp + BitVec.ofNat 64 (320 - 8 * (j + 1)) := by + rw [prologue_sp] + exact Offset.sub_ofNat_eq _ (by omega) + +theorem saved_slot_buffers {s : State} (h : AbiEnvironment s) (j : Nat) (bound : j < 6) + (buffer : Region × Bool) (member : buffer ∈ abiBuffers s ++ [(abiArguments s, false)]) : + (savedSlot s j).Disjoint buffer.1 := + (h.reserved _ (List.mem_cons_of_mem _ (List.mem_singleton_self _)) buffer member).sub_left + (saved_slot_sub s j bound) + +theorem saved_slot_writes {s : State} (h : AbiEnvironment s) (j : Nat) (bound : j < 6) : + ∀ r ∈ bodyWrites s, (savedSlot s j).Disjoint r := by + intro r hr + simp only [bodyWrites, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl + · apply saved_slot_buffers h j bound (abiMatrix s, true) + simp only [abiBuffers, List.mem_append, List.mem_cons, true_or, or_true] + · apply saved_slot_buffers h j bound (abiWork s, true) + simp only [abiBuffers, List.mem_append, List.mem_cons, true_or, or_true] + · apply saved_slot_buffers h j bound (abiOutput s, true) + simp only [abiBuffers, List.mem_append, List.mem_cons, true_or, or_true] + · change (⟨(savedSlot s j).base, 8⟩ : Region).Disjoint _ + rw [saved_slot_address s j bound] + exact Offset.disjoint_base _ (by omega) (by omega) + · change (⟨(savedSlot s j).base, 8⟩ : Region).Disjoint _ + rw [saved_slot_address s j bound] + exact Offset.disjoint_below _ (by omega) + +theorem BodyDone.saved {s t : State} (h : AbiEnvironment s) (done : BodyDone s t) (j : Nat) (bound : j < 6) : + t.mem.readW (s.gpr .rsp - BitVec.ofNat 64 (8 * (j + 1))) 64 = + (prologueState s).mem.readW (s.gpr .rsp - BitVec.ofNat 64 (8 * (j + 1))) 64 := + done.frame.readW (r := savedSlot s j) (Region.contains_self _ _) (saved_slot_writes h j bound) (by decide) + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCT.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCT.lean new file mode 100644 index 000000000..4bbd3ec7f --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCT.lean @@ -0,0 +1,43 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DerivePrivatePublic +import VerifiedGarbage.Proof.Argon2.X86_64.DerivePrepareCT +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveFrameCT + +/-! The entire entry point leaks only the exact allowance of the shared contract. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def AbiRelated (s t : State) : Prop := AbiEnvironment s ∧ AbiEnvironment t ∧ AbiPublic s t + +def PrologueRelated (a b : State) : Prop := ∃ s t, AbiRelated s t ∧ a = prologueState s ∧ b = prologueState t + +theorem body_rel (v : Proof.Blake2.X86_64.Backend) (name : String) : + RelCT isa PrologueRelated (Impl.Argon2.X86_64.Derive.body name (HPrime.hash v)) (fun _ _ => True) := by + have preparation := (prepare_rel.mono (P' := PrologueRelated) (by + rintro a b ⟨s, t, h, rfl, rfl⟩ + rw [prologue_sp, prologue_sp, h.2.2.sp]) (fun _ _ h => h)).wpDep (F := fun a b => + ∃ s, a = prologueState s ∧ AbiEnvironment s ∧ PrivatePrepared a b) (by + rintro a b ⟨s, t, h, rfl, rfl⟩ + exact ⟨(prologue_prepare s h.1).mono (fun _ prepared => ⟨s, rfl, h.1, prepared⟩), + (prologue_prepare t h.2.1).mono (fun _ prepared => ⟨t, rfl, h.2.1, prepared⟩)⟩) + unfold Impl.Argon2.X86_64.Derive.body + apply preparation.seq + apply (RelCT.exists_ (fun p => parameters_body_rel v name p)).mono ?_ (fun _ _ h => h) + rintro a b ⟨_, x, y, ⟨s, t, h, rfl, rfl⟩, + ⟨u, hu, _, prepared₁⟩, ⟨w, hw, _, prepared₂⟩⟩ + have left : PrivatePrepared (prologueState s) a := prepared₁ + have right : PrivatePrepared (prologueState t) b := prepared₂ + exact ⟨abiParams s, private_parameters_related h.1 h.2.1 h.2.2 left right⟩ + +theorem code_ct (v : Proof.Blake2.X86_64.Backend) (name : String) : + ConstantTime isa (Spec.Argon2.deriveContract X86_64.abi 344).pre + (Spec.Argon2.deriveContract X86_64.abi 344).pub + (Impl.Argon2.X86_64.Derive.code name (HPrime.hash v)) := by + have full := frame_rel Impl.Argon2.X86_64.Derive.saved _ AbiRelated + (fun _ _ h => h.2.2.sp) (body_rel v name) + exact (full.mono (fun s t h => + ⟨abi_environment s h.1, abi_environment t h.2.1, abi_public s t h.2.2⟩) + (fun _ _ h => h)).constantTime + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveContract.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveContract.lean new file mode 100644 index 000000000..5c53449c2 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveContract.lean @@ -0,0 +1,35 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveAbi + +/-! A concrete caller establishes satisfiability of the shared derivation contract. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def satArgs : List Nat := [8, 1, 1, 0, 0, 0, 0, 0x10000, 8, 0x20000, 0x30000, 4] + +def satMem (a : Addr) : Byte := + let d := a.toNat - 0x40008 + if 0x40008 ≤ a.toNat ∧ a.toNat < 0x40068 then + ((BitVec.ofNat 64 (satArgs[d / 8]?.getD 0)) >>> (8 * (d % 8))).setWidth 8 + else 0 + +def satState : State where + gpr r := match r with + | .rsp => 0x40000 | .r9 => 1 | _ => 0 + cf := none + zf := none + sf := none + of := none + mem := satMem + rd := [⟨0, 0⟩, ⟨0, 0⟩, ⟨0, 0⟩, ⟨0, 0⟩, ⟨0x40008, 96⟩] + wr := [⟨0x10000, 8192⟩, ⟨0x20000, 16384⟩, ⟨0x30000, 4⟩] + +theorem contract_sat : ∃ s, (Spec.Argon2.deriveContract X86_64.abi 344).pre s := by + refine ⟨satState, ?_⟩ + sig_sat_check [Spec.Argon2.deriveContract, Spec.Argon2.deriveSig, X86_64.abi, + X86_64.argRegs, X86_64.stackArg, X86_64.stackArgAddr, List.range, List.range.loop, + satState, satMem, satArgs, Spec.Argon2.params, Spec.Argon2.valid, + Spec.Argon2.Params.blocks, Spec.Argon2.Params.segmentLen] + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCopyArg.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCopyArg.lean new file mode 100644 index 000000000..66536c307 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCopyArg.lean @@ -0,0 +1,46 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveNormalize + +/-! Copy a read-only caller argument into the private derivation frame. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def copySource (j : Nat) : Nat := 328 + 8 * j +def copyDestination (j : Nat) : Nat := 176 + 8 * j + +structure CopiedArg (s t : State) (j : Nat) : Prop where + mem : t.mem = s.mem.writeW (s.gpr .rsp + BitVec.ofNat 64 (copyDestination j)) + (s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 (copySource j)) 64) + regs : ∀ r, r ≠ .rax → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + +theorem copyArg_ok (s : State) (j : Nat) + (read : InRegions (s.rd ++ s.wr) (s.gpr .rsp + BitVec.ofNat 64 (copySource j)) 8) + (write : InRegions s.wr (s.gpr .rsp + BitVec.ofNat 64 (copyDestination j)) 8) : + WP isa (.block (Impl.Argon2.X86_64.Derive.copyArg j)) s (CopiedArg s · j) := by + change InRegions (s.rd ++ s.wr) (s.gpr .rsp + BitVec.ofNat 64 (328 + 8 * j)) 8 at read + change InRegions s.wr (s.gpr .rsp + BitVec.ofNat 64 (176 + 8 * j)) 8 at write + apply WP.of_runBlock + simp only [Impl.Argon2.X86_64.Derive.copyArg, runBlock_cons, runStep_some, runBlock_nil, + exec, State.load64, State.store64, readSrc, State.ea, Impl.Argon2.X86_64.at_, + BitVec.ofInt_natCast, RegUpd.gpr_setReg, RegUpd.mem_setReg, RegUpd.rd_setReg, + RegUpd.wr_setReg, read, write, reduceCtorEq, + ite_true, ite_false, Option.some.injEq, Option.map_some, exists_eq_left'] + refine ⟨rfl, ?_, rfl, rfl, rfl⟩ + intro r hr + exact RegUpd.gpr_setReg_of_ne _ _ hr + +theorem CopiedArg.frame {s t : State} {j : Nat} (h : CopiedArg s t j) : + Frame [⟨s.gpr .rsp + BitVec.ofNat 64 (copyDestination j), 8⟩] s.mem t.mem := by + rw [h.mem] + exact (Frame.refl _ _).writeW (List.mem_singleton_self _) _ (Region.contains_self _ _) + +theorem CopiedArg.word {s t : State} {j : Nat} (h : CopiedArg s t j) : + t.mem.readW (t.gpr .rsp + BitVec.ofNat 64 (copyDestination j)) 64 = + s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 (copySource j)) 64 := by + rw [h.regs .rsp (by decide), h.mem, Mem.readW_writeW_self64] + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCopyArgs.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCopyArgs.lean new file mode 100644 index 000000000..23b570dff --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCopyArgs.lean @@ -0,0 +1,70 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveCopyArg + +/-! Copy all stack arguments without modifying their caller-owned storage. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +structure CopiedArgs (s t : State) (js : List Nat) : Prop where + values : ∀ j ∈ js, t.mem.readW (t.gpr .rsp + BitVec.ofNat 64 (copyDestination j)) 64 = + s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 (copySource j)) 64 + regs : ∀ r, r ≠ .rax → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + mxcsr : t.mxcsr = s.mxcsr + frame : Frame (js.map fun j => (⟨s.gpr .rsp + BitVec.ofNat 64 (copyDestination j), 8⟩ : Region)) s.mem t.mem + +theorem CopiedArgs.other_word {s t : State} {js : List Nat} (h : CopiedArgs s t js) + (d : Nat) (bound : d + 8 ≤ 2 ^ 64) + (separate : ∀ j ∈ js, d + 8 ≤ copyDestination j ∨ copyDestination j + 8 ≤ d) + (bounds : ∀ j ∈ js, copyDestination j + 8 ≤ 2 ^ 64) : + t.mem.readW (t.gpr .rsp + BitVec.ofNat 64 d) 64 = s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 d) 64 := by + rw [h.regs .rsp (by decide)] + apply h.frame.readW (r := ⟨s.gpr .rsp + BitVec.ofNat 64 d, 8⟩) (Region.contains_self _ _) ?_ (by decide) + intro region hr + obtain ⟨j, hj, rfl⟩ := List.mem_map.mp hr + exact Offset.disjoint _ (separate j hj) bound (bounds j hj) + +theorem copyArgs_ok (js : List Nat) (s : State) (bounds : ∀ j ∈ js, j < 12) + (distinct : js.Nodup) + (read : ∀ j ∈ js, InRegions (s.rd ++ s.wr) (s.gpr .rsp + BitVec.ofNat 64 (copySource j)) 8) + (write : ∀ j ∈ js, InRegions s.wr (s.gpr .rsp + BitVec.ofNat 64 (copyDestination j)) 8) : + WP isa (.block (js.flatMap Impl.Argon2.X86_64.Derive.copyArg)) s (CopiedArgs s · js) := by + induction js generalizing s with + | nil => exact WP.block_nil ⟨by simp, fun _ _ => rfl, rfl, rfl, rfl, Frame.refl _ _⟩ + | cons j js ih => + have nodup := List.nodup_cons.mp distinct + have jBound := bounds j (List.mem_cons_self ..) + have tailBounds : ∀ x ∈ js, x < 12 := fun x hx => bounds x (List.mem_cons_of_mem _ hx) + rw [List.flatMap_cons, WP.block_append_iff] + refine (copyArg_ok s j (read j (List.mem_cons_self ..)) (write j (List.mem_cons_self ..))).mono ?_ + intro t ht + refine (ih t tailBounds nodup.2 + (fun x hx => by rw [ht.rd, ht.wr, ht.regs .rsp (by decide)]; exact read x (List.mem_cons_of_mem _ hx)) + (fun x hx => by rw [ht.wr, ht.regs .rsp (by decide)]; exact write x (List.mem_cons_of_mem _ hx))).mono ?_ + intro u hu + refine ⟨?_, fun r hr => (hu.regs r hr).trans (ht.regs r hr), + hu.rd.trans ht.rd, hu.wr.trans ht.wr, hu.mxcsr.trans ht.mxcsr, ?_⟩ + · intro x hx + rcases List.mem_cons.mp hx with rfl | hx + · rw [hu.other_word (copyDestination x) (by unfold copyDestination; omega) (by + intro y hy + have ne : y ≠ x := fun eq => nodup.1 (eq ▸ hy) + unfold copyDestination; omega) (by + intro y hy; have := tailBounds y hy; unfold copyDestination; omega)] + exact ht.word + · rw [hu.values x hx, ht.regs .rsp (by decide), ht.mem] + apply Mem.readW_writeW_sep ?_ (by decide) + have xBound := tailBounds x hx + exact Offset.sep _ (Or.inr (by unfold copyDestination copySource; omega)) + (by unfold copySource; omega) (by unfold copyDestination; omega) + · apply (ht.frame.mono ?_).trans + · have frame := hu.frame + rw [ht.regs .rsp (by decide)] at frame + exact frame.mono (fun _ h => List.mem_cons_of_mem _ h) + · intro region hr + simp only [List.mem_singleton] at hr; subst region + exact List.mem_cons_self .. + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCorrect.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCorrect.lean new file mode 100644 index 000000000..e0106211c --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveCorrect.lean @@ -0,0 +1,29 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveReturn +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveMxcsr + +/-! Functional correctness, termination, memory safety, and the complete System V ABI. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem code_correct (v : Proof.Blake2.X86_64.Backend) (name : String) (s : State) + (pre : (Spec.Argon2.deriveContract X86_64.abi 344).pre s) : + ∃ tr t, Exec isa (Impl.Argon2.X86_64.Derive.code name (HPrime.hash v)) s tr t ∧ + abiPreserved s t ∧ (Spec.Argon2.deriveContract X86_64.abi 344).post s t := by + obtain ⟨tr, t, run, post, regs, frame⟩ := code_wp v name s pre + refine ⟨tr, t, run, abiPreserved_of_exec (code_mxcsr v name) run ⟨regs, ?_⟩, post⟩ + have h := abi_environment s pre + apply frame.readW (r := ⟨s.gpr .rsp, 8⟩) (Region.contains_self _ _) ?_ (by decide) + intro r hr + simp only [wholeWrites, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl + · exact h.reserved _ (by simp) (abiMatrix s, true) + (List.mem_append_left _ (List.mem_append_right _ (by simp))) + · exact h.reserved _ (by simp) (abiWork s, true) + (List.mem_append_left _ (List.mem_append_right _ (by simp))) + · exact h.reserved _ (by simp) (abiOutput s, true) + (List.mem_append_left _ (List.mem_append_right _ (by simp))) + · exact Offset.base_disjoint_below _ (by decide) + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFillLayout.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFillLayout.lean new file mode 100644 index 000000000..4fad62aad --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFillLayout.lean @@ -0,0 +1,62 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveMemorySpace + +/-! One reviewed allocation supplies all filling and address-generation ranges. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem private_fill_layout {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : FillKernel.Layout (abiParams s) t := by + have space := private_memory_space h prepared + rw [private_matrix_bytes h] at space + refine ⟨?_, private_local_write prepared 16 8 (by decide), space.matrix, + private_work_cover h prepared 5120 (by decide), ?_, space.frameMatrix.symm, ?_, ?_, + private_frame_stack prepared 8 (by decide), ?_⟩ + · intro d hd + have bounds : ∀ d ∈ [0, 16, 184, 232, 248], d + 8 ≤ 272 := by decide + exact private_local_read prepared d 8 (bounds d hd) + · have pointer : t.gpr .rbx = FillKernel.work t := by + rw [private_scratch h prepared]; exact (private_words h prepared).work.symm + rw [← pointer] + exact space.matrixWork.sub_right (Region.sub_prefix (by decide)) + · exact (space.stackMatrix.sub_left (below_sub (by decide) (by decide))).symm + · have pointer : t.gpr .rbx = FillKernel.work t := by + rw [private_scratch h prepared]; exact (private_words h prepared).work.symm + rw [← pointer] + exact space.frameWork.sub_right (Region.sub_prefix (by decide)) + · have pointer : t.gpr .rbx = FillKernel.work t := by + rw [private_scratch h prepared]; exact (private_words h prepared).work.symm + rw [← pointer] + exact (space.stackWork.sub_left (below_sub (by decide) (by decide))).sub_right + (Region.sub_prefix (by decide)) + +theorem private_address_layout {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : AddressCalls.Ready t := by + have space := private_memory_space h prepared + have pointer : t.gpr .rbx = AddressCalls.work t := by + rw [private_scratch h prepared]; exact (private_words h prepared).work.symm + refine ⟨private_local_read prepared 248 8 (by decide), private_work_cover h prepared 8192 (by decide), + ?_, private_frame_stack prepared 8 (by decide), ?_⟩ + · rw [← pointer]; exact space.frameWork.sub_right (Region.sub_prefix (by decide)) + · rw [← pointer] + exact (space.stackWork.sub_left (below_sub (by decide) (by decide))).sub_right (Region.sub_prefix (by decide)) + +theorem private_fill_environment {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : FillSetup.Environment (abiParams s) t := by + have space := private_memory_space h prepared + have words := private_words h prepared + have pointer : t.gpr .rbx = AddressCalls.work t := by + rw [private_scratch h prepared]; exact words.work.symm + rw [private_matrix_bytes h] at space + refine ⟨?_, h.valid.2.2.2.1, private_fill_layout h prepared, private_address_layout h prepared, ?_, + private_local_write prepared 8 8 (by decide), private_local_write prepared 0 8 (by decide), + ?_, words.blocks, words.passes, words.kind, words.lanes⟩ + · refine ⟨h.valid.1, Nat.lt_trans h.valid.2.1 (by decide), h.valid.2.2.2.2.1, + h.valid.2.2.2.2.2.1, by decide, h.valid.1, by decide⟩ + · intro d hd + have bounds : ∀ d ∈ [0, 8, 72, 112, 240], d + 8 ≤ 272 := by decide + exact private_local_read prepared d 8 (bounds d hd) + · rw [← pointer]; exact space.matrixWork.sub_right (Region.sub_prefix (by decide)) + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFinalLayout.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFinalLayout.lean new file mode 100644 index 000000000..e5164b76c --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFinalLayout.lean @@ -0,0 +1,56 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveFillLayout + +/-! Final lane reduction and H′ use the matrix and disjoint output/scratch allocations. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem private_final_layout {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : FinalOutput.Ready (abiParams s) t := by + have words := private_words h prepared + have separation := abi_separation h + have environment := private_fill_environment h prepared + have parameters := environment.parameters + have blocks := Proof.Argon2.lastIndex_bounds (abiParams s) parameters.lanesPositive + parameters.segment_bound.1 0 parameters.lanesPositive + have minimum : 1024 ≤ (abiParams s).blocks * 1024 := by + have positive : 1 ≤ (abiParams s).blocks := by omega + simpa only [Nat.one_mul] using Nat.mul_le_mul_right 1024 positive + have matrix := private_matrix_region h prepared + have work : FinalOutput.work t = (abiWork s).base := words.work + have output : (⟨FinalOutput.output t, (abiParams s).tagLen⟩ : Region) = abiOutput s := by + change (⟨Initial.wordAt t 256, (abiParams s).tagLen⟩ : Region) = abiOutput s + rw [words.output]; rfl + have matrixMember : (abiMatrix s, true) ∈ abiBuffers s ++ [(abiArguments s, false)] := by + simp only [abiBuffers, List.mem_append, List.mem_cons, true_or, or_true] + have workMember : (abiWork s, true) ∈ abiBuffers s ++ [(abiArguments s, false)] := by + simp only [abiBuffers, List.mem_append, List.mem_cons, true_or, or_true] + have outputMember : (abiOutput s, true) ∈ abiBuffers s ++ [(abiArguments s, false)] := by + simp only [abiBuffers, List.mem_append, List.mem_cons, true_or, or_true] + have matrixWork : (⟨ReductionState.matrix t, 1024⟩ : Region).Disjoint ⟨FinalOutput.work t, 16384⟩ := by + rw [work] + apply Region.Disjoint.sub_left separation.matrixWork + rw [← matrix] + exact Region.sub_prefix minimum + have stackMatrix : (below (t.gpr .rsp) 24).Disjoint ⟨ReductionState.matrix t, 1024⟩ := by + apply Region.Disjoint.sub_right + (private_stack_disjoint h prepared (abiMatrix s, true) matrixMember 24 (by decide)) + rw [← matrix]; exact Region.sub_prefix minimum + refine ⟨by have tag := h.valid.2.2.2.2.2.2.1; omega, h.valid.2.2.2.2.2.2.2.1, + ?_, words.tagLength, ?_, private_output_cover h prepared, ?_, matrixWork, ?_, stackMatrix, ?_, ?_⟩ + · intro d hd + have bounds : ∀ d ∈ [232, 256, 264, 248], d + 8 ≤ 272 := by decide + exact private_local_read prepared d 8 (bounds d hd) + · intro p n ⟨region, member, contains⟩ + simp only [List.mem_singleton] at member; subst region + have contained : Region.Contains ⟨FillKernel.matrix t, (abiParams s).blocks * 1024⟩ p n := by + unfold Region.Contains at contains ⊢; exact Nat.le_trans contains minimum + obtain ⟨r, hr, hc⟩ := private_matrix_cover h prepared p n ⟨_, List.mem_singleton_self _, contained⟩ + exact ⟨r, List.mem_append_right _ hr, hc⟩ + · rw [work]; exact private_work_member h prepared + · rw [output, work]; exact separation.outputWork + · rw [output]; exact private_stack_disjoint h prepared (abiOutput s, true) outputMember 24 (by decide) + · rw [work]; exact private_stack_disjoint h prepared (abiWork s, true) workMember 24 (by decide) + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrame.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrame.lean index 206a3096f..07acd20b3 100644 --- a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrame.lean +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrame.lean @@ -1,18 +1,18 @@ import VerifiedGarbage.Impl.Argon2.X86_64.Derive import VerifiedGarbage.Proof.Framework.X86_64.Frame -/-! Compose the nested saved-register frames and the 120-byte local allocation. -/ +/-! Compose the nested saved-register frames and the 272-byte local allocation. -/ namespace VG.Proof.Argon2.X86_64.Derive open VG VG.X86_64 def frameStart (s : State) : List Reg → State - | [] => pushed (List.replicate 15 .rax) s + | [] => pushed (List.replicate 34 .rax) s | r :: rs => frameStart (pushed [r] s) rs def frameEnd (s : State) : List Reg → State - | [] => popped .rax 15 s + | [] => popped .rax 34 s | r :: rs => popped r 1 (frameEnd s rs) theorem frameEnd_metadata (s t : State) (rs : List Reg) @@ -33,7 +33,7 @@ theorem frameEnd_metadata (s t : State) (rs : List Reg) · rw [frameEnd, popped_wr, innerWr, pushed_wr]; rfl theorem frame_ok (s : State) (rs : List Reg) (body : Prog isa) (Q : State → Prop) - (notSp : .rsp ∉ rs) (space : 120 + 8 * rs.length ≤ (s.gpr .rsp).toNat) + (notSp : .rsp ∉ rs) (space : 272 + 8 * rs.length ≤ (s.gpr .rsp).toNat) (run : WP isa body (frameStart s rs) fun t => t.gpr .rsp = (frameStart s rs).gpr .rsp ∧ t.wr = (frameStart s rs).wr ∧ Q (frameEnd t rs)) : WP isa (Impl.Argon2.X86_64.Derive.frame body rs) s Q := by @@ -46,7 +46,7 @@ theorem frame_ok (s : State) (rs : List Reg) (body : Prog isa) (Q : State → Pr rw [pushed_rsp] simp only [List.length_singleton, Nat.mul_one] exact toNat_sub_ofNat (by simp only [List.length_cons] at space; omega) - have innerSpace : 120 + 8 * rs.length ≤ ((pushed [r] s).gpr .rsp).toNat := by + have innerSpace : 272 + 8 * rs.length ≤ ((pushed [r] s).gpr .rsp).toNat := by rw [pushedBound]; simp only [List.length_cons] at space; omega apply WP.frame (by simp) (by simpa using notSp.1) (Ne.symm notSp.1) (by simp only [List.length_cons] at space; simp only [List.length_singleton, Nat.mul_one]; omega) diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrameState.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrameState.lean index 1f73720aa..5785e5556 100644 --- a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrameState.lean +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveFrameState.lean @@ -8,7 +8,7 @@ namespace VG.Proof.Argon2.X86_64.Derive open VG VG.X86_64 theorem frameStart_sp (s : State) (rs : List Reg) : - (frameStart s rs).gpr .rsp = s.gpr .rsp - BitVec.ofNat 64 (120 + 8 * rs.length) := by + (frameStart s rs).gpr .rsp = s.gpr .rsp - BitVec.ofNat 64 (272 + 8 * rs.length) := by induction rs generalizing s with | nil => rw [frameStart, pushed_rsp]; rfl | cons r rs ih => @@ -29,15 +29,15 @@ theorem frameStart_rd (s : State) (rs : List Reg) : (frameStart s rs).rd = s.rd | cons r rs ih => exact (ih (pushed [r] s)).trans (pushed_rd ..) theorem frameStart_frame (s : State) (rs : List Reg) (notSp : .rsp ∉ rs) - (space : 120 + 8 * rs.length ≤ (s.gpr .rsp).toNat) : - Frame [below (s.gpr .rsp) (120 + 8 * rs.length)] s.mem (frameStart s rs).mem := by + (space : 272 + 8 * rs.length ≤ (s.gpr .rsp).toNat) : + Frame [below (s.gpr .rsp) (272 + 8 * rs.length)] s.mem (frameStart s rs).mem := by induction rs generalizing s with | nil => - exact (pushRegs_mem s (List.replicate 15 .rax) (by decide) (by simpa using space)).1 + exact (pushRegs_mem s (List.replicate 34 .rax) (by decide) (by simpa using space)).1 | cons r rs ih => simp only [List.mem_cons, not_or] at notSp have enough : 8 ≤ (s.gpr .rsp).toNat := by simp only [List.length_cons] at space; omega - have innerSpace : 120 + 8 * rs.length ≤ ((pushed [r] s).gpr .rsp).toNat := by + have innerSpace : 272 + 8 * rs.length ≤ ((pushed [r] s).gpr .rsp).toNat := by rw [pushed_rsp] simp only [List.length_singleton, Nat.mul_one] rw [toNat_sub_ofNat enough] @@ -57,7 +57,7 @@ theorem frameStart_frame (s : State) (rs : List Reg) (notSp : .rsp ∉ rs) rw [pushed_rsp] simp only [List.length_cons, List.length_nil, Nat.zero_add, Nat.mul_one, below] rw [BitVec.sub_sub, ← BitVec.ofNat_add, - show 8 + (120 + 8 * rs.length) = 120 + 8 * (rs.length + 1) by omega] + show 8 + (272 + 8 * rs.length) = 272 + 8 * (rs.length + 1) by omega] exact Region.sub_prefix (by omega) theorem frameEnd_mem (s : State) (rs : List Reg) : (frameEnd s rs).mem = s.mem := by diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveHashInputs.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveHashInputs.lean new file mode 100644 index 000000000..e4f246b1d --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveHashInputs.lean @@ -0,0 +1,53 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveHashSpace + +/-! All four secret inputs keep their original pointers and lengths in the private frame. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 +open VG.Proof.Argon2.X86_64.Initial (wordAt inputRegion) + +theorem private_input_member {s t : State} (words : DeriveWords s t) (input : Nat × Nat) + (member : input ∈ Initial.inputs) : inputRegion t input.1 input.2 ∈ abiInputs s := by + simp only [Initial.inputs, List.mem_cons, List.not_mem_nil, or_false] at member + rcases member with rfl | rfl | rfl | rfl + · change (⟨wordAt t 104, (wordAt t 96).toNat⟩ : Region) ∈ abiInputs s + rw [words.password, words.passwordLength] + exact List.mem_cons_self .. + · change (⟨wordAt t 88, (wordAt t 80).toNat⟩ : Region) ∈ abiInputs s + rw [words.salt, words.saltLength] + exact List.mem_cons_of_mem _ (List.mem_cons_self ..) + · change (⟨wordAt t 200, (wordAt t 208).toNat⟩ : Region) ∈ abiInputs s + rw [words.secret, words.secretLength] + exact List.mem_cons_of_mem _ (List.mem_cons_of_mem _ (List.mem_cons_self ..)) + · change (⟨wordAt t 216, (wordAt t 224).toNat⟩ : Region) ∈ abiInputs s + rw [words.ad, words.adLength] + exact List.mem_cons_of_mem _ (List.mem_cons_of_mem _ (List.mem_cons_of_mem _ (List.mem_singleton_self _))) + +theorem private_hash_inputs {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : + ∀ input ∈ Initial.inputs, Initial.InputReady t input.1 input.2 := by + have words := private_words h prepared + have space := private_hash_space h prepared + have separation := abi_separation h + have scratch := private_scratch h prepared + intro input hi + have region := private_input_member words input hi + have facts : ∀ input ∈ Initial.inputs, input.1 ∈ Initial.slots ∧ input.2 ∈ Initial.slots ∧ + input.1 + 8 ≤ 272 ∧ input.2 + 8 ≤ 272 := by decide + obtain ⟨pointerSlot, lengthSlot, pointerBound, lengthBound⟩ := facts input hi + have buffer : (inputRegion t input.1 input.2, false) ∈ abiBuffers s ++ [(abiArguments s, false)] := + List.mem_append_left _ (List.mem_append_left _ (List.mem_map.mpr ⟨_, region, rfl⟩)) + refine ⟨space, pointerSlot, lengthSlot, pointerBound, lengthBound, + abi_input_lengths h _ region, ?_, ?_, ?_⟩ + · intro p n ⟨r, hr, hc⟩ + simp only [List.mem_singleton] at hr; subst r + refine ⟨_, List.mem_append_left _ ?_, hc⟩ + rw [prepared.rd] + change inputRegion t input.1 input.2 ∈ (frameStart s Impl.Argon2.X86_64.Derive.saved).rd + rw [frameStart_rd, h.rd] + exact List.mem_append_left _ region + · rw [scratch]; exact separation.inputWork _ region + · exact (private_stack_disjoint h prepared (inputRegion t input.1 input.2, false) buffer 16 (by decide)).symm + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveHashSpace.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveHashSpace.lean new file mode 100644 index 000000000..ac7904bcb --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveHashSpace.lean @@ -0,0 +1,24 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveWords +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveSeparation + +/-! Permissions for H₀ follow from the signature and the private ABI frame. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem private_hash_space {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : Initial.Space t := by + have scratch := private_scratch h prepared + have member : (abiWork s, true) ∈ abiBuffers s ++ [(abiArguments s, false)] := by + simp only [abiBuffers, List.mem_append, List.mem_cons, true_or, or_true] + refine ⟨?_, ?_, ?_, private_frame_stack prepared 16 (by decide), ?_, + by simpa only [BitVec.add_zero] using private_local_write prepared 0 64 (by decide)⟩ + · rw [scratch]; exact private_work_member h prepared + · rw [scratch]; exact private_stack_disjoint h prepared (abiWork s, true) member 16 (by decide) + · rw [scratch]; exact private_frame_disjoint h prepared (abiWork s, true) member + · intro d hd + have bounds : ∀ d ∈ Initial.slots, d + 8 ≤ 272 := by decide + exact private_local_read prepared d 8 (bounds d hd) + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveHeader.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveHeader.lean new file mode 100644 index 000000000..4765d0b39 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveHeader.lean @@ -0,0 +1,25 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveWords + +/-! H₀ hashes the original requested memory cost and the exact reviewed parameters. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem private_header {s t : State} (words : DeriveWords s t) : + Initial.headerBytes t = Proof.Argon2.initialHeader (abiParams s) := by + have headerWords : (List.range 6).map (Initial.headerValue t) = + [BitVec.ofNat 32 (abiParams s).lanes, BitVec.ofNat 32 (abiParams s).tagLen, + BitVec.ofNat 32 (abiParams s).memory, BitVec.ofNat 32 (abiParams s).passes, + 19#32, BitVec.ofNat 32 (abiParams s).variant.code] := by + change [(Initial.wordAt t 184).setWidth 32, (Initial.wordAt t 264).setWidth 32, + (Initial.wordAt t 176).setWidth 32, (Initial.wordAt t 72).setWidth 32, 19#32, + (Initial.wordAt t 112).setWidth 32] = _ + rw [words.lanes, words.tagLength, words.memory, words.passes, words.kind] + simp only [BitVec.setWidth_ofNat_of_le (show 32 ≤ 64 by decide)] + unfold Initial.headerBytes + rw [← List.flatMap_map, headerWords] + simp only [List.flatMap_cons, List.flatMap_nil, List.append_nil, ← List.append_assoc, + Proof.Argon2.initialHeader, Spec.Argon2.le32] + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveInputBytes.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveInputBytes.lean new file mode 100644 index 000000000..f2b66e6e7 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveInputBytes.lean @@ -0,0 +1,50 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveBodyReady + +/-! Saving registers and copying arguments leave all original input bytes unchanged. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem private_prepare_frame {s t : State} (prepared : PrivatePrepared (prologueState s) t) : + Frame [⟨(prologueState s).gpr .rsp, 272⟩] (prologueState s).mem t.mem := by + apply prepared.frame.sub + intro region hr + simp only [privateWrites, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl + · exact ⟨_, List.mem_singleton_self _, Region.sub_prefix (by decide)⟩ + · exact ⟨_, List.mem_singleton_self _, Offset.sub_base _ (by decide)⟩ + +theorem private_prologue_frame {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : Frame [below (s.gpr .rsp) 320] s.mem t.mem := by + have prologue := frameStart_frame s Impl.Argon2.X86_64.Derive.saved (by decide) (by + have space := h.stack; change 320 ≤ (s.gpr .rsp).toNat; omega) + apply prologue.trans + have preparation := private_prepare_frame prepared + rw [prologue_sp] at preparation + exact preparation.sub (by + intro region hr; simp only [List.mem_singleton] at hr; subst region + exact ⟨_, List.mem_singleton_self _, Region.sub_prefix (by decide)⟩) + +theorem private_input_bytes {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) (input : Nat × Nat) (hi : input ∈ Initial.inputs) : + Initial.inputBytes t input.1 input.2 = + Spec.Blake2.bytesAt s.mem (Initial.inputRegion t input.1 input.2).base + (Initial.inputRegion t input.1 input.2).len := by + have words := private_words h prepared + have region := private_input_member words input hi + have buffer : (Initial.inputRegion t input.1 input.2, false) ∈ abiBuffers s ++ [(abiArguments s, false)] := + List.mem_append_left _ (List.mem_append_left _ (List.mem_map.mpr ⟨_, region, rfl⟩)) + have disjoint := h.reserved (below (s.gpr .rsp) 344) + (List.mem_cons_of_mem _ (List.mem_singleton_self _)) + (Initial.inputRegion t input.1 input.2, false) buffer + have length := abi_input_lengths h _ region + apply Proof.Blake2.bytesAt_congr + intro i hi' + apply (private_prologue_frame h prepared).bytes (R := Initial.inputRegion t input.1 input.2) + _ (by omega) hi' + intro r hr + simp only [List.mem_singleton] at hr; subst r + exact (disjoint.sub_left (below_sub (by decide) (by decide))).symm + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveLit.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveLit.lean new file mode 100644 index 000000000..2bbd1d8ca --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveLit.lean @@ -0,0 +1,19 @@ +import VerifiedGarbage.Impl.Argon2.X86_64.Derive +import VerifiedGarbage.Proof.Argon2.X86_64.FillCompressLit +import VerifiedGarbage.Proof.Argon2.X86_64.ReferenceMapLit +import VerifiedGarbage.Proof.Argon2.X86_64.FillPointersLit +import VerifiedGarbage.Proof.Argon2.X86_64.AddressHeaderLit +import VerifiedGarbage.Proof.Argon2.X86_64.ClearBlockLit +import VerifiedGarbage.Proof.Argon2.X86_64.ReduceBlockLit +import VerifiedGarbage.Proof.Framework.Lit + +/-! Checked literals for the entry point's fixed instruction shapes. -/ + +namespace VG + +materialize_code Impl.Argon2.X86_64.Derive.prepare +materialize_code Impl.Argon2.X86_64.FillSetup.code +materialize_code Impl.Argon2.X86_64.FillIterations.loop +materialize_code Impl.Argon2.X86_64.FinalReduction.code + +end VG diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveMemorySpace.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveMemorySpace.lean new file mode 100644 index 000000000..89fca22bf --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveMemorySpace.lean @@ -0,0 +1,37 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveAllocations + +/-! The signature's rounded block allocation supplies all initialization permissions. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem private_matrix_bytes {s : State} (h : AbiEnvironment s) : + 1024 * ((abiParams s).lanes * (abiParams s).laneLen) = (abiParams s).blocks * 1024 := by + rw [← Proof.Argon2.blocks_lanes (abiParams s) h.valid.1, Nat.mul_comm] + +theorem private_memory_space {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : + MemoryInit.Space t (FillKernel.matrix t) + (1024 * ((abiParams s).lanes * (abiParams s).laneLen)) := by + have matrix := private_matrix_region h prepared + have separation := abi_separation h + have scratch := private_scratch h prepared + have matrixMember : (abiMatrix s, true) ∈ abiBuffers s ++ [(abiArguments s, false)] := by + simp only [abiBuffers, List.mem_append, List.mem_cons, true_or, or_true] + have workMember : (abiWork s, true) ∈ abiBuffers s ++ [(abiArguments s, false)] := by + simp only [abiBuffers, List.mem_append, List.mem_cons, true_or, or_true] + rw [private_matrix_bytes h] + refine ⟨private_matrix_cover h prepared, private_local_cover prepared 72 (by decide), ?_, + ?_, ?_, ?_, (private_frame_stack prepared 24 (by decide)).symm, ?_, ?_, ?_⟩ + · rw [scratch]; exact private_work_member h prepared + · rw [matrix]; exact private_frame_disjoint h prepared (abiMatrix s, true) matrixMember + · rw [scratch]; exact private_frame_disjoint h prepared (abiWork s, true) workMember + · rw [matrix, scratch]; exact separation.matrixWork + · rw [matrix]; exact private_stack_disjoint h prepared (abiMatrix s, true) matrixMember 24 (by decide) + · rw [scratch]; exact private_stack_disjoint h prepared (abiWork s, true) workMember 24 (by decide) + · have blocks := Proof.Argon2.blocks_le_memory (abiParams s) + have memoryBound := h.valid.2.2.2.2.2.1 + omega + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveMetadata.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveMetadata.lean new file mode 100644 index 000000000..09901146f --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveMetadata.lean @@ -0,0 +1,55 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DerivePrologue +import VerifiedGarbage.Proof.Argon2.X86_64.Parameters + +/-! The private frame contains the exact arguments decoded by the shared contract. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem private_stack_word {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) (j : Nat) (hj : j < 12) : + t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 (copyDestination j)) 64 = + let word := abiWord s (8 * (j + 1)) + if j < 3 then (word.setWidth 32).setWidth 64 else word := by + rw [prepared.stackWords j hj, prologue_word h j hj] + +theorem private_argument_word {s t : State} (prepared : PrivatePrepared (prologueState s) t) + (arg : Nat × Reg) (member : arg ∈ arguments) : + t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 arg.1) 64 = argumentValue s arg.2 := by + rw [prepared.values arg member] + unfold argumentValue + have notSp : ∀ arg ∈ arguments, arg.2 ≠ .rsp := by decide + unfold prologueState + rw [frameStart_reg s _ arg.2 (notSp arg member)] + +theorem private_local_write {s t : State} (prepared : PrivatePrepared (prologueState s) t) + (d n : Nat) (bound : d + n ≤ 272) : InRegions t.wr (t.gpr .rbp + BitVec.ofNat 64 d) n := by + rw [prepared.wr, prepared.bp] + apply prologue_locals s + exact ⟨_, List.mem_singleton_self _, Offset.contains_base _ bound (by omega)⟩ + +theorem private_local_read {s t : State} (prepared : PrivatePrepared (prologueState s) t) + (d n : Nat) (bound : d + n ≤ 272) : + InRegions (t.rd ++ t.wr) (t.gpr .rbp + BitVec.ofNat 64 d) n := by + obtain ⟨r, hr, hc⟩ := private_local_write prepared d n bound + exact ⟨r, List.mem_append_right _ hr, hc⟩ + +theorem private_parameters {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : Parameters.Ready (abiParams s) t := by + refine ⟨private_local_read prepared 176 8 (by decide), private_local_read prepared 184 8 (by decide), + ?_, ?_, h.valid.1, h.valid.2.2.2.2.2.1, h.valid.2.1⟩ + · have word := private_stack_word h prepared 0 (by decide) + change t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 176) 64 = + (((abiWord s 8).setWidth 32).setWidth 64) at word + change t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 176) 64 = + BitVec.ofNat 64 ((abiWord s 8).setWidth 32).toNat + rw [word, BitVec.ofNat_toNat] + · have word := private_stack_word h prepared 1 (by decide) + change t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 184) 64 = + (((abiWord s 16).setWidth 32).setWidth 64) at word + change t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 184) 64 = + BitVec.ofNat 64 ((abiWord s 16).setWidth 32).toNat + rw [word, BitVec.ofNat_toNat] + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveMxcsr.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveMxcsr.lean new file mode 100644 index 000000000..dac0de588 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveMxcsr.lean @@ -0,0 +1,52 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveLit +import VerifiedGarbage.Proof.Argon2.X86_64.HPrime.Correct +import VerifiedGarbage.Proof.Argon2.X86_64.InitialLit +import VerifiedGarbage.Proof.Argon2.X86_64.MemoryInitLit +import VerifiedGarbage.Proof.Argon2.X86_64.ParametersLit + +/-! The complete derivation preserves MXCSR for every BLAKE2b backend. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +local notation "property" => (fun i => !loadsMxcsr i) + +theorem initial_mxcsr (v : Proof.Blake2.X86_64.Backend) : + (Impl.Argon2.X86_64.Initial.code (HPrime.hash v)).allInstrs property = true := by + have init : (HPrime.hash v).init.allInstrs property = true := by + change (Impl.Blake2.X86_64.Stream.init Spec.Blake2.b).allInstrs _ = true + lit_decide + have update : (HPrime.hash v).update.allInstrs property = true := v.updateMxcsr + have finalize : (HPrime.hash v).finalize.allInstrs property = true := v.finalizeMxcsr + simp only [Impl.Argon2.X86_64.Initial.code, Impl.Argon2.X86_64.Initial.start, + Impl.Argon2.X86_64.Initial.absorb, Impl.Argon2.X86_64.Initial.finish, + Impl.Argon2.X86_64.HPrime.init, Impl.Argon2.X86_64.HPrime.absorbFixed, + Impl.Argon2.X86_64.HPrime.update, Impl.Argon2.X86_64.HPrime.finalize, Code.allInstrs] + rw [init, update, finalize] + lit_decide + +theorem memory_mxcsr (v : Proof.Blake2.X86_64.Backend) (name : String) : + (Impl.Argon2.X86_64.MemoryInit.code name (HPrime.hash v)).allInstrs property = true := by + simp only [Impl.Argon2.X86_64.MemoryInit.code, Impl.Argon2.X86_64.MemoryInit.clear, + Impl.Argon2.X86_64.MemoryInit.lane, Impl.Argon2.X86_64.MemoryInit.block, Code.allInstrs] + rw [HPrime.code_mxcsr v] + lit_decide + +theorem frame_mxcsr (body : Prog isa) (rs : List Reg) (h : body.allInstrs property = true) : + (Impl.Argon2.X86_64.Derive.frame body rs).allInstrs property = true := by + induction rs with + | nil => simpa [Impl.Argon2.X86_64.Derive.frame, Code.allInstrs, loadsMxcsr] using h + | cons r rs ih => simpa [Impl.Argon2.X86_64.Derive.frame, Code.allInstrs, loadsMxcsr] using ih + +theorem code_mxcsr (v : Proof.Blake2.X86_64.Backend) (name : String) : + (Impl.Argon2.X86_64.Derive.code name (HPrime.hash v)).allInstrs property = true := by + unfold Impl.Argon2.X86_64.Derive.code + apply frame_mxcsr + simp only [Impl.Argon2.X86_64.Derive.body, Impl.Argon2.X86_64.InitialBody.code, + Impl.Argon2.X86_64.InitFill.code, Impl.Argon2.X86_64.FillFinish.code, + Impl.Argon2.X86_64.Finish.code, Impl.Argon2.X86_64.FinalOutput.code, Code.allInstrs] + rw [initial_mxcsr v, memory_mxcsr v name, HPrime.code_mxcsr v] + lit_decide + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrepare.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrepare.lean index e715d3e20..98e27ec37 100644 --- a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrepare.lean +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrepare.lean @@ -35,11 +35,26 @@ structure Prepared (s t : State) : Prop where mxcsr : t.mxcsr = s.mxcsr frame : Frame (prepareWrites s) s.mem t.mem -theorem prepare_ok (s : State) (frameWrite : Covers [⟨s.gpr .rsp, 120⟩] s.wr) +theorem Prepared.other_word {s t : State} (h : Prepared s t) (d : Nat) + (afterFrame : 120 ≤ d) (bound : d + 8 < 2 ^ 64) + (separate : ∀ e ∈ normalizedOffsets, d + 8 ≤ e ∨ e + 8 ≤ d) : + t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 d) 64 = + s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 d) 64 := by + rw [h.bp] + apply h.frame.readW (r := ⟨s.gpr .rsp + BitVec.ofNat 64 d, 8⟩) (Region.contains_self _ _) ?_ (by decide) + intro region hr + rcases List.mem_cons.mp hr with rfl | hr + · simpa only [BitVec.add_zero] using Offset.disjoint (s.gpr .rsp) (d := d) (n := 8) (e := 0) (k := 120) + (Or.inr afterFrame) (Nat.le_of_lt bound) (by decide) + · obtain ⟨e, he, rfl⟩ := List.mem_map.mp hr + have bounds : ∀ e ∈ normalizedOffsets, e + 8 ≤ 2 ^ 64 := by decide + exact Offset.disjoint _ (separate e he) (Nat.le_of_lt bound) (bounds e he) + +theorem prepareLocal_ok (s : State) (frameWrite : Covers [⟨s.gpr .rsp, 120⟩] s.wr) (read : ∀ d ∈ 248 :: normalizedOffsets, InRegions (s.rd ++ s.wr) (s.gpr .rsp + BitVec.ofNat 64 d) 8) (write : ∀ d ∈ normalizedOffsets, InRegions s.wr (s.gpr .rsp + BitVec.ofNat 64 d) 8) : - WP isa Impl.Argon2.X86_64.Derive.prepare s (Prepared s) := by - unfold Impl.Argon2.X86_64.Derive.prepare + WP isa Impl.Argon2.X86_64.Derive.prepareLocal s (Prepared s) := by + unfold Impl.Argon2.X86_64.Derive.prepareLocal have scratchRead : InRegions (s.rd ++ s.wr) (s.gpr .rsp + 248) 8 := read 248 (List.mem_cons_self ..) refine WP.seq ((setup_ok s frameWrite scratchRead).mono ?_) intro a setup diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrivatePrepare.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrivatePrepare.lean new file mode 100644 index 000000000..de6da64b9 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrivatePrepare.lean @@ -0,0 +1,98 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveCopyArgs +import VerifiedGarbage.Proof.Argon2.X86_64.DerivePrepare + +/-! Prepare private copies of every ABI argument, preserving caller-owned storage. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def privateWrites (s : State) : List Region := [⟨s.gpr .rsp, 120⟩, ⟨s.gpr .rsp + 176, 96⟩] + +structure PrivatePrepared (s t : State) : Prop where + bp : t.gpr .rbp = s.gpr .rsp + sp : t.gpr .rsp = s.gpr .rsp + scratch : t.gpr .rbx = s.mem.readW (s.gpr .rsp + 400) 64 + values : ∀ arg ∈ arguments, t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 arg.1) 64 = argumentValue s arg.2 + stackWords : ∀ j < 12, t.mem.readW (t.gpr .rbp + BitVec.ofNat 64 (copyDestination j)) 64 = + let w := s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 (copySource j)) 64 + if j < 3 then (w.setWidth 32).setWidth 64 else w + regs : ∀ r ∈ calleeSaved, r ≠ .rbp → r ≠ .rbx → t.gpr r = s.gpr r + rd : t.rd = s.rd + wr : t.wr = s.wr + frame : Frame (privateWrites s) s.mem t.mem + +theorem private_prepare_ok (s : State) (locals : Covers [⟨s.gpr .rsp, 272⟩] s.wr) + (read : ∀ j < 12, InRegions (s.rd ++ s.wr) (s.gpr .rsp + BitVec.ofNat 64 (copySource j)) 8) : + WP isa Impl.Argon2.X86_64.Derive.prepare s (PrivatePrepared s) := by + have localWord : ∀ d, d + 8 ≤ 272 → InRegions s.wr (s.gpr .rsp + BitVec.ofNat 64 d) 8 := by + intro d hd + exact locals _ _ ⟨_, List.mem_singleton_self _, Offset.contains_base _ hd (by omega)⟩ + unfold Impl.Argon2.X86_64.Derive.prepare Impl.Argon2.X86_64.Derive.copyArgs + refine WP.seq ((copyArgs_ok (List.range 12) s + (fun _ h => List.mem_range.mp h) List.nodup_range (fun j h => read j (List.mem_range.mp h)) + (fun j h => localWord _ (by have := List.mem_range.mp h; unfold copyDestination; omega))).mono ?_) + intro a copied + have sp := copied.regs .rsp (by decide) + refine (prepareLocal_ok a (by + intro p n h + rw [copied.wr] + rw [sp] at h + apply locals p n + exact Covers.of_sub (by + intro r hr; simp only [List.mem_singleton] at hr; subst r + exact ⟨_, List.mem_singleton_self _, 0, (BitVec.add_zero _).symm, show 0 + 120 ≤ 272 by decide⟩) p n h) + (by + intro d hd + rw [copied.rd, copied.wr, sp] + have bound : ∀ d ∈ 248 :: normalizedOffsets, d + 8 ≤ 272 := by decide + obtain ⟨region, member, contains⟩ := localWord d (bound d hd) + exact ⟨region, List.mem_append_right _ member, contains⟩) + (by + intro d hd; rw [copied.wr, sp] + have bound : ∀ d ∈ normalizedOffsets, d + 8 ≤ 272 := by decide + exact localWord d (bound d hd))).mono ?_ + intro t prepared + refine ⟨prepared.bp.trans sp, prepared.sp.trans sp, ?_, ?_, ?_, ?_, + prepared.rd.trans copied.rd, prepared.wr.trans copied.wr, ?_⟩ + · have word := copied.values 9 (by decide) + change a.mem.readW (a.gpr .rsp + 248) 64 = s.mem.readW (s.gpr .rsp + 400) 64 at word + exact prepared.scratch.trans word + · intro arg ha + rw [prepared.values arg ha] + unfold argumentValue + have notAx : ∀ arg ∈ arguments, arg.2 ≠ .rax := by decide + rw [copied.regs arg.2 (notAx arg ha)] + · intro j hj + by_cases small : j < 3 + · have slot : ∀ j < 3, copyDestination j ∈ normalizedOffsets := by decide + rw [prepared.normalized _ (slot j small), copied.values j (List.mem_range.mpr hj), ite_eq_left small] + · rw [ite_eq_right small, prepared.other_word _ (by unfold copyDestination; omega) + (by unfold copyDestination; omega) (by + intro d hd + have upper : ∀ d ∈ normalizedOffsets, d + 8 ≤ 200 := by decide + have := upper d hd; unfold copyDestination; omega)] + exact copied.values j (List.mem_range.mpr hj) + · intro r hr hb hx + have notAx : ∀ r ∈ calleeSaved, r ≠ .rax := by decide + exact (prepared.regs r hr hb hx).trans (copied.regs r (notAx r hr)) + · apply (copied.frame.sub ?_).trans (prepared.frame.sub ?_) + · intro region hr + obtain ⟨j, hj, rfl⟩ := List.mem_map.mp hr + have bound := List.mem_range.mp hj + refine ⟨⟨s.gpr .rsp + 176, 96⟩, List.mem_cons_of_mem _ (List.mem_singleton_self _), ?_⟩ + unfold copyDestination + rw [BitVec.ofNat_add, ← BitVec.add_assoc] + exact Offset.sub_base _ (by omega) + · intro region hr + rcases List.mem_cons.mp hr with rfl | hr + · rw [sp]; exact ⟨_, List.mem_cons_self .., fun _ h => h⟩ + · obtain ⟨d, hd, rfl⟩ := List.mem_map.mp hr + rw [sp] + have bounds : ∀ d ∈ normalizedOffsets, 176 ≤ d ∧ d + 8 ≤ 272 := by decide + obtain ⟨lo, hi⟩ := bounds d hd + refine ⟨⟨s.gpr .rsp + 176, 96⟩, List.mem_cons_of_mem _ (List.mem_singleton_self _), ?_⟩ + rw [show d = 176 + (d - 176) by omega, BitVec.ofNat_add, ← BitVec.add_assoc] + exact Offset.sub_base _ (by omega) + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrivatePublic.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrivatePublic.lean new file mode 100644 index 000000000..855ee3c71 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrivatePublic.lean @@ -0,0 +1,91 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DerivePublic +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveInputBytes +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveParametersCT + +/-! Private argument copies retain exactly the reviewed public relation. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 +open VG.Proof.Argon2.X86_64.Initial (wordAt) + +theorem DeriveWords.public_words {s₁ s₂ t₁ t₂ : State} (h : AbiPublic s₁ s₂) + (left : DeriveWords s₁ t₁) (right : DeriveWords s₂ t₂) : + ∀ d ∈ Initial.slots, wordAt t₁ d = wordAt t₂ d := by + intro d hd + simp only [Initial.slots, List.mem_cons, List.not_mem_nil, or_false] at hd + rcases hd with rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl + · rw [left.passes, right.passes, h.params] + · rw [left.saltLength, right.saltLength]; exact h.regs .r8 (by simp) + · rw [left.salt, right.salt]; exact h.regs .rcx (by simp) + · rw [left.passwordLength, right.passwordLength]; exact h.regs .rdx (by simp) + · rw [left.password, right.password]; exact h.regs .rsi (by simp) + · rw [left.kind, right.kind, h.params] + · rw [left.memory, right.memory, h.params] + · rw [left.lanes, right.lanes, h.params] + · rw [left.secret, right.secret]; exact h.words 32 (by simp) + · rw [left.secretLength, right.secretLength]; exact h.words 40 (by simp) + · rw [left.ad, right.ad]; exact h.words 48 (by simp) + · rw [left.adLength, right.adLength]; exact h.words 56 (by simp) + · rw [left.tagLength, right.tagLength, h.params] + +def abiReferences (s : State) : List Nat := Spec.Argon2.references (abiParams s) + (Spec.Blake2.bytesAt s.mem (s.gpr .rsi) (s.gpr .rdx).toNat) + (Spec.Blake2.bytesAt s.mem (s.gpr .rcx) (s.gpr .r8).toNat) + (Spec.Blake2.bytesAt s.mem (abiWord s 32) (abiWord s 40).toNat) + (Spec.Blake2.bytesAt s.mem (abiWord s 48) (abiWord s 56).toNat) + +theorem private_references {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : + InitialBody.references (abiParams s) t = abiReferences s := by + have words := private_words h prepared + have password := private_input_bytes h prepared (104, 96) (by decide) + have salt := private_input_bytes h prepared (88, 80) (by decide) + have secret := private_input_bytes h prepared (200, 208) (by decide) + have ad := private_input_bytes h prepared (216, 224) (by decide) + simp only [Initial.inputRegion] at password salt secret ad + rw [words.password, words.passwordLength] at password + rw [words.salt, words.saltLength] at salt + rw [words.secret, words.secretLength] at secret + rw [words.ad, words.adLength] at ad + unfold InitialBody.references abiReferences + change Spec.Argon2.references (abiParams s) (Initial.inputBytes t 104 96) + (Initial.inputBytes t 88 80) (Initial.inputBytes t 200 208) (Initial.inputBytes t 216 224) = _ + rw [password, salt, secret, ad] + +theorem private_parameters_related {s₁ s₂ t₁ t₂ : State} + (left : AbiEnvironment s₁) (right : AbiEnvironment s₂) (h : AbiPublic s₁ s₂) + (prepared₁ : PrivatePrepared (prologueState s₁) t₁) + (prepared₂ : PrivatePrepared (prologueState s₂) t₂) : + ParametersRelated (abiParams s₁) t₁ t₂ := by + have same := h.params + have ready₁ := private_body_ready left prepared₁ + have ready₂ := private_body_ready right prepared₂ + rw [← same] at ready₂ + have keeps₁ := dimension_frame t₁ (abiParams s₁) + have keeps₂ := dimension_frame t₂ (abiParams s₁) + have words₁ := (private_words left prepared₁).of_state keeps₁ + have words₂ := (private_words right prepared₂).of_state keeps₂ + have sp : t₁.gpr .rsp = t₂.gpr .rsp := by rw [prepared₁.sp, prepared₂.sp, prologue_sp, prologue_sp, h.sp] + have bp : t₁.gpr .rbp = t₂.gpr .rbp := by rw [prepared₁.bp, prepared₂.bp, prologue_sp, prologue_sp, h.sp] + have bx : t₁.gpr .rbx = t₂.gpr .rbx := by + rw [private_scratch left prepared₁, private_scratch right prepared₂] + exact h.words 80 (by simp) + refine ⟨private_parameters left prepared₁, same.symm ▸ private_parameters right prepared₂, ?_⟩ + refine ⟨ready₁, ready₂, ?_, ?_, ?_, ?_, ?_⟩ + · refine ⟨⟨ready₁.hashSpace, ready₁.inputs⟩, + ⟨ready₂.hashSpace, ready₂.inputs⟩, ?_, ?_, ?_, ?_⟩ + · rw [keeps₁.bp, keeps₂.bp]; exact bp + · rw [keeps₁.bx, keeps₂.bx]; exact bx + · rw [keeps₁.sp, keeps₂.sp]; exact sp + · exact words₁.public_words h words₂ + · exact words₁.matrix.trans ((h.words 64 (by simp)).trans words₂.matrix.symm) + · exact words₁.output.trans ((h.words 88 (by simp)).trans words₂.output.symm) + · exact words₁.work.trans ((h.words 80 (by simp)).trans words₂.work.symm) + · unfold InitialBody.references + simp only [keeps₁.inputBytes, keeps₂.inputBytes] + change InitialBody.references (abiParams s₁) t₁ = InitialBody.references (abiParams s₁) t₂ + rw [private_references left prepared₁, same, private_references right prepared₂] + exact h.references + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrologue.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrologue.lean new file mode 100644 index 000000000..d1052dd32 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePrologue.lean @@ -0,0 +1,66 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveAbi +import VerifiedGarbage.Proof.Argon2.X86_64.DerivePrivatePrepare +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveSaved + +/-! Private frame permissions and caller argument values after the ABI prologue. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def prologueState (s : State) : State := frameStart s Impl.Argon2.X86_64.Derive.saved + +theorem prologue_sp (s : State) : (prologueState s).gpr .rsp = s.gpr .rsp - BitVec.ofNat 64 320 := + frameStart_sp s _ + +theorem frameStart_locals (s : State) (rs : List Reg) : + (⟨(frameStart s rs).gpr .rsp, 272⟩ : Region) ∈ (frameStart s rs).wr := by + induction rs generalizing s with + | nil => rw [frameStart, pushed_wr, pushed_rsp]; exact List.mem_cons_self .. + | cons r rs ih => exact ih (pushed [r] s) + +theorem prologue_locals (s : State) : Covers [⟨(prologueState s).gpr .rsp, 272⟩] (prologueState s).wr := by + intro p n ⟨region, member, contains⟩ + simp only [List.mem_singleton] at member; subst region + exact ⟨_, frameStart_locals s _, contains⟩ + +theorem prologue_source (s : State) (j : Nat) : + (prologueState s).gpr .rsp + BitVec.ofNat 64 (copySource j) = + s.gpr .rsp + BitVec.ofNat 64 (8 * (j + 1)) := by + rw [prologue_sp] + unfold copySource + rw [show 328 + 8 * j = 320 + 8 * (j + 1) by omega, + BitVec.ofNat_add, ← BitVec.add_assoc, BitVec.sub_add_cancel] + +theorem prologue_reads {s : State} (h : AbiEnvironment s) : + ∀ j < 12, InRegions ((prologueState s).rd ++ (prologueState s).wr) + ((prologueState s).gpr .rsp + BitVec.ofNat 64 (copySource j)) 8 := by + intro j hj + rw [prologue_source] + refine ⟨abiArguments s, List.mem_append_left _ ?_, ?_⟩ + · change abiArguments s ∈ (frameStart s Impl.Argon2.X86_64.Derive.saved).rd + rw [frameStart_rd, h.rd]; exact List.mem_append_right _ (List.mem_singleton_self _) + · unfold abiArguments + rw [show 8 * (j + 1) = 8 + 8 * j by omega, BitVec.ofNat_add, ← BitVec.add_assoc] + exact Offset.contains_base _ (by omega) (by omega) + +theorem prologue_word {s : State} (h : AbiEnvironment s) (j : Nat) (hj : j < 12) : + (prologueState s).mem.readW ((prologueState s).gpr .rsp + BitVec.ofNat 64 (copySource j)) 64 = + abiWord s (8 * (j + 1)) := by + rw [prologue_source] + have frame := frameStart_frame s Impl.Argon2.X86_64.Derive.saved (by decide) (by + have space := h.stack; change 320 ≤ (s.gpr .rsp).toNat; omega) + apply frame.readW (r := abiArguments s) ?_ ?_ (by decide) + · unfold abiArguments + rw [show 8 * (j + 1) = 8 + 8 * j by omega, BitVec.ofNat_add, ← BitVec.add_assoc] + exact Offset.contains_base _ (by omega) (by omega) + · intro region hr + simp only [List.mem_singleton] at hr; subst region + unfold abiArguments + exact Offset.disjoint_below _ (by decide) + +theorem prologue_prepare (s : State) (h : AbiEnvironment s) : + WP isa Impl.Argon2.X86_64.Derive.prepare (prologueState s) (PrivatePrepared (prologueState s)) := + private_prepare_ok _ (prologue_locals s) (prologue_reads h) + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePublic.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePublic.lean new file mode 100644 index 000000000..7f309166c --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DerivePublic.lean @@ -0,0 +1,42 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveAbi + +/-! The public entry-point relation reads u32 arguments at their declared width. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +structure AbiPublic (s t : State) : Prop where + sp : s.gpr .rsp = t.gpr .rsp + regs : ∀ r ∈ [.rsi, .rdx, .rcx, .r8], s.gpr r = t.gpr r + smallRegs : ∀ r ∈ [.rdi, .r9], (s.gpr r).setWidth 32 = (t.gpr r).setWidth 32 + smallWords : ∀ d ∈ [8, 16, 24], (abiWord s d).setWidth 32 = (abiWord t d).setWidth 32 + words : ∀ d ∈ [32, 40, 48, 56, 64, 72, 80, 88, 96], abiWord s d = abiWord t d + references : Spec.Argon2.references (abiParams s) + (Spec.Blake2.bytesAt s.mem (s.gpr .rsi) (s.gpr .rdx).toNat) + (Spec.Blake2.bytesAt s.mem (s.gpr .rcx) (s.gpr .r8).toNat) + (Spec.Blake2.bytesAt s.mem (abiWord s 32) (abiWord s 40).toNat) + (Spec.Blake2.bytesAt s.mem (abiWord s 48) (abiWord s 56).toNat) = + Spec.Argon2.references (abiParams t) + (Spec.Blake2.bytesAt t.mem (t.gpr .rsi) (t.gpr .rdx).toNat) + (Spec.Blake2.bytesAt t.mem (t.gpr .rcx) (t.gpr .r8).toNat) + (Spec.Blake2.bytesAt t.mem (abiWord t 32) (abiWord t 40).toNat) + (Spec.Blake2.bytesAt t.mem (abiWord t 48) (abiWord t 56).toNat) + +theorem abi_public (s t : State) (h : (Spec.Argon2.deriveContract X86_64.abi 344).pub s t) : + AbiPublic s t := by + sig_pub [Spec.Argon2.deriveContract, Spec.Argon2.deriveSig, X86_64.abi, + X86_64.argRegs, X86_64.stackArg, X86_64.stackArgAddr, List.range, List.range.loop] at h + sig_split h + constructor + all_goals sig_eval [abiWord, abiParams] + all_goals sig_and_intros + all_goals sig_close + all_goals with_reducible assumption + +theorem AbiPublic.params {s t : State} (h : AbiPublic s t) : abiParams s = abiParams t := by + unfold abiParams + rw [h.smallRegs .rdi (by simp), h.smallRegs .r9 (by simp), + h.smallWords 8 (by simp), h.smallWords 16 (by simp), h.words 96 (by simp)] + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveRegions.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveRegions.lean new file mode 100644 index 000000000..ab36f46f2 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveRegions.lean @@ -0,0 +1,62 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveMetadata + +/-! The private frame and called functions stay within the reviewed stack allowance. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem frameStart_wr_member (s : State) (rs : List Reg) (region : Region) (member : region ∈ s.wr) : + region ∈ (frameStart s rs).wr := by + induction rs generalizing s with + | nil => rw [frameStart, pushed_wr]; exact List.mem_cons_of_mem _ member + | cons r rs ih => apply ih; rw [pushed_wr]; exact List.mem_cons_of_mem _ member + +theorem private_wr_member {s t : State} (prepared : PrivatePrepared (prologueState s) t) + (region : Region) (member : region ∈ s.wr) : region ∈ t.wr := by + rw [prepared.wr] + exact frameStart_wr_member s _ region member + +theorem private_bp {s t : State} (prepared : PrivatePrepared (prologueState s) t) : + t.gpr .rbp = s.gpr .rsp - BitVec.ofNat 64 320 := prepared.bp.trans (prologue_sp s) + +theorem private_sp {s t : State} (prepared : PrivatePrepared (prologueState s) t) : + t.gpr .rsp = s.gpr .rsp - BitVec.ofNat 64 320 := prepared.sp.trans (prologue_sp s) + +theorem private_frame_sub {s t : State} (prepared : PrivatePrepared (prologueState s) t) : + Region.Sub ⟨t.gpr .rbp, 272⟩ (below (s.gpr .rsp) 344) := by + rw [private_bp prepared] + exact Offset.sub_below _ (by decide) (by decide) + +theorem private_stack_sub {s t : State} (prepared : PrivatePrepared (prologueState s) t) + (n : Nat) (bound : n ≤ 24) : Region.Sub (below (t.gpr .rsp) n) (below (s.gpr .rsp) 344) := by + rw [private_sp prepared] + unfold below + rw [BitVec.sub_sub, ← BitVec.ofNat_add] + exact Offset.sub_below _ (by omega) (by omega) + +theorem private_frame_disjoint {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) (buffer : Region × Bool) + (member : buffer ∈ abiBuffers s ++ [(abiArguments s, false)]) : + (⟨t.gpr .rbp, 272⟩ : Region).Disjoint buffer.1 := + (h.reserved _ (List.mem_cons_of_mem _ (List.mem_singleton_self _)) buffer member).sub_left + (private_frame_sub prepared) + +theorem private_stack_disjoint {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) (buffer : Region × Bool) + (member : buffer ∈ abiBuffers s ++ [(abiArguments s, false)]) (n : Nat) (bound : n ≤ 24) : + (below (t.gpr .rsp) n).Disjoint buffer.1 := + (h.reserved _ (List.mem_cons_of_mem _ (List.mem_singleton_self _)) buffer member).sub_left + (private_stack_sub prepared n bound) + +theorem private_frame_stack {s t : State} (prepared : PrivatePrepared (prologueState s) t) + (n : Nat) (bound : n ≤ 24) : (⟨t.gpr .rbp, 272⟩ : Region).Disjoint (below (t.gpr .rsp) n) := by + rw [prepared.bp, prepared.sp] + exact Offset.base_disjoint_below _ (by omega) + +theorem private_work_member {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : abiWork s ∈ t.wr := by + apply private_wr_member prepared + rw [h.wr]; exact List.mem_cons_of_mem _ (List.mem_cons_self ..) + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveRestore.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveRestore.lean index 2cf651be0..3451fb86c 100644 --- a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveRestore.lean +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveRestore.lean @@ -8,7 +8,7 @@ namespace VG.Proof.Argon2.X86_64.Derive open VG VG.X86_64 theorem frameEnd_sp (s : State) (rs : List Reg) : - (frameEnd s rs).gpr .rsp = s.gpr .rsp + BitVec.ofNat 64 (120 + 8 * rs.length) := by + (frameEnd s rs).gpr .rsp = s.gpr .rsp + BitVec.ofNat 64 (272 + 8 * rs.length) := by induction rs with | nil => rw [frameEnd, popped_rsp]; rfl | cons r rs ih => @@ -25,7 +25,7 @@ theorem popped_one_reg (s : State) (r : Reg) (notSp : r ≠ .rsp) : theorem frameEnd_restore (s : State) (rs : List Reg) (values : Reg → Addr) (notSp : .rsp ∉ rs) (distinct : rs.Nodup) (words : ∀ j (hj : j < rs.length), - s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 (120 + 8 * rs.length - 8 * (j + 1))) 64 = values rs[j]) : + s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 (272 + 8 * rs.length - 8 * (j + 1))) 64 = values rs[j]) : ∀ r ∈ rs, (frameEnd s rs).gpr r = values r := by induction rs with | nil => intro r hr; exact False.elim (List.not_mem_nil hr) @@ -33,11 +33,11 @@ theorem frameEnd_restore (s : State) (rs : List Reg) (values : Reg → Addr) simp only [List.mem_cons, not_or] at notSp have nodup := List.nodup_cons.mp distinct have innerWords : ∀ j (hj : j < rs.length), - s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 (120 + 8 * rs.length - 8 * (j + 1))) 64 = values rs[j] := by + s.mem.readW (s.gpr .rsp + BitVec.ofNat 64 (272 + 8 * rs.length - 8 * (j + 1))) 64 = values rs[j] := by intro j hj have word := words (j + 1) (by simp only [List.length_cons]; omega) - have offset : 120 + 8 * (r :: rs).length - 8 * (j + 1 + 1) = - 120 + 8 * rs.length - 8 * (j + 1) := by + have offset : 272 + 8 * (r :: rs).length - 8 * (j + 1 + 1) = + 272 + 8 * rs.length - 8 * (j + 1) := by simp only [List.length_cons]; omega rw [offset] at word exact word @@ -47,7 +47,7 @@ theorem frameEnd_restore (s : State) (rs : List Reg) (values : Reg → Addr) rcases hx with rfl | hx · rw [frameEnd, popped_one_reg _ _ (Ne.symm notSp.1), frameEnd_mem, frameEnd_sp] have word := words 0 (by simp) - have offset : 120 + 8 * (x :: rs).length - 8 * (0 + 1) = 120 + 8 * rs.length := by + have offset : 272 + 8 * (x :: rs).length - 8 * (0 + 1) = 272 + 8 * rs.length := by simp only [List.length_cons]; omega rw [offset] at word exact word @@ -56,7 +56,7 @@ theorem frameEnd_restore (s : State) (rs : List Reg) (values : Reg → Addr) exact inner x hx theorem frame_restored (s t : State) (rs : List Reg) (notSp : .rsp ∉ rs) - (distinct : rs.Nodup) (space : 120 + 8 * rs.length ≤ (s.gpr .rsp).toNat) + (distinct : rs.Nodup) (space : 272 + 8 * rs.length ≤ (s.gpr .rsp).toNat) (sp : t.gpr .rsp = (frameStart s rs).gpr .rsp) (unchanged : ∀ j (_hj : j < rs.length), t.mem.readW (s.gpr .rsp - BitVec.ofNat 64 (8 * (j + 1))) 64 = @@ -64,7 +64,7 @@ theorem frame_restored (s t : State) (rs : List Reg) (notSp : .rsp ∉ rs) ∀ r ∈ rs, (frameEnd t rs).gpr r = s.gpr r := by apply frameEnd_restore t rs s.gpr notSp distinct intro j hj - have offsetBound : 8 * (j + 1) ≤ 120 + 8 * rs.length := by omega + have offsetBound : 8 * (j + 1) ≤ 272 + 8 * rs.length := by omega rw [sp, frameStart_sp, ← Offset.ofNat_sub_ofNat offsetBound, Offset.sub_add_sub_cancel, unchanged j hj] exact frameStart_word s rs notSp space j hj diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveReturn.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveReturn.lean new file mode 100644 index 000000000..d85b750ca --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveReturn.lean @@ -0,0 +1,68 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveBodySaved +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveRestore + +/-! The nested ABI frames return the complete result and restore all saved registers. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +def wholeWrites (s : State) : List Region := [abiMatrix s, abiWork s, abiOutput s, below (s.gpr .rsp) 344] + +theorem BodyDone.whole_frame {s t : State} (h : AbiEnvironment s) (done : BodyDone s t) : + Frame (wholeWrites s) s.mem t.mem := by + have prologue := frameStart_frame s Impl.Argon2.X86_64.Derive.saved (by decide) (by + have space := h.stack; change 320 ≤ (s.gpr .rsp).toNat; omega) + apply (prologue.sub ?_).trans (done.frame.sub ?_) + · intro r hr + simp only [List.mem_singleton] at hr; subst r + exact ⟨below (s.gpr .rsp) 344, by simp [wholeWrites], below_sub (by decide) (by decide)⟩ + · intro r hr + simp only [bodyWrites, List.mem_cons, List.not_mem_nil, or_false] at hr + rcases hr with rfl | rfl | rfl | rfl | rfl + · exact ⟨abiMatrix s, by simp [wholeWrites], fun _ h => h⟩ + · exact ⟨abiWork s, by simp [wholeWrites], fun _ h => h⟩ + · exact ⟨abiOutput s, by simp [wholeWrites], fun _ h => h⟩ + · refine ⟨below (s.gpr .rsp) 344, by simp [wholeWrites], ?_⟩ + rw [prologue_sp] + exact Offset.sub_below _ (by decide) (by decide) + · refine ⟨below (s.gpr .rsp) 344, by simp [wholeWrites], ?_⟩ + rw [prologue_sp] + unfold below + rw [BitVec.sub_sub, ← BitVec.ofNat_add] + exact Region.sub_prefix (by decide) + +theorem return_post {s t : State} (done : BodyDone s t) : + (Spec.Argon2.deriveContract X86_64.abi 344).post s (frameEnd t Impl.Argon2.X86_64.Derive.saved) := by + have post := done.post + sig_post [Spec.Argon2.deriveContract, Spec.Argon2.deriveSig, X86_64.abi, + X86_64.argRegs, X86_64.stackArg, X86_64.stackArgAddr, List.range, List.range.loop] at post + sig_post [Spec.Argon2.deriveContract, Spec.Argon2.deriveSig, X86_64.abi, + X86_64.argRegs, X86_64.stackArg, X86_64.stackArgAddr, List.range, List.range.loop] + exact post + +theorem code_wp (v : Proof.Blake2.X86_64.Backend) (name : String) (s : State) + (pre : (Spec.Argon2.deriveContract X86_64.abi 344).pre s) : + WP isa (Impl.Argon2.X86_64.Derive.code name (HPrime.hash v)) s fun t => + (Spec.Argon2.deriveContract X86_64.abi 344).post s t ∧ + (∀ r ∈ calleeSaved, t.gpr r = s.gpr r) ∧ Frame (wholeWrites s) s.mem t.mem := by + have h := abi_environment s pre + unfold Impl.Argon2.X86_64.Derive.code + apply frame_ok s Impl.Argon2.X86_64.Derive.saved _ _ (by decide) + (by have space := h.stack; change 320 ≤ (s.gpr .rsp).toNat; omega) + refine (body_ok v name s h).mono ?_ + intro t done + refine ⟨done.sp, done.wr, return_post done, ?_, ?_⟩ + · have restored := frame_restored s t Impl.Argon2.X86_64.Derive.saved (by decide) (by decide) + (by have space := h.stack; change 320 ≤ (s.gpr .rsp).toNat; omega) done.sp + (fun j hj => done.saved h j hj) + have stack := (frameEnd_metadata s t Impl.Argon2.X86_64.Derive.saved done.sp done.wr).1 + intro r hr + have member : ∀ r ∈ calleeSaved, r = .rsp ∨ r ∈ Impl.Argon2.X86_64.Derive.saved := by decide + rcases member r hr with rfl | hr + · exact stack + · exact restored r hr + · rw [frameEnd_mem] + exact done.whole_frame h + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSaved.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSaved.lean index d06ce51a0..82cb6a8c8 100644 --- a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSaved.lean +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSaved.lean @@ -7,14 +7,14 @@ namespace VG.Proof.Argon2.X86_64.Derive open VG VG.X86_64 theorem frameStart_word (s : State) (rs : List Reg) (notSp : .rsp ∉ rs) - (space : 120 + 8 * rs.length ≤ (s.gpr .rsp).toNat) (j : Nat) (bound : j < rs.length) : + (space : 272 + 8 * rs.length ≤ (s.gpr .rsp).toNat) (j : Nat) (bound : j < rs.length) : (frameStart s rs).mem.readW (s.gpr .rsp - BitVec.ofNat 64 (8 * (j + 1))) 64 = s.gpr rs[j] := by induction rs generalizing s j with | nil => exact absurd bound (Nat.not_lt_zero _) | cons r rs ih => simp only [List.mem_cons, not_or] at notSp have enough : 8 ≤ (s.gpr .rsp).toNat := by simp only [List.length_cons] at space; omega - have innerSpace : 120 + 8 * rs.length ≤ ((pushed [r] s).gpr .rsp).toNat := by + have innerSpace : 272 + 8 * rs.length ≤ ((pushed [r] s).gpr .rsp).toNat := by rw [pushed_rsp] simp only [List.length_singleton, Nat.mul_one] rw [toNat_sub_ofNat enough] diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSeparation.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSeparation.lean new file mode 100644 index 000000000..838141811 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSeparation.lean @@ -0,0 +1,40 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveRegions + +/-! Buffer separation is supplied by the shared signature, including read-only arguments. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +structure AbiSeparation (s : State) : Prop where + inputWork : ∀ r ∈ abiInputs s, r.Disjoint (abiWork s) + matrixWork : (abiMatrix s).Disjoint (abiWork s) + outputWork : (abiOutput s).Disjoint (abiWork s) + matrixOutput : (abiMatrix s).Disjoint (abiOutput s) + +theorem abi_separation {s : State} (h : AbiEnvironment s) : AbiSeparation s := by + have pairs := h.pairs + sig_eval [abiBuffers, abiInputs, abiMatrix, abiWork, abiOutput, abiArguments] at pairs + sig_split pairs + constructor + all_goals sig_eval [abiInputs, abiMatrix, abiWork, abiOutput] + all_goals sig_and_intros + all_goals first + | with_reducible assumption + | with_reducible exact Region.Disjoint.symm ‹_› + +theorem private_scratch {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : t.gpr .rbx = (abiWork s).base := by + have word := prologue_word h 9 (by decide) + change (prologueState s).mem.readW ((prologueState s).gpr .rsp + 400) 64 = abiWord s 80 at word + exact prepared.scratch.trans word + +theorem abi_input_lengths {s : State} (h : AbiEnvironment s) : ∀ r ∈ abiInputs s, r.len < 2 ^ 32 := by + have valid := h.valid + unfold Spec.Argon2.valid at valid + obtain ⟨_, _, _, _, _, _, _, _, password, salt, secret, ad⟩ := valid + sig_eval [abiInputs] + sig_and_intros + all_goals with_reducible assumption + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSpSafe.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSpSafe.lean new file mode 100644 index 000000000..344c4bd21 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveSpSafe.lean @@ -0,0 +1,61 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveLit +import VerifiedGarbage.Proof.Argon2.X86_64.HPrime.Verified +import VerifiedGarbage.Proof.Argon2.X86_64.InitialLit +import VerifiedGarbage.Proof.Argon2.X86_64.MemoryInitLit +import VerifiedGarbage.Proof.Argon2.X86_64.ParametersLit + +/-! The complete derivation does not directly write the stack pointer for every BLAKE2b backend. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +local notation "property" => (fun i => !isa.writesSp i) + +theorem initial_spSafe (v : Proof.Blake2.X86_64.Backend) : + (Impl.Argon2.X86_64.Initial.code (HPrime.hash v)).all property = true := by + have init : (HPrime.hash v).init.all property = true := by + change (Impl.Blake2.X86_64.Stream.init Spec.Blake2.b).all _ = true + lit_decide + have update : (HPrime.hash v).update.all property = true := v.updateSpSafe + have finalize : (HPrime.hash v).finalize.all property = true := v.finalizeSpSafe + simp only [Impl.Argon2.X86_64.Initial.code, Impl.Argon2.X86_64.Initial.start, + Impl.Argon2.X86_64.Initial.absorb, Impl.Argon2.X86_64.Initial.finish, + Impl.Argon2.X86_64.HPrime.init, Impl.Argon2.X86_64.HPrime.absorbFixed, + Impl.Argon2.X86_64.HPrime.update, Impl.Argon2.X86_64.HPrime.finalize, Code.all] + rw [init, update, finalize] + lit_decide + +theorem memory_spSafe (v : Proof.Blake2.X86_64.Backend) (name : String) : + (Impl.Argon2.X86_64.MemoryInit.code name (HPrime.hash v)).all property = true := by + simp only [Impl.Argon2.X86_64.MemoryInit.code, Impl.Argon2.X86_64.MemoryInit.clear, + Impl.Argon2.X86_64.MemoryInit.lane, Impl.Argon2.X86_64.MemoryInit.block, Code.all] + rw [HPrime.spSafe v] + lit_decide + +theorem frame_spSafe (body : Prog isa) (rs : List Reg) (h : body.all property = true) + (safe : ∀ r ∈ rs, r ≠ .rsp) : + (Impl.Argon2.X86_64.Derive.frame body rs).all property = true := by + induction rs with + | nil => + change (true && body.all property && true) = true + rw [h]; rfl + | cons r rs ih => + change (true && (Impl.Argon2.X86_64.Derive.frame body rs).all property && property (.pop r 1)) = true + rw [ih (fun q hq => safe q (List.mem_cons_of_mem _ hq))] + simp only [Bool.true_and] + change Bool.not ((some r == some Reg.rsp) : Bool) = true + rw [Bool.not_eq_true', beq_eq_false_iff_ne] + exact fun eq => safe r (List.mem_cons_self ..) (Option.some.inj eq) + +theorem code_spSafe (v : Proof.Blake2.X86_64.Backend) (name : String) : + (Impl.Argon2.X86_64.Derive.code name (HPrime.hash v)).all property = true := by + unfold Impl.Argon2.X86_64.Derive.code + apply frame_spSafe (safe := by decide) + simp only [Impl.Argon2.X86_64.Derive.body, Impl.Argon2.X86_64.InitialBody.code, + Impl.Argon2.X86_64.InitFill.code, Impl.Argon2.X86_64.FillFinish.code, + Impl.Argon2.X86_64.Finish.code, Impl.Argon2.X86_64.FinalOutput.code, Code.all] + rw [initial_spSafe v, memory_spSafe v name, HPrime.spSafe v] + lit_decide + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveVerified.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveVerified.lean new file mode 100644 index 000000000..62fdb7e15 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveVerified.lean @@ -0,0 +1,17 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveCorrect +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveCT +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveContract +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveSpSafe + +/-! Complete Argon2 verification against the reviewed shared API contract. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 + +theorem verified (v : Proof.Blake2.X86_64.Backend) (name : String) : + Verified X86_64.target (Impl.Argon2.X86_64.Derive.code name (HPrime.hash v)) + (Spec.Argon2.deriveContract X86_64.abi 344) := + ⟨code_correct v name, code_ct v name, contract_sat⟩ + +end VG.Proof.Argon2.X86_64.Derive diff --git a/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveWords.lean b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveWords.lean new file mode 100644 index 000000000..61d0b5ed0 --- /dev/null +++ b/lean/VerifiedGarbage/Proof/Argon2/X86_64/DeriveWords.lean @@ -0,0 +1,69 @@ +import VerifiedGarbage.Proof.Argon2.X86_64.DeriveRegions +import VerifiedGarbage.Proof.Argon2.X86_64.InitialBody + +/-! Exact words consumed by hashing, initialization, filling, and finalization. -/ + +namespace VG.Proof.Argon2.X86_64.Derive + +open VG VG.X86_64 +open VG.Proof.Argon2.X86_64.Initial (wordAt) + +theorem params_variant_code (kind passes memory lanes tagLen : Nat) (bound : kind ≤ 2) : + (Spec.Argon2.params kind passes memory lanes tagLen).variant.code = kind := by + by_cases zero : kind = 0 + · subst kind; rfl + · by_cases one : kind = 1 + · subst kind; rfl + · have two : kind = 2 := by omega + subst kind; rfl + +structure DeriveWords (s t : State) : Prop where + passes : wordAt t 72 = BitVec.ofNat 64 (abiParams s).passes + saltLength : wordAt t 80 = s.gpr .r8 + salt : wordAt t 88 = s.gpr .rcx + passwordLength : wordAt t 96 = s.gpr .rdx + password : wordAt t 104 = s.gpr .rsi + kind : wordAt t 112 = BitVec.ofNat 64 (abiParams s).variant.code + memory : wordAt t 176 = BitVec.ofNat 64 (abiParams s).memory + lanes : wordAt t 184 = BitVec.ofNat 64 (abiParams s).lanes + secret : wordAt t 200 = abiWord s 32 + secretLength : wordAt t 208 = abiWord s 40 + ad : wordAt t 216 = abiWord s 48 + adLength : wordAt t 224 = abiWord s 56 + matrix : wordAt t 232 = abiWord s 64 + blocks : wordAt t 240 = BitVec.ofNat 64 (abiParams s).blocks + work : wordAt t 248 = abiWord s 80 + output : wordAt t 256 = abiWord s 88 + tagLength : wordAt t 264 = BitVec.ofNat 64 (abiParams s).tagLen + +theorem private_words {s t : State} (h : AbiEnvironment s) + (prepared : PrivatePrepared (prologueState s) t) : DeriveWords s t := by + have parameters := private_parameters h prepared + refine ⟨?_, private_argument_word prepared (80, .r8) (by decide), + private_argument_word prepared (88, .rcx) (by decide), + private_argument_word prepared (96, .rdx) (by decide), + private_argument_word prepared (104, .rsi) (by decide), ?_, parameters.memoryWord, parameters.lanesWord, + private_stack_word h prepared 3 (by decide), private_stack_word h prepared 4 (by decide), + private_stack_word h prepared 5 (by decide), private_stack_word h prepared 6 (by decide), + private_stack_word h prepared 7 (by decide), ?_, private_stack_word h prepared 9 (by decide), + private_stack_word h prepared 10 (by decide), ?_⟩ + · have word := private_argument_word prepared (72, .r9) (by decide) + change wordAt t 72 = ((s.gpr .r9).setWidth 32).setWidth 64 at word + change wordAt t 72 = BitVec.ofNat 64 ((s.gpr .r9).setWidth 32).toNat + rw [word, BitVec.ofNat_toNat] + · have code := params_variant_code ((s.gpr .rdi).setWidth 32).toNat + (abiParams s).passes (abiParams s).memory (abiParams s).lanes (abiParams s).tagLen h.kind + change (abiParams s).variant.code = ((s.gpr .rdi).setWidth 32).toNat at code + rw [code] + have word := private_argument_word prepared (112, .rdi) (by decide) + change wordAt t 112 = ((s.gpr .rdi).setWidth 32).setWidth 64 at word + rw [word, BitVec.ofNat_toNat] + · have word := private_stack_word h prepared 8 (by decide) + change wordAt t 240 = abiWord s 72 at word + rw [word, ← h.blocks, BitVec.ofNat_toNat, BitVec.setWidth_eq] + · have word := private_stack_word h prepared 11 (by decide) + change wordAt t 264 = abiWord s 96 at word + change wordAt t 264 = BitVec.ofNat 64 (abiWord s 96).toNat + rw [word, BitVec.ofNat_toNat, BitVec.setWidth_eq] + +end VG.Proof.Argon2.X86_64.Derive diff --git a/src/argon2.rs b/src/argon2.rs new file mode 100644 index 000000000..e5a2c1071 --- /dev/null +++ b/src/argon2.rs @@ -0,0 +1,168 @@ +//! Argon2 version 1.3 (RFC 9106). +//! +//! The complete derivation is verified assembly (`VG.Spec.Argon2.deriveContract`), +//! including H₀, memory initialization, every filling pass and final H′. Hashing +//! follows the selected BLAKE2b backend. Rust only validates arguments and +//! allocates the matrix and scratch. Lanes are evaluated serially: `threads` +//! limits workers without changing the result. +//! +//! Argon2i leaks no input contents. Argon2d and Argon2id permit the +//! data-dependent reference indices specified by `VG.Spec.Argon2.references`. + +#![cfg(all(target_arch = "x86_64", feature = "alloc"))] + +use alloc::vec::Vec; +use core::fmt; + +use crate::arch::argon2::vg_argon2; +use crate::hashes::blake2b::Blake2bBackend; + +/// Argon2's addressing variant. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum Variant { + /// Data-dependent addressing. + Argon2d = 0, + /// Data-independent addressing. + Argon2i = 1, + /// Independent addressing for the first half of the first pass. + Argon2id = 2, +} + +/// Why [`derive`] refused to derive a key. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Error { + /// Invalid costs or lengths: iterations must be positive; lanes and + /// threads must be in 1..2²⁴; memory must be at least eight KiB per lane; + /// inputs must be shorter than 2³² bytes and output must be 4..2³² bytes. + InvalidParameters, + /// Allocating the memory matrix or scratch failed. + AllocationFailed, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Self::InvalidParameters => "invalid Argon2 parameters", + Self::AllocationFailed => "could not allocate Argon2's memory", + }) + } +} + +impl core::error::Error for Error {} + +/// Fills `out` with an Argon2 version 1.3 key. `memory_cost` is in KiB and +/// `iterations` counts passes. `lanes` is the algorithm's parallelism input; +/// `threads` is a maximum worker count and does not change the key. `secret` +/// and `associated_data` may be empty. The matrix contains +/// `4 * lanes * floor(memory_cost / (4 * lanes))` blocks of 1024 bytes; +/// scratch occupies another 16 KiB. +/// +/// # Errors +/// +/// Returns [`Error::InvalidParameters`] for invalid costs or lengths and +/// [`Error::AllocationFailed`] if memory allocation fails. `out` is unchanged +/// on either error. +#[allow(clippy::too_many_arguments)] +pub fn derive( + variant: Variant, + password: &[u8], + salt: &[u8], + iterations: u32, + memory_cost: u32, + lanes: u32, + threads: u32, + secret: &[u8], + associated_data: &[u8], + out: &mut [u8], +) -> Result<(), Error> { + if !valid( + iterations, + memory_cost, + lanes, + threads, + [ + password.len(), + salt.len(), + secret.len(), + associated_data.len(), + out.len(), + ], + ) { + return Err(Error::InvalidParameters); + } + let divisor = 4 * lanes; + let blocks = (memory_cost / divisor * divisor) as usize; + let mut matrix = allocate::<128>(blocks)?; + let mut scratch = allocate::<2048>(1)?; + let derive = match Blake2bBackend::select(crate::cpu::detected()) { + Blake2bBackend::Scalar => vg_argon2, + }; + // SAFETY: validation establishes every numeric precondition of + // `deriveContract`. The matrix contains exactly the rounded block count + // and scratch is 2048 u64s. Input slices are valid for their lengths; + // output and both allocations are distinct mutable objects. They do not + // overlap each other, any input, the caller's stack arguments or the + // 344-byte assembly stack frame, and no region wraps the address space. + // The selected BLAKE2b backend supplies every required CPU feature. + unsafe { + derive( + variant as u32, + password.as_ptr(), + password.len(), + salt.as_ptr(), + salt.len(), + iterations, + memory_cost, + lanes, + threads, + secret.as_ptr(), + secret.len(), + associated_data.as_ptr(), + associated_data.len(), + matrix.as_mut_ptr(), + blocks, + scratch.as_mut_ptr(), + out.as_mut_ptr(), + out.len(), + ) + }; + Ok(()) +} + +fn valid(iterations: u32, memory: u32, lanes: u32, threads: u32, lengths: [usize; 5]) -> bool { + iterations > 0 + && (1..1 << 24).contains(&lanes) + && (1..1 << 24).contains(&threads) + && memory >= 8 * lanes + && lengths[4] >= 4 + && lengths.into_iter().all(|n| n <= u32::MAX as usize) +} + +fn allocate(len: usize) -> Result, Error> { + let mut v = Vec::new(); + v.try_reserve_exact(len) + .map_err(|_| Error::AllocationFailed)?; + v.resize(len, [0; N]); + Ok(v) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn length_limits() { + assert!(valid(1, 8, 1, 1, [u32::MAX as usize; 5])); + for j in 0..5 { + let mut lengths = [0, 0, 0, 0, 4]; + lengths[j] = u32::MAX as usize + 1; + assert!(!valid(1, 8, 1, 1, lengths)); + } + } + + #[test] + fn allocation_failure() { + assert_eq!(allocate::<128>(usize::MAX), Err(Error::AllocationFailed)); + } +} diff --git a/src/asm/x86_64/argon2.rs b/src/asm/x86_64/argon2.rs index 6a7e78710..d4488678a 100644 --- a/src/asm/x86_64/argon2.rs +++ b/src/asm/x86_64/argon2.rs @@ -6203,3 +6203,2857 @@ pub(crate) unsafe extern "sysv64" fn vg_argon2_hprime(input: *const u8, input_le vg_blake2b_finalize = sym super::blake2b::vg_blake2b_finalize, ) } + +/// Argon2 version 1.3 (RFC 9106): derives `out_len` bytes at `out` from the password, salt, optional secret and associated data. `kind` selects Argon2d (0), Argon2i (1) or Argon2id (2). Performs the entire derivation: H₀, H′, initialization, all memory-filling passes, the final lane XOR and H′ of that block. +/// +/// `iterations` is the pass count, `memory_cost` is the requested KiB count, and `lanes` is the algorithm's parallelism parameter. `threads` limits execution workers and does not change the result; serial execution is permitted. +/// +/// Contract: `VG.Spec.Argon2.deriveContract`. Argon2i is constant time in all input contents. Argon2d/id additionally permit timing to depend on the sequence of data-dependent reference block indices (`VG.Spec.Argon2.references`), as required by their addressing rules, but on nothing else secret. Pointers, lengths and numeric parameters are public. +/// +/// Serial lane evaluation honors every positive worker limit. All hashing uses the selected BLAKE2b streaming backend, including H₀ and every H′ call. +/// +/// # Safety +/// +/// * `password` must be valid for reads of `password_len` bytes. +/// * `salt` must be valid for reads of `salt_len` bytes. +/// * `secret` must be valid for reads of `secret_len` bytes. +/// * `associated_data` must be valid for reads of `ad_len` bytes. +/// * `memory` must be valid for reads and writes of `1024 * blocks` bytes. +/// * `scratch` must be valid for reads and writes of 16384 bytes. +/// * `out` must be valid for reads and writes of `out_len` bytes. +/// * `kind` must be 0, 1 or 2; `iterations` must be positive; `lanes` and `threads` must be in 1..2^24-1; `memory_cost` must be at least `8 * lanes`. +/// * All input lengths must be less than 2^32; `out_len` must be in 4..2^32-1. +/// * `blocks` must equal `4 * lanes * floor(memory_cost / (4 * lanes))`. +/// * `memory` and `scratch` are working space: their initial contents are arbitrary and their contents on return are unspecified. +/// * `memory`, `scratch` and `out` must not overlap each other, `password`, `salt`, `secret`, `associated_data` or the arguments on the stack (distinct Rust objects never do). +/// * None of `password`, `salt`, `secret`, `associated_data`, `memory`, `scratch` and `out` may overlap the return address on the stack or the 344 bytes of stack below it, or wrap around the end of the address space (no Rust object does). +#[unsafe(naked)] +pub(crate) unsafe extern "sysv64" fn vg_argon2(kind: u32, password: *const u8, password_len: usize, salt: *const u8, salt_len: usize, iterations: u32, memory_cost: u32, lanes: u32, threads: u32, secret: *const u8, secret_len: usize, associated_data: *const u8, ad_len: usize, memory: *mut [u64; 128], blocks: usize, scratch: *mut [u64; 2048], out: *mut u8, out_len: usize) { + core::arch::naked_asm!( + "push rbx", + "push rbp", + "push r12", + "push r13", + "push r14", + "push r15", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "push rax", + "mov rax, QWORD PTR [rsp+328]", + "mov QWORD PTR [rsp+176], rax", + "mov rax, QWORD PTR [rsp+336]", + "mov QWORD PTR [rsp+184], rax", + "mov rax, QWORD PTR [rsp+344]", + "mov QWORD PTR [rsp+192], rax", + "mov rax, QWORD PTR [rsp+352]", + "mov QWORD PTR [rsp+200], rax", + "mov rax, QWORD PTR [rsp+360]", + "mov QWORD PTR [rsp+208], rax", + "mov rax, QWORD PTR [rsp+368]", + "mov QWORD PTR [rsp+216], rax", + "mov rax, QWORD PTR [rsp+376]", + "mov QWORD PTR [rsp+224], rax", + "mov rax, QWORD PTR [rsp+384]", + "mov QWORD PTR [rsp+232], rax", + "mov rax, QWORD PTR [rsp+392]", + "mov QWORD PTR [rsp+240], rax", + "mov rax, QWORD PTR [rsp+400]", + "mov QWORD PTR [rsp+248], rax", + "mov rax, QWORD PTR [rsp+408]", + "mov QWORD PTR [rsp+256], rax", + "mov rax, QWORD PTR [rsp+416]", + "mov QWORD PTR [rsp+264], rax", + "mov rbp, rsp", + "mov edi, edi", + "mov r9d, r9d", + "mov QWORD PTR [rbp+72], r9", + "mov QWORD PTR [rbp+80], r8", + "mov QWORD PTR [rbp+88], rcx", + "mov QWORD PTR [rbp+96], rdx", + "mov QWORD PTR [rbp+104], rsi", + "mov QWORD PTR [rbp+112], rdi", + "mov rbx, QWORD PTR [rbp+248]", + "mov rax, QWORD PTR [rbp+176]", + "mov eax, eax", + "mov QWORD PTR [rbp+176], rax", + "mov rax, QWORD PTR [rbp+184]", + "mov eax, eax", + "mov QWORD PTR [rbp+184], rax", + "mov rax, QWORD PTR [rbp+192]", + "mov eax, eax", + "mov QWORD PTR [rbp+192], rax", + "mov rdi, QWORD PTR [rbp+176]", + "mov rsi, QWORD PTR [rbp+184]", + "add rsi, rsi", + "add rsi, rsi", + "mov r8d, 0", + "mov r9d, 0", + "mov eax, 0", + "cmp rax, 0", + "mov rcx, rdi", + "shr rcx, 32", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 31", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 30", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 29", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 28", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 27", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 26", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 25", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 24", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 23", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 22", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 21", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 20", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 19", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 18", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 17", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 16", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 15", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 14", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 13", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 12", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 11", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 10", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 9", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 8", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 7", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 6", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 5", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 4", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 3", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 2", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 1", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov r13, r9", + "add r13, r13", + "add r13, r13", + "mov esi, 64", + "mov rdi, rbx", + "mov rdx, rbx", + "add rdx, 832", + "mov ecx, 0", + "call {vg_blake2b_init}", + "mov rax, QWORD PTR [rbp+184]", + "mov DWORD PTR [rbx+768], eax", + "mov rax, QWORD PTR [rbp+264]", + "mov DWORD PTR [rbx+772], eax", + "mov rax, QWORD PTR [rbp+176]", + "mov DWORD PTR [rbx+776], eax", + "mov rax, QWORD PTR [rbp+72]", + "mov DWORD PTR [rbx+780], eax", + "mov eax, 19", + "mov DWORD PTR [rbx+784], eax", + "mov rax, QWORD PTR [rbp+112]", + "mov DWORD PTR [rbx+788], eax", + "mov esi, 0", + "mov rdx, rbx", + "add rdx, 768", + "mov ecx, 24", + "mov rdi, rbx", + "mov r8, rbx", + "add r8, 192", + "call {vg_blake2b_update}", + "mov r12d, 24", + "mov r14, QWORD PTR [rbp+96]", + "mov DWORD PTR [rbx+792], r14d", + "mov rsi, r12", + "mov rdx, rbx", + "add rdx, 792", + "mov ecx, 4", + "mov rdi, rbx", + "mov r8, rbx", + "add r8, 192", + "call {vg_blake2b_update}", + "add r12, 4", + "mov rsi, r12", + "mov rdx, QWORD PTR [rbp+104]", + "mov rcx, r14", + "mov rdi, rbx", + "mov r8, rbx", + "add r8, 192", + "call {vg_blake2b_update}", + "add r12, r14", + "mov r14, QWORD PTR [rbp+80]", + "mov DWORD PTR [rbx+792], r14d", + "mov rsi, r12", + "mov rdx, rbx", + "add rdx, 792", + "mov ecx, 4", + "mov rdi, rbx", + "mov r8, rbx", + "add r8, 192", + "call {vg_blake2b_update}", + "add r12, 4", + "mov rsi, r12", + "mov rdx, QWORD PTR [rbp+88]", + "mov rcx, r14", + "mov rdi, rbx", + "mov r8, rbx", + "add r8, 192", + "call {vg_blake2b_update}", + "add r12, r14", + "mov r14, QWORD PTR [rbp+208]", + "mov DWORD PTR [rbx+792], r14d", + "mov rsi, r12", + "mov rdx, rbx", + "add rdx, 792", + "mov ecx, 4", + "mov rdi, rbx", + "mov r8, rbx", + "add r8, 192", + "call {vg_blake2b_update}", + "add r12, 4", + "mov rsi, r12", + "mov rdx, QWORD PTR [rbp+200]", + "mov rcx, r14", + "mov rdi, rbx", + "mov r8, rbx", + "add r8, 192", + "call {vg_blake2b_update}", + "add r12, r14", + "mov r14, QWORD PTR [rbp+224]", + "mov DWORD PTR [rbx+792], r14d", + "mov rsi, r12", + "mov rdx, rbx", + "add rdx, 792", + "mov ecx, 4", + "mov rdi, rbx", + "mov r8, rbx", + "add r8, 192", + "call {vg_blake2b_update}", + "add r12, 4", + "mov rsi, r12", + "mov rdx, QWORD PTR [rbp+216]", + "mov rcx, r14", + "mov rdi, rbx", + "mov r8, rbx", + "add r8, 192", + "call {vg_blake2b_update}", + "add r12, r14", + "mov rsi, r12", + "mov rdi, rbx", + "mov rdx, rbx", + "add rdx, 768", + "mov rcx, rbx", + "add rcx, 192", + "call {vg_blake2b_finalize}", + "mov r14, rbp", + "mov eax, 64", + "mov rdx, rbx", + "add rdx, 768", + "20:", + "movzx ecx, BYTE PTR [rdx]", + "mov BYTE PTR [r14], cl", + "add rdx, 1", + "add r14, 1", + "sub rax, 1", + "jne 20b", + "mov r14, QWORD PTR [rbp+232]", + "mov rax, QWORD PTR [rbp+240]", + "mov ecx, 0", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "21:", + "mov QWORD PTR [r14], rcx", + "add r14, 8", + "sub rax, 1", + "jne 21b", + "mov r14, QWORD PTR [rbp+232]", + "mov r12d, 0", + "mov r15, QWORD PTR [rbp+184]", + "add r13, r13", + "add r13, r13", + "add r13, r13", + "add r13, r13", + "add r13, r13", + "add r13, r13", + "add r13, r13", + "add r13, r13", + "add r13, r13", + "add r13, r13", + "22:", + "mov eax, 0", + "mov DWORD PTR [rbp+64], eax", + "mov DWORD PTR [rbp+68], r12d", + "mov rdi, rbp", + "mov esi, 72", + "mov rdx, r14", + "mov ecx, 1024", + "mov r8, rbx", + "call {vg_argon2_hprime}", + "add r14, 1024", + "mov eax, 1", + "mov DWORD PTR [rbp+64], eax", + "mov DWORD PTR [rbp+68], r12d", + "mov rdi, rbp", + "mov esi, 72", + "mov rdx, r14", + "mov ecx, 1024", + "mov r8, rbx", + "call {vg_argon2_hprime}", + "add r14, r13", + "sub r14, 1024", + "add r12, 1", + "sub r15, 1", + "jne 22b", + "mov r12, r13", + "shr r12, 10", + "shr r13, 12", + "mov rax, 0", + "mov QWORD PTR [rbp], rax", + "mov rbx, 0", + "mov r14, 0", + "23:", + "mov r14, 0", + "24:", + "mov rbx, 0", + "25:", + "mov rax, 0", + "mov QWORD PTR [rbp+8], rax", + "mov rcx, QWORD PTR [rbp]", + "or rcx, r14", + "cmp rcx, 0", + "je 26f", + "mov r15, 0", + "jmp 27f", + "26:", + "mov r15, 2", + "27:", + "cmp r15, r13", + "jb 28f", + "jmp 29f", + "28:", + "210:", + "mov rax, QWORD PTR [rbp+112]", + "mov r10, rax", + "xor r10, 1", + "cmp r10, 1", + "sbb r10, r10", + "mov r8, rax", + "xor r8, 2", + "cmp r8, 1", + "sbb r8, r8", + "mov r9, QWORD PTR [rbp]", + "cmp r9, 1", + "sbb r9, r9", + "cmp r14, 2", + "sbb r11, r11", + "and r8, r9", + "and r8, r11", + "or r10, r8", + "and r10, 1", + "cmp r10, 0", + "je 211f", + "mov rax, r15", + "shr rax, 7", + "add rax, 1", + "cmp rax, QWORD PTR [rbp+8]", + "je 213f", + "mov QWORD PTR [rbp+8], rax", + "mov rdi, QWORD PTR [rbp+248]", + "add rdi, 5120", + "mov rax, 0", + "mov QWORD PTR [rdi], rax", + "mov QWORD PTR [rdi+8], rax", + "mov QWORD PTR [rdi+16], rax", + "mov QWORD PTR [rdi+24], rax", + "mov QWORD PTR [rdi+32], rax", + "mov QWORD PTR [rdi+40], rax", + "mov QWORD PTR [rdi+48], rax", + "mov QWORD PTR [rdi+56], rax", + "mov QWORD PTR [rdi+64], rax", + "mov QWORD PTR [rdi+72], rax", + "mov QWORD PTR [rdi+80], rax", + "mov QWORD PTR [rdi+88], rax", + "mov QWORD PTR [rdi+96], rax", + "mov QWORD PTR [rdi+104], rax", + "mov QWORD PTR [rdi+112], rax", + "mov QWORD PTR [rdi+120], rax", + "mov QWORD PTR [rdi+128], rax", + "mov QWORD PTR [rdi+136], rax", + "mov QWORD PTR [rdi+144], rax", + "mov QWORD PTR [rdi+152], rax", + "mov QWORD PTR [rdi+160], rax", + "mov QWORD PTR [rdi+168], rax", + "mov QWORD PTR [rdi+176], rax", + "mov QWORD PTR [rdi+184], rax", + "mov QWORD PTR [rdi+192], rax", + "mov QWORD PTR [rdi+200], rax", + "mov QWORD PTR [rdi+208], rax", + "mov QWORD PTR [rdi+216], rax", + "mov QWORD PTR [rdi+224], rax", + "mov QWORD PTR [rdi+232], rax", + "mov QWORD PTR [rdi+240], rax", + "mov QWORD PTR [rdi+248], rax", + "mov QWORD PTR [rdi+256], rax", + "mov QWORD PTR [rdi+264], rax", + "mov QWORD PTR [rdi+272], rax", + "mov QWORD PTR [rdi+280], rax", + "mov QWORD PTR [rdi+288], rax", + "mov QWORD PTR [rdi+296], rax", + "mov QWORD PTR [rdi+304], rax", + "mov QWORD PTR [rdi+312], rax", + "mov QWORD PTR [rdi+320], rax", + "mov QWORD PTR [rdi+328], rax", + "mov QWORD PTR [rdi+336], rax", + "mov QWORD PTR [rdi+344], rax", + "mov QWORD PTR [rdi+352], rax", + "mov QWORD PTR [rdi+360], rax", + "mov QWORD PTR [rdi+368], rax", + "mov QWORD PTR [rdi+376], rax", + "mov QWORD PTR [rdi+384], rax", + "mov QWORD PTR [rdi+392], rax", + "mov QWORD PTR [rdi+400], rax", + "mov QWORD PTR [rdi+408], rax", + "mov QWORD PTR [rdi+416], rax", + "mov QWORD PTR [rdi+424], rax", + "mov QWORD PTR [rdi+432], rax", + "mov QWORD PTR [rdi+440], rax", + "mov QWORD PTR [rdi+448], rax", + "mov QWORD PTR [rdi+456], rax", + "mov QWORD PTR [rdi+464], rax", + "mov QWORD PTR [rdi+472], rax", + "mov QWORD PTR [rdi+480], rax", + "mov QWORD PTR [rdi+488], rax", + "mov QWORD PTR [rdi+496], rax", + "mov QWORD PTR [rdi+504], rax", + "mov QWORD PTR [rdi+512], rax", + "mov QWORD PTR [rdi+520], rax", + "mov QWORD PTR [rdi+528], rax", + "mov QWORD PTR [rdi+536], rax", + "mov QWORD PTR [rdi+544], rax", + "mov QWORD PTR [rdi+552], rax", + "mov QWORD PTR [rdi+560], rax", + "mov QWORD PTR [rdi+568], rax", + "mov QWORD PTR [rdi+576], rax", + "mov QWORD PTR [rdi+584], rax", + "mov QWORD PTR [rdi+592], rax", + "mov QWORD PTR [rdi+600], rax", + "mov QWORD PTR [rdi+608], rax", + "mov QWORD PTR [rdi+616], rax", + "mov QWORD PTR [rdi+624], rax", + "mov QWORD PTR [rdi+632], rax", + "mov QWORD PTR [rdi+640], rax", + "mov QWORD PTR [rdi+648], rax", + "mov QWORD PTR [rdi+656], rax", + "mov QWORD PTR [rdi+664], rax", + "mov QWORD PTR [rdi+672], rax", + "mov QWORD PTR [rdi+680], rax", + "mov QWORD PTR [rdi+688], rax", + "mov QWORD PTR [rdi+696], rax", + "mov QWORD PTR [rdi+704], rax", + "mov QWORD PTR [rdi+712], rax", + "mov QWORD PTR [rdi+720], rax", + "mov QWORD PTR [rdi+728], rax", + "mov QWORD PTR [rdi+736], rax", + "mov QWORD PTR [rdi+744], rax", + "mov QWORD PTR [rdi+752], rax", + "mov QWORD PTR [rdi+760], rax", + "mov QWORD PTR [rdi+768], rax", + "mov QWORD PTR [rdi+776], rax", + "mov QWORD PTR [rdi+784], rax", + "mov QWORD PTR [rdi+792], rax", + "mov QWORD PTR [rdi+800], rax", + "mov QWORD PTR [rdi+808], rax", + "mov QWORD PTR [rdi+816], rax", + "mov QWORD PTR [rdi+824], rax", + "mov QWORD PTR [rdi+832], rax", + "mov QWORD PTR [rdi+840], rax", + "mov QWORD PTR [rdi+848], rax", + "mov QWORD PTR [rdi+856], rax", + "mov QWORD PTR [rdi+864], rax", + "mov QWORD PTR [rdi+872], rax", + "mov QWORD PTR [rdi+880], rax", + "mov QWORD PTR [rdi+888], rax", + "mov QWORD PTR [rdi+896], rax", + "mov QWORD PTR [rdi+904], rax", + "mov QWORD PTR [rdi+912], rax", + "mov QWORD PTR [rdi+920], rax", + "mov QWORD PTR [rdi+928], rax", + "mov QWORD PTR [rdi+936], rax", + "mov QWORD PTR [rdi+944], rax", + "mov QWORD PTR [rdi+952], rax", + "mov QWORD PTR [rdi+960], rax", + "mov QWORD PTR [rdi+968], rax", + "mov QWORD PTR [rdi+976], rax", + "mov QWORD PTR [rdi+984], rax", + "mov QWORD PTR [rdi+992], rax", + "mov QWORD PTR [rdi+1000], rax", + "mov QWORD PTR [rdi+1008], rax", + "mov QWORD PTR [rdi+1016], rax", + "mov rdi, QWORD PTR [rbp+248]", + "add rdi, 7168", + "mov rax, 0", + "mov QWORD PTR [rdi], rax", + "mov QWORD PTR [rdi+8], rax", + "mov QWORD PTR [rdi+16], rax", + "mov QWORD PTR [rdi+24], rax", + "mov QWORD PTR [rdi+32], rax", + "mov QWORD PTR [rdi+40], rax", + "mov QWORD PTR [rdi+48], rax", + "mov QWORD PTR [rdi+56], rax", + "mov QWORD PTR [rdi+64], rax", + "mov QWORD PTR [rdi+72], rax", + "mov QWORD PTR [rdi+80], rax", + "mov QWORD PTR [rdi+88], rax", + "mov QWORD PTR [rdi+96], rax", + "mov QWORD PTR [rdi+104], rax", + "mov QWORD PTR [rdi+112], rax", + "mov QWORD PTR [rdi+120], rax", + "mov QWORD PTR [rdi+128], rax", + "mov QWORD PTR [rdi+136], rax", + "mov QWORD PTR [rdi+144], rax", + "mov QWORD PTR [rdi+152], rax", + "mov QWORD PTR [rdi+160], rax", + "mov QWORD PTR [rdi+168], rax", + "mov QWORD PTR [rdi+176], rax", + "mov QWORD PTR [rdi+184], rax", + "mov QWORD PTR [rdi+192], rax", + "mov QWORD PTR [rdi+200], rax", + "mov QWORD PTR [rdi+208], rax", + "mov QWORD PTR [rdi+216], rax", + "mov QWORD PTR [rdi+224], rax", + "mov QWORD PTR [rdi+232], rax", + "mov QWORD PTR [rdi+240], rax", + "mov QWORD PTR [rdi+248], rax", + "mov QWORD PTR [rdi+256], rax", + "mov QWORD PTR [rdi+264], rax", + "mov QWORD PTR [rdi+272], rax", + "mov QWORD PTR [rdi+280], rax", + "mov QWORD PTR [rdi+288], rax", + "mov QWORD PTR [rdi+296], rax", + "mov QWORD PTR [rdi+304], rax", + "mov QWORD PTR [rdi+312], rax", + "mov QWORD PTR [rdi+320], rax", + "mov QWORD PTR [rdi+328], rax", + "mov QWORD PTR [rdi+336], rax", + "mov QWORD PTR [rdi+344], rax", + "mov QWORD PTR [rdi+352], rax", + "mov QWORD PTR [rdi+360], rax", + "mov QWORD PTR [rdi+368], rax", + "mov QWORD PTR [rdi+376], rax", + "mov QWORD PTR [rdi+384], rax", + "mov QWORD PTR [rdi+392], rax", + "mov QWORD PTR [rdi+400], rax", + "mov QWORD PTR [rdi+408], rax", + "mov QWORD PTR [rdi+416], rax", + "mov QWORD PTR [rdi+424], rax", + "mov QWORD PTR [rdi+432], rax", + "mov QWORD PTR [rdi+440], rax", + "mov QWORD PTR [rdi+448], rax", + "mov QWORD PTR [rdi+456], rax", + "mov QWORD PTR [rdi+464], rax", + "mov QWORD PTR [rdi+472], rax", + "mov QWORD PTR [rdi+480], rax", + "mov QWORD PTR [rdi+488], rax", + "mov QWORD PTR [rdi+496], rax", + "mov QWORD PTR [rdi+504], rax", + "mov QWORD PTR [rdi+512], rax", + "mov QWORD PTR [rdi+520], rax", + "mov QWORD PTR [rdi+528], rax", + "mov QWORD PTR [rdi+536], rax", + "mov QWORD PTR [rdi+544], rax", + "mov QWORD PTR [rdi+552], rax", + "mov QWORD PTR [rdi+560], rax", + "mov QWORD PTR [rdi+568], rax", + "mov QWORD PTR [rdi+576], rax", + "mov QWORD PTR [rdi+584], rax", + "mov QWORD PTR [rdi+592], rax", + "mov QWORD PTR [rdi+600], rax", + "mov QWORD PTR [rdi+608], rax", + "mov QWORD PTR [rdi+616], rax", + "mov QWORD PTR [rdi+624], rax", + "mov QWORD PTR [rdi+632], rax", + "mov QWORD PTR [rdi+640], rax", + "mov QWORD PTR [rdi+648], rax", + "mov QWORD PTR [rdi+656], rax", + "mov QWORD PTR [rdi+664], rax", + "mov QWORD PTR [rdi+672], rax", + "mov QWORD PTR [rdi+680], rax", + "mov QWORD PTR [rdi+688], rax", + "mov QWORD PTR [rdi+696], rax", + "mov QWORD PTR [rdi+704], rax", + "mov QWORD PTR [rdi+712], rax", + "mov QWORD PTR [rdi+720], rax", + "mov QWORD PTR [rdi+728], rax", + "mov QWORD PTR [rdi+736], rax", + "mov QWORD PTR [rdi+744], rax", + "mov QWORD PTR [rdi+752], rax", + "mov QWORD PTR [rdi+760], rax", + "mov QWORD PTR [rdi+768], rax", + "mov QWORD PTR [rdi+776], rax", + "mov QWORD PTR [rdi+784], rax", + "mov QWORD PTR [rdi+792], rax", + "mov QWORD PTR [rdi+800], rax", + "mov QWORD PTR [rdi+808], rax", + "mov QWORD PTR [rdi+816], rax", + "mov QWORD PTR [rdi+824], rax", + "mov QWORD PTR [rdi+832], rax", + "mov QWORD PTR [rdi+840], rax", + "mov QWORD PTR [rdi+848], rax", + "mov QWORD PTR [rdi+856], rax", + "mov QWORD PTR [rdi+864], rax", + "mov QWORD PTR [rdi+872], rax", + "mov QWORD PTR [rdi+880], rax", + "mov QWORD PTR [rdi+888], rax", + "mov QWORD PTR [rdi+896], rax", + "mov QWORD PTR [rdi+904], rax", + "mov QWORD PTR [rdi+912], rax", + "mov QWORD PTR [rdi+920], rax", + "mov QWORD PTR [rdi+928], rax", + "mov QWORD PTR [rdi+936], rax", + "mov QWORD PTR [rdi+944], rax", + "mov QWORD PTR [rdi+952], rax", + "mov QWORD PTR [rdi+960], rax", + "mov QWORD PTR [rdi+968], rax", + "mov QWORD PTR [rdi+976], rax", + "mov QWORD PTR [rdi+984], rax", + "mov QWORD PTR [rdi+992], rax", + "mov QWORD PTR [rdi+1000], rax", + "mov QWORD PTR [rdi+1008], rax", + "mov QWORD PTR [rdi+1016], rax", + "mov rdi, QWORD PTR [rbp+248]", + "add rdi, 5120", + "mov rax, QWORD PTR [rbp]", + "mov QWORD PTR [rdi], rax", + "mov QWORD PTR [rdi+8], rbx", + "mov QWORD PTR [rdi+16], r14", + "mov rax, QWORD PTR [rbp+240]", + "mov QWORD PTR [rdi+24], rax", + "mov rax, QWORD PTR [rbp+72]", + "mov QWORD PTR [rdi+32], rax", + "mov rax, QWORD PTR [rbp+112]", + "mov QWORD PTR [rdi+40], rax", + "mov rax, QWORD PTR [rbp+8]", + "mov QWORD PTR [rdi+48], rax", + "mov rcx, QWORD PTR [rbp+248]", + "mov rdi, rcx", + "add rdi, 7168", + "mov rsi, rcx", + "add rsi, 5120", + "mov rdx, rcx", + "add rdx, 4096", + "call {vg_argon2_compress}", + "mov rcx, QWORD PTR [rbp+248]", + "mov rdi, rcx", + "add rdi, 7168", + "mov rsi, rcx", + "add rsi, 4096", + "mov rdx, rcx", + "add rdx, 6144", + "call {vg_argon2_compress}", + "jmp 214f", + "213:", + "214:", + "mov rcx, QWORD PTR [rbp+248]", + "mov rax, r15", + "and rax, 127", + "mov rdi, QWORD PTR [rcx+rax*8+6144]", + "jmp 212f", + "211:", + "mov r8, QWORD PTR [rbp+232]", + "mov rax, r14", + "mul r13", + "mov rcx, rax", + "add rcx, r15", + "cmp rcx, 0", + "je 215f", + "mov rdi, rcx", + "jmp 216f", + "215:", + "mov rdi, r12", + "216:", + "sub rdi, 1", + "mov rcx, rdi", + "mov rax, rbx", + "mul r12", + "add rax, rcx", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, r8", + "mov rdi, QWORD PTR [rax]", + "212:", + "mov rsi, QWORD PTR [rbp+184]", + "mov r11, rdi", + "shr rdi, 32", + "mov r8d, 0", + "mov r9d, 0", + "mov eax, 0", + "cmp rax, 0", + "mov rcx, rdi", + "shr rcx, 32", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 31", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 30", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 29", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 28", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 27", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 26", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 25", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 24", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 23", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 22", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 21", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 20", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 19", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 18", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 17", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 16", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 15", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 14", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 13", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 12", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 11", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 10", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 9", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 8", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 7", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 6", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 5", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 4", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 3", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 2", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov rcx, rdi", + "shr rcx, 1", + "adc r8, r8", + "mov r10, r8", + "sub r8, rsi", + "sbb rax, rax", + "xor r10, r8", + "and r10, rax", + "xor r8, r10", + "add rax, 1", + "add r9, r9", + "add r9, rax", + "mov r9, QWORD PTR [rbp]", + "mov rax, r9", + "or rax, r14", + "je 217f", + "jmp 218f", + "217:", + "mov r8, rbx", + "218:", + "mov rdi, r8", + "mov rsi, rbx", + "cmp r9, 0", + "je 219f", + "mov rax, r14", + "add rax, 1", + "mul r13", + "mov r10, rax", + "cmp r14, 3", + "je 221f", + "jmp 222f", + "221:", + "mov r10, 0", + "222:", + "jmp 220f", + "219:", + "mov r10, 0", + "220:", + "cmp r9, 0", + "je 223f", + "mov rax, r12", + "sub rax, r13", + "mov rcx, rax", + "mov rdx, rax", + "add rdx, r15", + "sub rdx, 1", + "jmp 224f", + "223:", + "mov rax, r13", + "mul r14", + "mov rcx, rax", + "mov rdx, rax", + "add rdx, r15", + "sub rdx, 1", + "224:", + "mov r8, r15", + "sub r8, 1", + "sbb r9, r9", + "add rcx, r9", + "mov rax, rdi", + "xor rax, rsi", + "sub rax, 1", + "sbb rax, rax", + "mov r8, rcx", + "xor rdx, rcx", + "and rdx, rax", + "xor r8, rdx", + "mov r9, rdi", + "mov rdi, r11", + "mov rsi, r8", + "mov eax, edi", + "mul rax", + "shr rax, 32", + "mul rsi", + "shr rax, 32", + "mov rcx, rsi", + "sub rcx, 1", + "sub rcx, rax", + "mov rax, rcx", + "mov rdi, rax", + "add rdi, r10", + "mov rsi, r12", + "mov r10, rdi", + "sub rdi, rsi", + "sbb rax, rax", + "xor r10, rdi", + "and r10, rax", + "xor rdi, r10", + "mov r8, QWORD PTR [rbp+232]", + "mov rsi, rdi", + "mov rax, r14", + "mul r13", + "mov rcx, rax", + "add rcx, r15", + "cmp rcx, 0", + "je 225f", + "mov rdi, rcx", + "jmp 226f", + "225:", + "mov rdi, r12", + "226:", + "sub rdi, 1", + "mov rax, rbx", + "mul r12", + "add rax, rcx", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, r8", + "mov r10, rax", + "mov rcx, rdi", + "mov rax, rbx", + "mul r12", + "add rax, rcx", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, r8", + "mov r11, rax", + "mov rcx, rsi", + "mov rax, r9", + "mul r12", + "add rax, rcx", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, r8", + "mov rsi, rax", + "mov rdi, r11", + "mov QWORD PTR [rbp+16], r10", + "mov rcx, QWORD PTR [rbp+248]", + "mov rdx, rcx", + "add rdx, 4096", + "call {vg_argon2_compress}", + "mov rdi, QWORD PTR [rbp+16]", + "mov rsi, QWORD PTR [rbp+248]", + "add rsi, 4096", + "mov r9, QWORD PTR [rbp]", + "cmp r9, 0", + "je 227f", + "mov rax, QWORD PTR [rsi]", + "xor rax, QWORD PTR [rdi]", + "mov QWORD PTR [rdi], rax", + "mov rax, QWORD PTR [rsi+8]", + "xor rax, QWORD PTR [rdi+8]", + "mov QWORD PTR [rdi+8], rax", + "mov rax, QWORD PTR [rsi+16]", + "xor rax, QWORD PTR [rdi+16]", + "mov QWORD PTR [rdi+16], rax", + "mov rax, QWORD PTR [rsi+24]", + "xor rax, QWORD PTR [rdi+24]", + "mov QWORD PTR [rdi+24], rax", + "mov rax, QWORD PTR [rsi+32]", + "xor rax, QWORD PTR [rdi+32]", + "mov QWORD PTR [rdi+32], rax", + "mov rax, QWORD PTR [rsi+40]", + "xor rax, QWORD PTR [rdi+40]", + "mov QWORD PTR [rdi+40], rax", + "mov rax, QWORD PTR [rsi+48]", + "xor rax, QWORD PTR [rdi+48]", + "mov QWORD PTR [rdi+48], rax", + "mov rax, QWORD PTR [rsi+56]", + "xor rax, QWORD PTR [rdi+56]", + "mov QWORD PTR [rdi+56], rax", + "mov rax, QWORD PTR [rsi+64]", + "xor rax, QWORD PTR [rdi+64]", + "mov QWORD PTR [rdi+64], rax", + "mov rax, QWORD PTR [rsi+72]", + "xor rax, QWORD PTR [rdi+72]", + "mov QWORD PTR [rdi+72], rax", + "mov rax, QWORD PTR [rsi+80]", + "xor rax, QWORD PTR [rdi+80]", + "mov QWORD PTR [rdi+80], rax", + "mov rax, QWORD PTR [rsi+88]", + "xor rax, QWORD PTR [rdi+88]", + "mov QWORD PTR [rdi+88], rax", + "mov rax, QWORD PTR [rsi+96]", + "xor rax, QWORD PTR [rdi+96]", + "mov QWORD PTR [rdi+96], rax", + "mov rax, QWORD PTR [rsi+104]", + "xor rax, QWORD PTR [rdi+104]", + "mov QWORD PTR [rdi+104], rax", + "mov rax, QWORD PTR [rsi+112]", + "xor rax, QWORD PTR [rdi+112]", + "mov QWORD PTR [rdi+112], rax", + "mov rax, QWORD PTR [rsi+120]", + "xor rax, QWORD PTR [rdi+120]", + "mov QWORD PTR [rdi+120], rax", + "mov rax, QWORD PTR [rsi+128]", + "xor rax, QWORD PTR [rdi+128]", + "mov QWORD PTR [rdi+128], rax", + "mov rax, QWORD PTR [rsi+136]", + "xor rax, QWORD PTR [rdi+136]", + "mov QWORD PTR [rdi+136], rax", + "mov rax, QWORD PTR [rsi+144]", + "xor rax, QWORD PTR [rdi+144]", + "mov QWORD PTR [rdi+144], rax", + "mov rax, QWORD PTR [rsi+152]", + "xor rax, QWORD PTR [rdi+152]", + "mov QWORD PTR [rdi+152], rax", + "mov rax, QWORD PTR [rsi+160]", + "xor rax, QWORD PTR [rdi+160]", + "mov QWORD PTR [rdi+160], rax", + "mov rax, QWORD PTR [rsi+168]", + "xor rax, QWORD PTR [rdi+168]", + "mov QWORD PTR [rdi+168], rax", + "mov rax, QWORD PTR [rsi+176]", + "xor rax, QWORD PTR [rdi+176]", + "mov QWORD PTR [rdi+176], rax", + "mov rax, QWORD PTR [rsi+184]", + "xor rax, QWORD PTR [rdi+184]", + "mov QWORD PTR [rdi+184], rax", + "mov rax, QWORD PTR [rsi+192]", + "xor rax, QWORD PTR [rdi+192]", + "mov QWORD PTR [rdi+192], rax", + "mov rax, QWORD PTR [rsi+200]", + "xor rax, QWORD PTR [rdi+200]", + "mov QWORD PTR [rdi+200], rax", + "mov rax, QWORD PTR [rsi+208]", + "xor rax, QWORD PTR [rdi+208]", + "mov QWORD PTR [rdi+208], rax", + "mov rax, QWORD PTR [rsi+216]", + "xor rax, QWORD PTR [rdi+216]", + "mov QWORD PTR [rdi+216], rax", + "mov rax, QWORD PTR [rsi+224]", + "xor rax, QWORD PTR [rdi+224]", + "mov QWORD PTR [rdi+224], rax", + "mov rax, QWORD PTR [rsi+232]", + "xor rax, QWORD PTR [rdi+232]", + "mov QWORD PTR [rdi+232], rax", + "mov rax, QWORD PTR [rsi+240]", + "xor rax, QWORD PTR [rdi+240]", + "mov QWORD PTR [rdi+240], rax", + "mov rax, QWORD PTR [rsi+248]", + "xor rax, QWORD PTR [rdi+248]", + "mov QWORD PTR [rdi+248], rax", + "mov rax, QWORD PTR [rsi+256]", + "xor rax, QWORD PTR [rdi+256]", + "mov QWORD PTR [rdi+256], rax", + "mov rax, QWORD PTR [rsi+264]", + "xor rax, QWORD PTR [rdi+264]", + "mov QWORD PTR [rdi+264], rax", + "mov rax, QWORD PTR [rsi+272]", + "xor rax, QWORD PTR [rdi+272]", + "mov QWORD PTR [rdi+272], rax", + "mov rax, QWORD PTR [rsi+280]", + "xor rax, QWORD PTR [rdi+280]", + "mov QWORD PTR [rdi+280], rax", + "mov rax, QWORD PTR [rsi+288]", + "xor rax, QWORD PTR [rdi+288]", + "mov QWORD PTR [rdi+288], rax", + "mov rax, QWORD PTR [rsi+296]", + "xor rax, QWORD PTR [rdi+296]", + "mov QWORD PTR [rdi+296], rax", + "mov rax, QWORD PTR [rsi+304]", + "xor rax, QWORD PTR [rdi+304]", + "mov QWORD PTR [rdi+304], rax", + "mov rax, QWORD PTR [rsi+312]", + "xor rax, QWORD PTR [rdi+312]", + "mov QWORD PTR [rdi+312], rax", + "mov rax, QWORD PTR [rsi+320]", + "xor rax, QWORD PTR [rdi+320]", + "mov QWORD PTR [rdi+320], rax", + "mov rax, QWORD PTR [rsi+328]", + "xor rax, QWORD PTR [rdi+328]", + "mov QWORD PTR [rdi+328], rax", + "mov rax, QWORD PTR [rsi+336]", + "xor rax, QWORD PTR [rdi+336]", + "mov QWORD PTR [rdi+336], rax", + "mov rax, QWORD PTR [rsi+344]", + "xor rax, QWORD PTR [rdi+344]", + "mov QWORD PTR [rdi+344], rax", + "mov rax, QWORD PTR [rsi+352]", + "xor rax, QWORD PTR [rdi+352]", + "mov QWORD PTR [rdi+352], rax", + "mov rax, QWORD PTR [rsi+360]", + "xor rax, QWORD PTR [rdi+360]", + "mov QWORD PTR [rdi+360], rax", + "mov rax, QWORD PTR [rsi+368]", + "xor rax, QWORD PTR [rdi+368]", + "mov QWORD PTR [rdi+368], rax", + "mov rax, QWORD PTR [rsi+376]", + "xor rax, QWORD PTR [rdi+376]", + "mov QWORD PTR [rdi+376], rax", + "mov rax, QWORD PTR [rsi+384]", + "xor rax, QWORD PTR [rdi+384]", + "mov QWORD PTR [rdi+384], rax", + "mov rax, QWORD PTR [rsi+392]", + "xor rax, QWORD PTR [rdi+392]", + "mov QWORD PTR [rdi+392], rax", + "mov rax, QWORD PTR [rsi+400]", + "xor rax, QWORD PTR [rdi+400]", + "mov QWORD PTR [rdi+400], rax", + "mov rax, QWORD PTR [rsi+408]", + "xor rax, QWORD PTR [rdi+408]", + "mov QWORD PTR [rdi+408], rax", + "mov rax, QWORD PTR [rsi+416]", + "xor rax, QWORD PTR [rdi+416]", + "mov QWORD PTR [rdi+416], rax", + "mov rax, QWORD PTR [rsi+424]", + "xor rax, QWORD PTR [rdi+424]", + "mov QWORD PTR [rdi+424], rax", + "mov rax, QWORD PTR [rsi+432]", + "xor rax, QWORD PTR [rdi+432]", + "mov QWORD PTR [rdi+432], rax", + "mov rax, QWORD PTR [rsi+440]", + "xor rax, QWORD PTR [rdi+440]", + "mov QWORD PTR [rdi+440], rax", + "mov rax, QWORD PTR [rsi+448]", + "xor rax, QWORD PTR [rdi+448]", + "mov QWORD PTR [rdi+448], rax", + "mov rax, QWORD PTR [rsi+456]", + "xor rax, QWORD PTR [rdi+456]", + "mov QWORD PTR [rdi+456], rax", + "mov rax, QWORD PTR [rsi+464]", + "xor rax, QWORD PTR [rdi+464]", + "mov QWORD PTR [rdi+464], rax", + "mov rax, QWORD PTR [rsi+472]", + "xor rax, QWORD PTR [rdi+472]", + "mov QWORD PTR [rdi+472], rax", + "mov rax, QWORD PTR [rsi+480]", + "xor rax, QWORD PTR [rdi+480]", + "mov QWORD PTR [rdi+480], rax", + "mov rax, QWORD PTR [rsi+488]", + "xor rax, QWORD PTR [rdi+488]", + "mov QWORD PTR [rdi+488], rax", + "mov rax, QWORD PTR [rsi+496]", + "xor rax, QWORD PTR [rdi+496]", + "mov QWORD PTR [rdi+496], rax", + "mov rax, QWORD PTR [rsi+504]", + "xor rax, QWORD PTR [rdi+504]", + "mov QWORD PTR [rdi+504], rax", + "mov rax, QWORD PTR [rsi+512]", + "xor rax, QWORD PTR [rdi+512]", + "mov QWORD PTR [rdi+512], rax", + "mov rax, QWORD PTR [rsi+520]", + "xor rax, QWORD PTR [rdi+520]", + "mov QWORD PTR [rdi+520], rax", + "mov rax, QWORD PTR [rsi+528]", + "xor rax, QWORD PTR [rdi+528]", + "mov QWORD PTR [rdi+528], rax", + "mov rax, QWORD PTR [rsi+536]", + "xor rax, QWORD PTR [rdi+536]", + "mov QWORD PTR [rdi+536], rax", + "mov rax, QWORD PTR [rsi+544]", + "xor rax, QWORD PTR [rdi+544]", + "mov QWORD PTR [rdi+544], rax", + "mov rax, QWORD PTR [rsi+552]", + "xor rax, QWORD PTR [rdi+552]", + "mov QWORD PTR [rdi+552], rax", + "mov rax, QWORD PTR [rsi+560]", + "xor rax, QWORD PTR [rdi+560]", + "mov QWORD PTR [rdi+560], rax", + "mov rax, QWORD PTR [rsi+568]", + "xor rax, QWORD PTR [rdi+568]", + "mov QWORD PTR [rdi+568], rax", + "mov rax, QWORD PTR [rsi+576]", + "xor rax, QWORD PTR [rdi+576]", + "mov QWORD PTR [rdi+576], rax", + "mov rax, QWORD PTR [rsi+584]", + "xor rax, QWORD PTR [rdi+584]", + "mov QWORD PTR [rdi+584], rax", + "mov rax, QWORD PTR [rsi+592]", + "xor rax, QWORD PTR [rdi+592]", + "mov QWORD PTR [rdi+592], rax", + "mov rax, QWORD PTR [rsi+600]", + "xor rax, QWORD PTR [rdi+600]", + "mov QWORD PTR [rdi+600], rax", + "mov rax, QWORD PTR [rsi+608]", + "xor rax, QWORD PTR [rdi+608]", + "mov QWORD PTR [rdi+608], rax", + "mov rax, QWORD PTR [rsi+616]", + "xor rax, QWORD PTR [rdi+616]", + "mov QWORD PTR [rdi+616], rax", + "mov rax, QWORD PTR [rsi+624]", + "xor rax, QWORD PTR [rdi+624]", + "mov QWORD PTR [rdi+624], rax", + "mov rax, QWORD PTR [rsi+632]", + "xor rax, QWORD PTR [rdi+632]", + "mov QWORD PTR [rdi+632], rax", + "mov rax, QWORD PTR [rsi+640]", + "xor rax, QWORD PTR [rdi+640]", + "mov QWORD PTR [rdi+640], rax", + "mov rax, QWORD PTR [rsi+648]", + "xor rax, QWORD PTR [rdi+648]", + "mov QWORD PTR [rdi+648], rax", + "mov rax, QWORD PTR [rsi+656]", + "xor rax, QWORD PTR [rdi+656]", + "mov QWORD PTR [rdi+656], rax", + "mov rax, QWORD PTR [rsi+664]", + "xor rax, QWORD PTR [rdi+664]", + "mov QWORD PTR [rdi+664], rax", + "mov rax, QWORD PTR [rsi+672]", + "xor rax, QWORD PTR [rdi+672]", + "mov QWORD PTR [rdi+672], rax", + "mov rax, QWORD PTR [rsi+680]", + "xor rax, QWORD PTR [rdi+680]", + "mov QWORD PTR [rdi+680], rax", + "mov rax, QWORD PTR [rsi+688]", + "xor rax, QWORD PTR [rdi+688]", + "mov QWORD PTR [rdi+688], rax", + "mov rax, QWORD PTR [rsi+696]", + "xor rax, QWORD PTR [rdi+696]", + "mov QWORD PTR [rdi+696], rax", + "mov rax, QWORD PTR [rsi+704]", + "xor rax, QWORD PTR [rdi+704]", + "mov QWORD PTR [rdi+704], rax", + "mov rax, QWORD PTR [rsi+712]", + "xor rax, QWORD PTR [rdi+712]", + "mov QWORD PTR [rdi+712], rax", + "mov rax, QWORD PTR [rsi+720]", + "xor rax, QWORD PTR [rdi+720]", + "mov QWORD PTR [rdi+720], rax", + "mov rax, QWORD PTR [rsi+728]", + "xor rax, QWORD PTR [rdi+728]", + "mov QWORD PTR [rdi+728], rax", + "mov rax, QWORD PTR [rsi+736]", + "xor rax, QWORD PTR [rdi+736]", + "mov QWORD PTR [rdi+736], rax", + "mov rax, QWORD PTR [rsi+744]", + "xor rax, QWORD PTR [rdi+744]", + "mov QWORD PTR [rdi+744], rax", + "mov rax, QWORD PTR [rsi+752]", + "xor rax, QWORD PTR [rdi+752]", + "mov QWORD PTR [rdi+752], rax", + "mov rax, QWORD PTR [rsi+760]", + "xor rax, QWORD PTR [rdi+760]", + "mov QWORD PTR [rdi+760], rax", + "mov rax, QWORD PTR [rsi+768]", + "xor rax, QWORD PTR [rdi+768]", + "mov QWORD PTR [rdi+768], rax", + "mov rax, QWORD PTR [rsi+776]", + "xor rax, QWORD PTR [rdi+776]", + "mov QWORD PTR [rdi+776], rax", + "mov rax, QWORD PTR [rsi+784]", + "xor rax, QWORD PTR [rdi+784]", + "mov QWORD PTR [rdi+784], rax", + "mov rax, QWORD PTR [rsi+792]", + "xor rax, QWORD PTR [rdi+792]", + "mov QWORD PTR [rdi+792], rax", + "mov rax, QWORD PTR [rsi+800]", + "xor rax, QWORD PTR [rdi+800]", + "mov QWORD PTR [rdi+800], rax", + "mov rax, QWORD PTR [rsi+808]", + "xor rax, QWORD PTR [rdi+808]", + "mov QWORD PTR [rdi+808], rax", + "mov rax, QWORD PTR [rsi+816]", + "xor rax, QWORD PTR [rdi+816]", + "mov QWORD PTR [rdi+816], rax", + "mov rax, QWORD PTR [rsi+824]", + "xor rax, QWORD PTR [rdi+824]", + "mov QWORD PTR [rdi+824], rax", + "mov rax, QWORD PTR [rsi+832]", + "xor rax, QWORD PTR [rdi+832]", + "mov QWORD PTR [rdi+832], rax", + "mov rax, QWORD PTR [rsi+840]", + "xor rax, QWORD PTR [rdi+840]", + "mov QWORD PTR [rdi+840], rax", + "mov rax, QWORD PTR [rsi+848]", + "xor rax, QWORD PTR [rdi+848]", + "mov QWORD PTR [rdi+848], rax", + "mov rax, QWORD PTR [rsi+856]", + "xor rax, QWORD PTR [rdi+856]", + "mov QWORD PTR [rdi+856], rax", + "mov rax, QWORD PTR [rsi+864]", + "xor rax, QWORD PTR [rdi+864]", + "mov QWORD PTR [rdi+864], rax", + "mov rax, QWORD PTR [rsi+872]", + "xor rax, QWORD PTR [rdi+872]", + "mov QWORD PTR [rdi+872], rax", + "mov rax, QWORD PTR [rsi+880]", + "xor rax, QWORD PTR [rdi+880]", + "mov QWORD PTR [rdi+880], rax", + "mov rax, QWORD PTR [rsi+888]", + "xor rax, QWORD PTR [rdi+888]", + "mov QWORD PTR [rdi+888], rax", + "mov rax, QWORD PTR [rsi+896]", + "xor rax, QWORD PTR [rdi+896]", + "mov QWORD PTR [rdi+896], rax", + "mov rax, QWORD PTR [rsi+904]", + "xor rax, QWORD PTR [rdi+904]", + "mov QWORD PTR [rdi+904], rax", + "mov rax, QWORD PTR [rsi+912]", + "xor rax, QWORD PTR [rdi+912]", + "mov QWORD PTR [rdi+912], rax", + "mov rax, QWORD PTR [rsi+920]", + "xor rax, QWORD PTR [rdi+920]", + "mov QWORD PTR [rdi+920], rax", + "mov rax, QWORD PTR [rsi+928]", + "xor rax, QWORD PTR [rdi+928]", + "mov QWORD PTR [rdi+928], rax", + "mov rax, QWORD PTR [rsi+936]", + "xor rax, QWORD PTR [rdi+936]", + "mov QWORD PTR [rdi+936], rax", + "mov rax, QWORD PTR [rsi+944]", + "xor rax, QWORD PTR [rdi+944]", + "mov QWORD PTR [rdi+944], rax", + "mov rax, QWORD PTR [rsi+952]", + "xor rax, QWORD PTR [rdi+952]", + "mov QWORD PTR [rdi+952], rax", + "mov rax, QWORD PTR [rsi+960]", + "xor rax, QWORD PTR [rdi+960]", + "mov QWORD PTR [rdi+960], rax", + "mov rax, QWORD PTR [rsi+968]", + "xor rax, QWORD PTR [rdi+968]", + "mov QWORD PTR [rdi+968], rax", + "mov rax, QWORD PTR [rsi+976]", + "xor rax, QWORD PTR [rdi+976]", + "mov QWORD PTR [rdi+976], rax", + "mov rax, QWORD PTR [rsi+984]", + "xor rax, QWORD PTR [rdi+984]", + "mov QWORD PTR [rdi+984], rax", + "mov rax, QWORD PTR [rsi+992]", + "xor rax, QWORD PTR [rdi+992]", + "mov QWORD PTR [rdi+992], rax", + "mov rax, QWORD PTR [rsi+1000]", + "xor rax, QWORD PTR [rdi+1000]", + "mov QWORD PTR [rdi+1000], rax", + "mov rax, QWORD PTR [rsi+1008]", + "xor rax, QWORD PTR [rdi+1008]", + "mov QWORD PTR [rdi+1008], rax", + "mov rax, QWORD PTR [rsi+1016]", + "xor rax, QWORD PTR [rdi+1016]", + "mov QWORD PTR [rdi+1016], rax", + "jmp 228f", + "227:", + "mov rax, QWORD PTR [rsi]", + "mov QWORD PTR [rdi], rax", + "mov rax, QWORD PTR [rsi+8]", + "mov QWORD PTR [rdi+8], rax", + "mov rax, QWORD PTR [rsi+16]", + "mov QWORD PTR [rdi+16], rax", + "mov rax, QWORD PTR [rsi+24]", + "mov QWORD PTR [rdi+24], rax", + "mov rax, QWORD PTR [rsi+32]", + "mov QWORD PTR [rdi+32], rax", + "mov rax, QWORD PTR [rsi+40]", + "mov QWORD PTR [rdi+40], rax", + "mov rax, QWORD PTR [rsi+48]", + "mov QWORD PTR [rdi+48], rax", + "mov rax, QWORD PTR [rsi+56]", + "mov QWORD PTR [rdi+56], rax", + "mov rax, QWORD PTR [rsi+64]", + "mov QWORD PTR [rdi+64], rax", + "mov rax, QWORD PTR [rsi+72]", + "mov QWORD PTR [rdi+72], rax", + "mov rax, QWORD PTR [rsi+80]", + "mov QWORD PTR [rdi+80], rax", + "mov rax, QWORD PTR [rsi+88]", + "mov QWORD PTR [rdi+88], rax", + "mov rax, QWORD PTR [rsi+96]", + "mov QWORD PTR [rdi+96], rax", + "mov rax, QWORD PTR [rsi+104]", + "mov QWORD PTR [rdi+104], rax", + "mov rax, QWORD PTR [rsi+112]", + "mov QWORD PTR [rdi+112], rax", + "mov rax, QWORD PTR [rsi+120]", + "mov QWORD PTR [rdi+120], rax", + "mov rax, QWORD PTR [rsi+128]", + "mov QWORD PTR [rdi+128], rax", + "mov rax, QWORD PTR [rsi+136]", + "mov QWORD PTR [rdi+136], rax", + "mov rax, QWORD PTR [rsi+144]", + "mov QWORD PTR [rdi+144], rax", + "mov rax, QWORD PTR [rsi+152]", + "mov QWORD PTR [rdi+152], rax", + "mov rax, QWORD PTR [rsi+160]", + "mov QWORD PTR [rdi+160], rax", + "mov rax, QWORD PTR [rsi+168]", + "mov QWORD PTR [rdi+168], rax", + "mov rax, QWORD PTR [rsi+176]", + "mov QWORD PTR [rdi+176], rax", + "mov rax, QWORD PTR [rsi+184]", + "mov QWORD PTR [rdi+184], rax", + "mov rax, QWORD PTR [rsi+192]", + "mov QWORD PTR [rdi+192], rax", + "mov rax, QWORD PTR [rsi+200]", + "mov QWORD PTR [rdi+200], rax", + "mov rax, QWORD PTR [rsi+208]", + "mov QWORD PTR [rdi+208], rax", + "mov rax, QWORD PTR [rsi+216]", + "mov QWORD PTR [rdi+216], rax", + "mov rax, QWORD PTR [rsi+224]", + "mov QWORD PTR [rdi+224], rax", + "mov rax, QWORD PTR [rsi+232]", + "mov QWORD PTR [rdi+232], rax", + "mov rax, QWORD PTR [rsi+240]", + "mov QWORD PTR [rdi+240], rax", + "mov rax, QWORD PTR [rsi+248]", + "mov QWORD PTR [rdi+248], rax", + "mov rax, QWORD PTR [rsi+256]", + "mov QWORD PTR [rdi+256], rax", + "mov rax, QWORD PTR [rsi+264]", + "mov QWORD PTR [rdi+264], rax", + "mov rax, QWORD PTR [rsi+272]", + "mov QWORD PTR [rdi+272], rax", + "mov rax, QWORD PTR [rsi+280]", + "mov QWORD PTR [rdi+280], rax", + "mov rax, QWORD PTR [rsi+288]", + "mov QWORD PTR [rdi+288], rax", + "mov rax, QWORD PTR [rsi+296]", + "mov QWORD PTR [rdi+296], rax", + "mov rax, QWORD PTR [rsi+304]", + "mov QWORD PTR [rdi+304], rax", + "mov rax, QWORD PTR [rsi+312]", + "mov QWORD PTR [rdi+312], rax", + "mov rax, QWORD PTR [rsi+320]", + "mov QWORD PTR [rdi+320], rax", + "mov rax, QWORD PTR [rsi+328]", + "mov QWORD PTR [rdi+328], rax", + "mov rax, QWORD PTR [rsi+336]", + "mov QWORD PTR [rdi+336], rax", + "mov rax, QWORD PTR [rsi+344]", + "mov QWORD PTR [rdi+344], rax", + "mov rax, QWORD PTR [rsi+352]", + "mov QWORD PTR [rdi+352], rax", + "mov rax, QWORD PTR [rsi+360]", + "mov QWORD PTR [rdi+360], rax", + "mov rax, QWORD PTR [rsi+368]", + "mov QWORD PTR [rdi+368], rax", + "mov rax, QWORD PTR [rsi+376]", + "mov QWORD PTR [rdi+376], rax", + "mov rax, QWORD PTR [rsi+384]", + "mov QWORD PTR [rdi+384], rax", + "mov rax, QWORD PTR [rsi+392]", + "mov QWORD PTR [rdi+392], rax", + "mov rax, QWORD PTR [rsi+400]", + "mov QWORD PTR [rdi+400], rax", + "mov rax, QWORD PTR [rsi+408]", + "mov QWORD PTR [rdi+408], rax", + "mov rax, QWORD PTR [rsi+416]", + "mov QWORD PTR [rdi+416], rax", + "mov rax, QWORD PTR [rsi+424]", + "mov QWORD PTR [rdi+424], rax", + "mov rax, QWORD PTR [rsi+432]", + "mov QWORD PTR [rdi+432], rax", + "mov rax, QWORD PTR [rsi+440]", + "mov QWORD PTR [rdi+440], rax", + "mov rax, QWORD PTR [rsi+448]", + "mov QWORD PTR [rdi+448], rax", + "mov rax, QWORD PTR [rsi+456]", + "mov QWORD PTR [rdi+456], rax", + "mov rax, QWORD PTR [rsi+464]", + "mov QWORD PTR [rdi+464], rax", + "mov rax, QWORD PTR [rsi+472]", + "mov QWORD PTR [rdi+472], rax", + "mov rax, QWORD PTR [rsi+480]", + "mov QWORD PTR [rdi+480], rax", + "mov rax, QWORD PTR [rsi+488]", + "mov QWORD PTR [rdi+488], rax", + "mov rax, QWORD PTR [rsi+496]", + "mov QWORD PTR [rdi+496], rax", + "mov rax, QWORD PTR [rsi+504]", + "mov QWORD PTR [rdi+504], rax", + "mov rax, QWORD PTR [rsi+512]", + "mov QWORD PTR [rdi+512], rax", + "mov rax, QWORD PTR [rsi+520]", + "mov QWORD PTR [rdi+520], rax", + "mov rax, QWORD PTR [rsi+528]", + "mov QWORD PTR [rdi+528], rax", + "mov rax, QWORD PTR [rsi+536]", + "mov QWORD PTR [rdi+536], rax", + "mov rax, QWORD PTR [rsi+544]", + "mov QWORD PTR [rdi+544], rax", + "mov rax, QWORD PTR [rsi+552]", + "mov QWORD PTR [rdi+552], rax", + "mov rax, QWORD PTR [rsi+560]", + "mov QWORD PTR [rdi+560], rax", + "mov rax, QWORD PTR [rsi+568]", + "mov QWORD PTR [rdi+568], rax", + "mov rax, QWORD PTR [rsi+576]", + "mov QWORD PTR [rdi+576], rax", + "mov rax, QWORD PTR [rsi+584]", + "mov QWORD PTR [rdi+584], rax", + "mov rax, QWORD PTR [rsi+592]", + "mov QWORD PTR [rdi+592], rax", + "mov rax, QWORD PTR [rsi+600]", + "mov QWORD PTR [rdi+600], rax", + "mov rax, QWORD PTR [rsi+608]", + "mov QWORD PTR [rdi+608], rax", + "mov rax, QWORD PTR [rsi+616]", + "mov QWORD PTR [rdi+616], rax", + "mov rax, QWORD PTR [rsi+624]", + "mov QWORD PTR [rdi+624], rax", + "mov rax, QWORD PTR [rsi+632]", + "mov QWORD PTR [rdi+632], rax", + "mov rax, QWORD PTR [rsi+640]", + "mov QWORD PTR [rdi+640], rax", + "mov rax, QWORD PTR [rsi+648]", + "mov QWORD PTR [rdi+648], rax", + "mov rax, QWORD PTR [rsi+656]", + "mov QWORD PTR [rdi+656], rax", + "mov rax, QWORD PTR [rsi+664]", + "mov QWORD PTR [rdi+664], rax", + "mov rax, QWORD PTR [rsi+672]", + "mov QWORD PTR [rdi+672], rax", + "mov rax, QWORD PTR [rsi+680]", + "mov QWORD PTR [rdi+680], rax", + "mov rax, QWORD PTR [rsi+688]", + "mov QWORD PTR [rdi+688], rax", + "mov rax, QWORD PTR [rsi+696]", + "mov QWORD PTR [rdi+696], rax", + "mov rax, QWORD PTR [rsi+704]", + "mov QWORD PTR [rdi+704], rax", + "mov rax, QWORD PTR [rsi+712]", + "mov QWORD PTR [rdi+712], rax", + "mov rax, QWORD PTR [rsi+720]", + "mov QWORD PTR [rdi+720], rax", + "mov rax, QWORD PTR [rsi+728]", + "mov QWORD PTR [rdi+728], rax", + "mov rax, QWORD PTR [rsi+736]", + "mov QWORD PTR [rdi+736], rax", + "mov rax, QWORD PTR [rsi+744]", + "mov QWORD PTR [rdi+744], rax", + "mov rax, QWORD PTR [rsi+752]", + "mov QWORD PTR [rdi+752], rax", + "mov rax, QWORD PTR [rsi+760]", + "mov QWORD PTR [rdi+760], rax", + "mov rax, QWORD PTR [rsi+768]", + "mov QWORD PTR [rdi+768], rax", + "mov rax, QWORD PTR [rsi+776]", + "mov QWORD PTR [rdi+776], rax", + "mov rax, QWORD PTR [rsi+784]", + "mov QWORD PTR [rdi+784], rax", + "mov rax, QWORD PTR [rsi+792]", + "mov QWORD PTR [rdi+792], rax", + "mov rax, QWORD PTR [rsi+800]", + "mov QWORD PTR [rdi+800], rax", + "mov rax, QWORD PTR [rsi+808]", + "mov QWORD PTR [rdi+808], rax", + "mov rax, QWORD PTR [rsi+816]", + "mov QWORD PTR [rdi+816], rax", + "mov rax, QWORD PTR [rsi+824]", + "mov QWORD PTR [rdi+824], rax", + "mov rax, QWORD PTR [rsi+832]", + "mov QWORD PTR [rdi+832], rax", + "mov rax, QWORD PTR [rsi+840]", + "mov QWORD PTR [rdi+840], rax", + "mov rax, QWORD PTR [rsi+848]", + "mov QWORD PTR [rdi+848], rax", + "mov rax, QWORD PTR [rsi+856]", + "mov QWORD PTR [rdi+856], rax", + "mov rax, QWORD PTR [rsi+864]", + "mov QWORD PTR [rdi+864], rax", + "mov rax, QWORD PTR [rsi+872]", + "mov QWORD PTR [rdi+872], rax", + "mov rax, QWORD PTR [rsi+880]", + "mov QWORD PTR [rdi+880], rax", + "mov rax, QWORD PTR [rsi+888]", + "mov QWORD PTR [rdi+888], rax", + "mov rax, QWORD PTR [rsi+896]", + "mov QWORD PTR [rdi+896], rax", + "mov rax, QWORD PTR [rsi+904]", + "mov QWORD PTR [rdi+904], rax", + "mov rax, QWORD PTR [rsi+912]", + "mov QWORD PTR [rdi+912], rax", + "mov rax, QWORD PTR [rsi+920]", + "mov QWORD PTR [rdi+920], rax", + "mov rax, QWORD PTR [rsi+928]", + "mov QWORD PTR [rdi+928], rax", + "mov rax, QWORD PTR [rsi+936]", + "mov QWORD PTR [rdi+936], rax", + "mov rax, QWORD PTR [rsi+944]", + "mov QWORD PTR [rdi+944], rax", + "mov rax, QWORD PTR [rsi+952]", + "mov QWORD PTR [rdi+952], rax", + "mov rax, QWORD PTR [rsi+960]", + "mov QWORD PTR [rdi+960], rax", + "mov rax, QWORD PTR [rsi+968]", + "mov QWORD PTR [rdi+968], rax", + "mov rax, QWORD PTR [rsi+976]", + "mov QWORD PTR [rdi+976], rax", + "mov rax, QWORD PTR [rsi+984]", + "mov QWORD PTR [rdi+984], rax", + "mov rax, QWORD PTR [rsi+992]", + "mov QWORD PTR [rdi+992], rax", + "mov rax, QWORD PTR [rsi+1000]", + "mov QWORD PTR [rdi+1000], rax", + "mov rax, QWORD PTR [rsi+1008]", + "mov QWORD PTR [rdi+1008], rax", + "mov rax, QWORD PTR [rsi+1016]", + "mov QWORD PTR [rdi+1016], rax", + "228:", + "add r15, 1", + "cmp r15, r13", + "jb 210b", + "29:", + "add rbx, 1", + "cmp rbx, QWORD PTR [rbp+184]", + "jb 25b", + "add r14, 1", + "cmp r14, 4", + "jb 24b", + "mov rax, QWORD PTR [rbp]", + "add rax, 1", + "mov QWORD PTR [rbp], rax", + "cmp rax, QWORD PTR [rbp+72]", + "jb 23b", + "mov rdi, QWORD PTR [rbp+232]", + "mov rbx, 0", + "mov rax, 0", + "mov QWORD PTR [rdi], rax", + "mov QWORD PTR [rdi+8], rax", + "mov QWORD PTR [rdi+16], rax", + "mov QWORD PTR [rdi+24], rax", + "mov QWORD PTR [rdi+32], rax", + "mov QWORD PTR [rdi+40], rax", + "mov QWORD PTR [rdi+48], rax", + "mov QWORD PTR [rdi+56], rax", + "mov QWORD PTR [rdi+64], rax", + "mov QWORD PTR [rdi+72], rax", + "mov QWORD PTR [rdi+80], rax", + "mov QWORD PTR [rdi+88], rax", + "mov QWORD PTR [rdi+96], rax", + "mov QWORD PTR [rdi+104], rax", + "mov QWORD PTR [rdi+112], rax", + "mov QWORD PTR [rdi+120], rax", + "mov QWORD PTR [rdi+128], rax", + "mov QWORD PTR [rdi+136], rax", + "mov QWORD PTR [rdi+144], rax", + "mov QWORD PTR [rdi+152], rax", + "mov QWORD PTR [rdi+160], rax", + "mov QWORD PTR [rdi+168], rax", + "mov QWORD PTR [rdi+176], rax", + "mov QWORD PTR [rdi+184], rax", + "mov QWORD PTR [rdi+192], rax", + "mov QWORD PTR [rdi+200], rax", + "mov QWORD PTR [rdi+208], rax", + "mov QWORD PTR [rdi+216], rax", + "mov QWORD PTR [rdi+224], rax", + "mov QWORD PTR [rdi+232], rax", + "mov QWORD PTR [rdi+240], rax", + "mov QWORD PTR [rdi+248], rax", + "mov QWORD PTR [rdi+256], rax", + "mov QWORD PTR [rdi+264], rax", + "mov QWORD PTR [rdi+272], rax", + "mov QWORD PTR [rdi+280], rax", + "mov QWORD PTR [rdi+288], rax", + "mov QWORD PTR [rdi+296], rax", + "mov QWORD PTR [rdi+304], rax", + "mov QWORD PTR [rdi+312], rax", + "mov QWORD PTR [rdi+320], rax", + "mov QWORD PTR [rdi+328], rax", + "mov QWORD PTR [rdi+336], rax", + "mov QWORD PTR [rdi+344], rax", + "mov QWORD PTR [rdi+352], rax", + "mov QWORD PTR [rdi+360], rax", + "mov QWORD PTR [rdi+368], rax", + "mov QWORD PTR [rdi+376], rax", + "mov QWORD PTR [rdi+384], rax", + "mov QWORD PTR [rdi+392], rax", + "mov QWORD PTR [rdi+400], rax", + "mov QWORD PTR [rdi+408], rax", + "mov QWORD PTR [rdi+416], rax", + "mov QWORD PTR [rdi+424], rax", + "mov QWORD PTR [rdi+432], rax", + "mov QWORD PTR [rdi+440], rax", + "mov QWORD PTR [rdi+448], rax", + "mov QWORD PTR [rdi+456], rax", + "mov QWORD PTR [rdi+464], rax", + "mov QWORD PTR [rdi+472], rax", + "mov QWORD PTR [rdi+480], rax", + "mov QWORD PTR [rdi+488], rax", + "mov QWORD PTR [rdi+496], rax", + "mov QWORD PTR [rdi+504], rax", + "mov QWORD PTR [rdi+512], rax", + "mov QWORD PTR [rdi+520], rax", + "mov QWORD PTR [rdi+528], rax", + "mov QWORD PTR [rdi+536], rax", + "mov QWORD PTR [rdi+544], rax", + "mov QWORD PTR [rdi+552], rax", + "mov QWORD PTR [rdi+560], rax", + "mov QWORD PTR [rdi+568], rax", + "mov QWORD PTR [rdi+576], rax", + "mov QWORD PTR [rdi+584], rax", + "mov QWORD PTR [rdi+592], rax", + "mov QWORD PTR [rdi+600], rax", + "mov QWORD PTR [rdi+608], rax", + "mov QWORD PTR [rdi+616], rax", + "mov QWORD PTR [rdi+624], rax", + "mov QWORD PTR [rdi+632], rax", + "mov QWORD PTR [rdi+640], rax", + "mov QWORD PTR [rdi+648], rax", + "mov QWORD PTR [rdi+656], rax", + "mov QWORD PTR [rdi+664], rax", + "mov QWORD PTR [rdi+672], rax", + "mov QWORD PTR [rdi+680], rax", + "mov QWORD PTR [rdi+688], rax", + "mov QWORD PTR [rdi+696], rax", + "mov QWORD PTR [rdi+704], rax", + "mov QWORD PTR [rdi+712], rax", + "mov QWORD PTR [rdi+720], rax", + "mov QWORD PTR [rdi+728], rax", + "mov QWORD PTR [rdi+736], rax", + "mov QWORD PTR [rdi+744], rax", + "mov QWORD PTR [rdi+752], rax", + "mov QWORD PTR [rdi+760], rax", + "mov QWORD PTR [rdi+768], rax", + "mov QWORD PTR [rdi+776], rax", + "mov QWORD PTR [rdi+784], rax", + "mov QWORD PTR [rdi+792], rax", + "mov QWORD PTR [rdi+800], rax", + "mov QWORD PTR [rdi+808], rax", + "mov QWORD PTR [rdi+816], rax", + "mov QWORD PTR [rdi+824], rax", + "mov QWORD PTR [rdi+832], rax", + "mov QWORD PTR [rdi+840], rax", + "mov QWORD PTR [rdi+848], rax", + "mov QWORD PTR [rdi+856], rax", + "mov QWORD PTR [rdi+864], rax", + "mov QWORD PTR [rdi+872], rax", + "mov QWORD PTR [rdi+880], rax", + "mov QWORD PTR [rdi+888], rax", + "mov QWORD PTR [rdi+896], rax", + "mov QWORD PTR [rdi+904], rax", + "mov QWORD PTR [rdi+912], rax", + "mov QWORD PTR [rdi+920], rax", + "mov QWORD PTR [rdi+928], rax", + "mov QWORD PTR [rdi+936], rax", + "mov QWORD PTR [rdi+944], rax", + "mov QWORD PTR [rdi+952], rax", + "mov QWORD PTR [rdi+960], rax", + "mov QWORD PTR [rdi+968], rax", + "mov QWORD PTR [rdi+976], rax", + "mov QWORD PTR [rdi+984], rax", + "mov QWORD PTR [rdi+992], rax", + "mov QWORD PTR [rdi+1000], rax", + "mov QWORD PTR [rdi+1008], rax", + "mov QWORD PTR [rdi+1016], rax", + "229:", + "mov r8, QWORD PTR [rbp+232]", + "mov rax, rbx", + "mov rcx, r12", + "sub rcx, 1", + "mul r12", + "add rax, rcx", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, rax", + "add rax, r8", + "mov rsi, rax", + "mov rdi, r8", + "mov rax, QWORD PTR [rsi]", + "xor rax, QWORD PTR [rdi]", + "mov QWORD PTR [rdi], rax", + "mov rax, QWORD PTR [rsi+8]", + "xor rax, QWORD PTR [rdi+8]", + "mov QWORD PTR [rdi+8], rax", + "mov rax, QWORD PTR [rsi+16]", + "xor rax, QWORD PTR [rdi+16]", + "mov QWORD PTR [rdi+16], rax", + "mov rax, QWORD PTR [rsi+24]", + "xor rax, QWORD PTR [rdi+24]", + "mov QWORD PTR [rdi+24], rax", + "mov rax, QWORD PTR [rsi+32]", + "xor rax, QWORD PTR [rdi+32]", + "mov QWORD PTR [rdi+32], rax", + "mov rax, QWORD PTR [rsi+40]", + "xor rax, QWORD PTR [rdi+40]", + "mov QWORD PTR [rdi+40], rax", + "mov rax, QWORD PTR [rsi+48]", + "xor rax, QWORD PTR [rdi+48]", + "mov QWORD PTR [rdi+48], rax", + "mov rax, QWORD PTR [rsi+56]", + "xor rax, QWORD PTR [rdi+56]", + "mov QWORD PTR [rdi+56], rax", + "mov rax, QWORD PTR [rsi+64]", + "xor rax, QWORD PTR [rdi+64]", + "mov QWORD PTR [rdi+64], rax", + "mov rax, QWORD PTR [rsi+72]", + "xor rax, QWORD PTR [rdi+72]", + "mov QWORD PTR [rdi+72], rax", + "mov rax, QWORD PTR [rsi+80]", + "xor rax, QWORD PTR [rdi+80]", + "mov QWORD PTR [rdi+80], rax", + "mov rax, QWORD PTR [rsi+88]", + "xor rax, QWORD PTR [rdi+88]", + "mov QWORD PTR [rdi+88], rax", + "mov rax, QWORD PTR [rsi+96]", + "xor rax, QWORD PTR [rdi+96]", + "mov QWORD PTR [rdi+96], rax", + "mov rax, QWORD PTR [rsi+104]", + "xor rax, QWORD PTR [rdi+104]", + "mov QWORD PTR [rdi+104], rax", + "mov rax, QWORD PTR [rsi+112]", + "xor rax, QWORD PTR [rdi+112]", + "mov QWORD PTR [rdi+112], rax", + "mov rax, QWORD PTR [rsi+120]", + "xor rax, QWORD PTR [rdi+120]", + "mov QWORD PTR [rdi+120], rax", + "mov rax, QWORD PTR [rsi+128]", + "xor rax, QWORD PTR [rdi+128]", + "mov QWORD PTR [rdi+128], rax", + "mov rax, QWORD PTR [rsi+136]", + "xor rax, QWORD PTR [rdi+136]", + "mov QWORD PTR [rdi+136], rax", + "mov rax, QWORD PTR [rsi+144]", + "xor rax, QWORD PTR [rdi+144]", + "mov QWORD PTR [rdi+144], rax", + "mov rax, QWORD PTR [rsi+152]", + "xor rax, QWORD PTR [rdi+152]", + "mov QWORD PTR [rdi+152], rax", + "mov rax, QWORD PTR [rsi+160]", + "xor rax, QWORD PTR [rdi+160]", + "mov QWORD PTR [rdi+160], rax", + "mov rax, QWORD PTR [rsi+168]", + "xor rax, QWORD PTR [rdi+168]", + "mov QWORD PTR [rdi+168], rax", + "mov rax, QWORD PTR [rsi+176]", + "xor rax, QWORD PTR [rdi+176]", + "mov QWORD PTR [rdi+176], rax", + "mov rax, QWORD PTR [rsi+184]", + "xor rax, QWORD PTR [rdi+184]", + "mov QWORD PTR [rdi+184], rax", + "mov rax, QWORD PTR [rsi+192]", + "xor rax, QWORD PTR [rdi+192]", + "mov QWORD PTR [rdi+192], rax", + "mov rax, QWORD PTR [rsi+200]", + "xor rax, QWORD PTR [rdi+200]", + "mov QWORD PTR [rdi+200], rax", + "mov rax, QWORD PTR [rsi+208]", + "xor rax, QWORD PTR [rdi+208]", + "mov QWORD PTR [rdi+208], rax", + "mov rax, QWORD PTR [rsi+216]", + "xor rax, QWORD PTR [rdi+216]", + "mov QWORD PTR [rdi+216], rax", + "mov rax, QWORD PTR [rsi+224]", + "xor rax, QWORD PTR [rdi+224]", + "mov QWORD PTR [rdi+224], rax", + "mov rax, QWORD PTR [rsi+232]", + "xor rax, QWORD PTR [rdi+232]", + "mov QWORD PTR [rdi+232], rax", + "mov rax, QWORD PTR [rsi+240]", + "xor rax, QWORD PTR [rdi+240]", + "mov QWORD PTR [rdi+240], rax", + "mov rax, QWORD PTR [rsi+248]", + "xor rax, QWORD PTR [rdi+248]", + "mov QWORD PTR [rdi+248], rax", + "mov rax, QWORD PTR [rsi+256]", + "xor rax, QWORD PTR [rdi+256]", + "mov QWORD PTR [rdi+256], rax", + "mov rax, QWORD PTR [rsi+264]", + "xor rax, QWORD PTR [rdi+264]", + "mov QWORD PTR [rdi+264], rax", + "mov rax, QWORD PTR [rsi+272]", + "xor rax, QWORD PTR [rdi+272]", + "mov QWORD PTR [rdi+272], rax", + "mov rax, QWORD PTR [rsi+280]", + "xor rax, QWORD PTR [rdi+280]", + "mov QWORD PTR [rdi+280], rax", + "mov rax, QWORD PTR [rsi+288]", + "xor rax, QWORD PTR [rdi+288]", + "mov QWORD PTR [rdi+288], rax", + "mov rax, QWORD PTR [rsi+296]", + "xor rax, QWORD PTR [rdi+296]", + "mov QWORD PTR [rdi+296], rax", + "mov rax, QWORD PTR [rsi+304]", + "xor rax, QWORD PTR [rdi+304]", + "mov QWORD PTR [rdi+304], rax", + "mov rax, QWORD PTR [rsi+312]", + "xor rax, QWORD PTR [rdi+312]", + "mov QWORD PTR [rdi+312], rax", + "mov rax, QWORD PTR [rsi+320]", + "xor rax, QWORD PTR [rdi+320]", + "mov QWORD PTR [rdi+320], rax", + "mov rax, QWORD PTR [rsi+328]", + "xor rax, QWORD PTR [rdi+328]", + "mov QWORD PTR [rdi+328], rax", + "mov rax, QWORD PTR [rsi+336]", + "xor rax, QWORD PTR [rdi+336]", + "mov QWORD PTR [rdi+336], rax", + "mov rax, QWORD PTR [rsi+344]", + "xor rax, QWORD PTR [rdi+344]", + "mov QWORD PTR [rdi+344], rax", + "mov rax, QWORD PTR [rsi+352]", + "xor rax, QWORD PTR [rdi+352]", + "mov QWORD PTR [rdi+352], rax", + "mov rax, QWORD PTR [rsi+360]", + "xor rax, QWORD PTR [rdi+360]", + "mov QWORD PTR [rdi+360], rax", + "mov rax, QWORD PTR [rsi+368]", + "xor rax, QWORD PTR [rdi+368]", + "mov QWORD PTR [rdi+368], rax", + "mov rax, QWORD PTR [rsi+376]", + "xor rax, QWORD PTR [rdi+376]", + "mov QWORD PTR [rdi+376], rax", + "mov rax, QWORD PTR [rsi+384]", + "xor rax, QWORD PTR [rdi+384]", + "mov QWORD PTR [rdi+384], rax", + "mov rax, QWORD PTR [rsi+392]", + "xor rax, QWORD PTR [rdi+392]", + "mov QWORD PTR [rdi+392], rax", + "mov rax, QWORD PTR [rsi+400]", + "xor rax, QWORD PTR [rdi+400]", + "mov QWORD PTR [rdi+400], rax", + "mov rax, QWORD PTR [rsi+408]", + "xor rax, QWORD PTR [rdi+408]", + "mov QWORD PTR [rdi+408], rax", + "mov rax, QWORD PTR [rsi+416]", + "xor rax, QWORD PTR [rdi+416]", + "mov QWORD PTR [rdi+416], rax", + "mov rax, QWORD PTR [rsi+424]", + "xor rax, QWORD PTR [rdi+424]", + "mov QWORD PTR [rdi+424], rax", + "mov rax, QWORD PTR [rsi+432]", + "xor rax, QWORD PTR [rdi+432]", + "mov QWORD PTR [rdi+432], rax", + "mov rax, QWORD PTR [rsi+440]", + "xor rax, QWORD PTR [rdi+440]", + "mov QWORD PTR [rdi+440], rax", + "mov rax, QWORD PTR [rsi+448]", + "xor rax, QWORD PTR [rdi+448]", + "mov QWORD PTR [rdi+448], rax", + "mov rax, QWORD PTR [rsi+456]", + "xor rax, QWORD PTR [rdi+456]", + "mov QWORD PTR [rdi+456], rax", + "mov rax, QWORD PTR [rsi+464]", + "xor rax, QWORD PTR [rdi+464]", + "mov QWORD PTR [rdi+464], rax", + "mov rax, QWORD PTR [rsi+472]", + "xor rax, QWORD PTR [rdi+472]", + "mov QWORD PTR [rdi+472], rax", + "mov rax, QWORD PTR [rsi+480]", + "xor rax, QWORD PTR [rdi+480]", + "mov QWORD PTR [rdi+480], rax", + "mov rax, QWORD PTR [rsi+488]", + "xor rax, QWORD PTR [rdi+488]", + "mov QWORD PTR [rdi+488], rax", + "mov rax, QWORD PTR [rsi+496]", + "xor rax, QWORD PTR [rdi+496]", + "mov QWORD PTR [rdi+496], rax", + "mov rax, QWORD PTR [rsi+504]", + "xor rax, QWORD PTR [rdi+504]", + "mov QWORD PTR [rdi+504], rax", + "mov rax, QWORD PTR [rsi+512]", + "xor rax, QWORD PTR [rdi+512]", + "mov QWORD PTR [rdi+512], rax", + "mov rax, QWORD PTR [rsi+520]", + "xor rax, QWORD PTR [rdi+520]", + "mov QWORD PTR [rdi+520], rax", + "mov rax, QWORD PTR [rsi+528]", + "xor rax, QWORD PTR [rdi+528]", + "mov QWORD PTR [rdi+528], rax", + "mov rax, QWORD PTR [rsi+536]", + "xor rax, QWORD PTR [rdi+536]", + "mov QWORD PTR [rdi+536], rax", + "mov rax, QWORD PTR [rsi+544]", + "xor rax, QWORD PTR [rdi+544]", + "mov QWORD PTR [rdi+544], rax", + "mov rax, QWORD PTR [rsi+552]", + "xor rax, QWORD PTR [rdi+552]", + "mov QWORD PTR [rdi+552], rax", + "mov rax, QWORD PTR [rsi+560]", + "xor rax, QWORD PTR [rdi+560]", + "mov QWORD PTR [rdi+560], rax", + "mov rax, QWORD PTR [rsi+568]", + "xor rax, QWORD PTR [rdi+568]", + "mov QWORD PTR [rdi+568], rax", + "mov rax, QWORD PTR [rsi+576]", + "xor rax, QWORD PTR [rdi+576]", + "mov QWORD PTR [rdi+576], rax", + "mov rax, QWORD PTR [rsi+584]", + "xor rax, QWORD PTR [rdi+584]", + "mov QWORD PTR [rdi+584], rax", + "mov rax, QWORD PTR [rsi+592]", + "xor rax, QWORD PTR [rdi+592]", + "mov QWORD PTR [rdi+592], rax", + "mov rax, QWORD PTR [rsi+600]", + "xor rax, QWORD PTR [rdi+600]", + "mov QWORD PTR [rdi+600], rax", + "mov rax, QWORD PTR [rsi+608]", + "xor rax, QWORD PTR [rdi+608]", + "mov QWORD PTR [rdi+608], rax", + "mov rax, QWORD PTR [rsi+616]", + "xor rax, QWORD PTR [rdi+616]", + "mov QWORD PTR [rdi+616], rax", + "mov rax, QWORD PTR [rsi+624]", + "xor rax, QWORD PTR [rdi+624]", + "mov QWORD PTR [rdi+624], rax", + "mov rax, QWORD PTR [rsi+632]", + "xor rax, QWORD PTR [rdi+632]", + "mov QWORD PTR [rdi+632], rax", + "mov rax, QWORD PTR [rsi+640]", + "xor rax, QWORD PTR [rdi+640]", + "mov QWORD PTR [rdi+640], rax", + "mov rax, QWORD PTR [rsi+648]", + "xor rax, QWORD PTR [rdi+648]", + "mov QWORD PTR [rdi+648], rax", + "mov rax, QWORD PTR [rsi+656]", + "xor rax, QWORD PTR [rdi+656]", + "mov QWORD PTR [rdi+656], rax", + "mov rax, QWORD PTR [rsi+664]", + "xor rax, QWORD PTR [rdi+664]", + "mov QWORD PTR [rdi+664], rax", + "mov rax, QWORD PTR [rsi+672]", + "xor rax, QWORD PTR [rdi+672]", + "mov QWORD PTR [rdi+672], rax", + "mov rax, QWORD PTR [rsi+680]", + "xor rax, QWORD PTR [rdi+680]", + "mov QWORD PTR [rdi+680], rax", + "mov rax, QWORD PTR [rsi+688]", + "xor rax, QWORD PTR [rdi+688]", + "mov QWORD PTR [rdi+688], rax", + "mov rax, QWORD PTR [rsi+696]", + "xor rax, QWORD PTR [rdi+696]", + "mov QWORD PTR [rdi+696], rax", + "mov rax, QWORD PTR [rsi+704]", + "xor rax, QWORD PTR [rdi+704]", + "mov QWORD PTR [rdi+704], rax", + "mov rax, QWORD PTR [rsi+712]", + "xor rax, QWORD PTR [rdi+712]", + "mov QWORD PTR [rdi+712], rax", + "mov rax, QWORD PTR [rsi+720]", + "xor rax, QWORD PTR [rdi+720]", + "mov QWORD PTR [rdi+720], rax", + "mov rax, QWORD PTR [rsi+728]", + "xor rax, QWORD PTR [rdi+728]", + "mov QWORD PTR [rdi+728], rax", + "mov rax, QWORD PTR [rsi+736]", + "xor rax, QWORD PTR [rdi+736]", + "mov QWORD PTR [rdi+736], rax", + "mov rax, QWORD PTR [rsi+744]", + "xor rax, QWORD PTR [rdi+744]", + "mov QWORD PTR [rdi+744], rax", + "mov rax, QWORD PTR [rsi+752]", + "xor rax, QWORD PTR [rdi+752]", + "mov QWORD PTR [rdi+752], rax", + "mov rax, QWORD PTR [rsi+760]", + "xor rax, QWORD PTR [rdi+760]", + "mov QWORD PTR [rdi+760], rax", + "mov rax, QWORD PTR [rsi+768]", + "xor rax, QWORD PTR [rdi+768]", + "mov QWORD PTR [rdi+768], rax", + "mov rax, QWORD PTR [rsi+776]", + "xor rax, QWORD PTR [rdi+776]", + "mov QWORD PTR [rdi+776], rax", + "mov rax, QWORD PTR [rsi+784]", + "xor rax, QWORD PTR [rdi+784]", + "mov QWORD PTR [rdi+784], rax", + "mov rax, QWORD PTR [rsi+792]", + "xor rax, QWORD PTR [rdi+792]", + "mov QWORD PTR [rdi+792], rax", + "mov rax, QWORD PTR [rsi+800]", + "xor rax, QWORD PTR [rdi+800]", + "mov QWORD PTR [rdi+800], rax", + "mov rax, QWORD PTR [rsi+808]", + "xor rax, QWORD PTR [rdi+808]", + "mov QWORD PTR [rdi+808], rax", + "mov rax, QWORD PTR [rsi+816]", + "xor rax, QWORD PTR [rdi+816]", + "mov QWORD PTR [rdi+816], rax", + "mov rax, QWORD PTR [rsi+824]", + "xor rax, QWORD PTR [rdi+824]", + "mov QWORD PTR [rdi+824], rax", + "mov rax, QWORD PTR [rsi+832]", + "xor rax, QWORD PTR [rdi+832]", + "mov QWORD PTR [rdi+832], rax", + "mov rax, QWORD PTR [rsi+840]", + "xor rax, QWORD PTR [rdi+840]", + "mov QWORD PTR [rdi+840], rax", + "mov rax, QWORD PTR [rsi+848]", + "xor rax, QWORD PTR [rdi+848]", + "mov QWORD PTR [rdi+848], rax", + "mov rax, QWORD PTR [rsi+856]", + "xor rax, QWORD PTR [rdi+856]", + "mov QWORD PTR [rdi+856], rax", + "mov rax, QWORD PTR [rsi+864]", + "xor rax, QWORD PTR [rdi+864]", + "mov QWORD PTR [rdi+864], rax", + "mov rax, QWORD PTR [rsi+872]", + "xor rax, QWORD PTR [rdi+872]", + "mov QWORD PTR [rdi+872], rax", + "mov rax, QWORD PTR [rsi+880]", + "xor rax, QWORD PTR [rdi+880]", + "mov QWORD PTR [rdi+880], rax", + "mov rax, QWORD PTR [rsi+888]", + "xor rax, QWORD PTR [rdi+888]", + "mov QWORD PTR [rdi+888], rax", + "mov rax, QWORD PTR [rsi+896]", + "xor rax, QWORD PTR [rdi+896]", + "mov QWORD PTR [rdi+896], rax", + "mov rax, QWORD PTR [rsi+904]", + "xor rax, QWORD PTR [rdi+904]", + "mov QWORD PTR [rdi+904], rax", + "mov rax, QWORD PTR [rsi+912]", + "xor rax, QWORD PTR [rdi+912]", + "mov QWORD PTR [rdi+912], rax", + "mov rax, QWORD PTR [rsi+920]", + "xor rax, QWORD PTR [rdi+920]", + "mov QWORD PTR [rdi+920], rax", + "mov rax, QWORD PTR [rsi+928]", + "xor rax, QWORD PTR [rdi+928]", + "mov QWORD PTR [rdi+928], rax", + "mov rax, QWORD PTR [rsi+936]", + "xor rax, QWORD PTR [rdi+936]", + "mov QWORD PTR [rdi+936], rax", + "mov rax, QWORD PTR [rsi+944]", + "xor rax, QWORD PTR [rdi+944]", + "mov QWORD PTR [rdi+944], rax", + "mov rax, QWORD PTR [rsi+952]", + "xor rax, QWORD PTR [rdi+952]", + "mov QWORD PTR [rdi+952], rax", + "mov rax, QWORD PTR [rsi+960]", + "xor rax, QWORD PTR [rdi+960]", + "mov QWORD PTR [rdi+960], rax", + "mov rax, QWORD PTR [rsi+968]", + "xor rax, QWORD PTR [rdi+968]", + "mov QWORD PTR [rdi+968], rax", + "mov rax, QWORD PTR [rsi+976]", + "xor rax, QWORD PTR [rdi+976]", + "mov QWORD PTR [rdi+976], rax", + "mov rax, QWORD PTR [rsi+984]", + "xor rax, QWORD PTR [rdi+984]", + "mov QWORD PTR [rdi+984], rax", + "mov rax, QWORD PTR [rsi+992]", + "xor rax, QWORD PTR [rdi+992]", + "mov QWORD PTR [rdi+992], rax", + "mov rax, QWORD PTR [rsi+1000]", + "xor rax, QWORD PTR [rdi+1000]", + "mov QWORD PTR [rdi+1000], rax", + "mov rax, QWORD PTR [rsi+1008]", + "xor rax, QWORD PTR [rdi+1008]", + "mov QWORD PTR [rdi+1008], rax", + "mov rax, QWORD PTR [rsi+1016]", + "xor rax, QWORD PTR [rdi+1016]", + "mov QWORD PTR [rdi+1016], rax", + "add rbx, 1", + "cmp rbx, QWORD PTR [rbp+184]", + "jb 229b", + "mov rdi, QWORD PTR [rbp+232]", + "mov rsi, 1024", + "mov rdx, QWORD PTR [rbp+256]", + "mov rcx, QWORD PTR [rbp+264]", + "mov r8, QWORD PTR [rbp+248]", + "call {vg_argon2_hprime}", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop rax", + "pop r15", + "pop r14", + "pop r13", + "pop r12", + "pop rbp", + "pop rbx", + "ret", + vg_blake2b_init = sym super::blake2b::vg_blake2b_init, + vg_blake2b_update = sym super::blake2b::vg_blake2b_update, + vg_blake2b_finalize = sym super::blake2b::vg_blake2b_finalize, + vg_argon2_hprime = sym super::argon2::vg_argon2_hprime, + vg_argon2_compress = sym super::argon2::vg_argon2_compress, + ) +} diff --git a/src/lib.rs b/src/lib.rs index 3cd8f6c5d..f7b12e031 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,7 @@ compile_error!("32-bit ARM needs an AAPCS target (not Apple's armv7s or armv7k)" mod aes; pub mod aes_gcm; +pub mod argon2; pub mod chacha20; pub mod chacha20poly1305; pub mod cmac; diff --git a/tests/rfc9106/main.rs b/tests/rfc9106/main.rs new file mode 100644 index 000000000..d3cb55cbb --- /dev/null +++ b/tests/rfc9106/main.rs @@ -0,0 +1,132 @@ +//! Published RFC 9106 vectors, read from the unmodified RFC, and API boundaries. + +#![cfg(all(target_arch = "x86_64", feature = "alloc"))] + +use verified_garbage::argon2::{Error, Variant, derive}; + +fn bytes(text: &str, label: &str, length: usize) -> Vec { + text.split_once(label) + .unwrap() + .1 + .split_whitespace() + .take(length) + .map(|s| u8::from_str_radix(s, 16).unwrap()) + .collect() +} + +fn number(text: &str, label: &str) -> u32 { + text.split_once(label) + .unwrap() + .1 + .split_whitespace() + .next() + .unwrap() + .trim_end_matches(',') + .parse() + .unwrap() +} + +fn input(text: &str, label: &str) -> Vec { + let rest = text.split_once(&format!("{label}[")).unwrap().1; + let (length, data) = rest.split_once("]:").unwrap(); + data.split_whitespace() + .take(length.parse().unwrap()) + .map(|s| u8::from_str_radix(s, 16).unwrap()) + .collect() +} + +#[test] +fn rfc9106_vectors() { + let text = include_str!("../../vectors/rfc9106/rfc9106.txt"); + for (variant, name) in [ + (Variant::Argon2d, "Argon2d"), + (Variant::Argon2i, "Argon2i"), + (Variant::Argon2id, "Argon2id"), + ] { + let text = text + .split_once(&format!("{name} version number 19")) + .unwrap() + .1; + let expected = bytes(text, "Tag:", number(text, "Tag length:") as usize); + let mut out = vec![0; expected.len()]; + for threads in [1, 2, 8] { + derive( + variant, + &input(text, "Password"), + &input(text, "Salt"), + number(text, "Passes:"), + number(text, "Memory:"), + number(text, "Parallelism:"), + threads, + &input(text, "Secret"), + &input(text, "Associated data"), + &mut out, + ) + .unwrap(); + assert_eq!(out, expected, "{variant:?}, threads={threads}"); + } + } +} + +#[test] +fn invalid_parameters_preserve_output() { + let mut out = [0xa5; 4]; + for (passes, memory, lanes, threads) in [ + (0, 8, 1, 1), + (1, 8, 0, 1), + (1, u32::MAX, 1 << 24, 1), + (1, 8, 1, 0), + (1, 8, 1, 1 << 24), + (1, 7, 1, 1), + (1, 15, 2, 1), + ] { + assert_eq!( + derive( + Variant::Argon2id, + b"", + b"", + passes, + memory, + lanes, + threads, + b"", + b"", + &mut out + ), + Err(Error::InvalidParameters) + ); + assert_eq!(out, [0xa5; 4]); + } + for len in 0..4 { + assert_eq!( + derive( + Variant::Argon2i, + b"", + b"", + 1, + 8, + 1, + 1, + b"", + b"", + &mut out[..len] + ), + Err(Error::InvalidParameters) + ); + } +} + +#[test] +fn errors() { + for (error, message) in [ + (Error::InvalidParameters, "invalid Argon2 parameters"), + ( + Error::AllocationFailed, + "could not allocate Argon2's memory", + ), + ] { + assert_eq!(error.to_string(), message); + let _: &dyn std::error::Error = &error; + assert!(!format!("{error:?}").is_empty()); + } +} From bdfe5b11ab8ff7886c6e40864d1b2445be5dbc05 Mon Sep 17 00:00:00 2001 From: Paul Kehrer <161495+reaperhulk@users.noreply.github.com> Date: Fri, 2 Oct 2026 11:58:03 +0000 Subject: [PATCH 8/8] Keep Argon2 benchmarks compatible with OpenSSL 3.0 runners --- bench/Cargo.toml | 6 +++ bench/benches/primitives/argon2.rs | 64 +++++++++++++++--------------- bench/tests/argon2.rs | 2 +- 3 files changed, 39 insertions(+), 33 deletions(-) diff --git a/bench/Cargo.toml b/bench/Cargo.toml index 43637e527..ae65f238c 100644 --- a/bench/Cargo.toml +++ b/bench/Cargo.toml @@ -15,6 +15,12 @@ publish = false # `src/cpu.rs` and `ci/bench_arches.py`). verified-garbage = { path = "..", features = ["cpu-features-env"] } +[features] +# OpenSSL 3.2+ supplies Argon2; the benchmark runners use 3.0. +# On a supported host, use `cargo bench --features openssl-argon2` and +# `cargo test --features openssl-argon2 --test argon2` for the comparison. +openssl-argon2 = [] + [dev-dependencies] criterion = { version = "0.8", default-features = false, features = ["cargo_bench_support"] } openssl = "0.10" diff --git a/bench/benches/primitives/argon2.rs b/bench/benches/primitives/argon2.rs index db9343a51..9e334689f 100644 --- a/bench/benches/primitives/argon2.rs +++ b/bench/benches/primitives/argon2.rs @@ -1,4 +1,7 @@ //! Complete Argon2 derivations, including allocation and initialization. +//! +//! OpenSSL supplies Argon2 from version 3.2; the runners use 3.0. Enable +//! `openssl-argon2` on a supported host to benchmark it alongside this library. use criterion::Criterion; @@ -11,22 +14,11 @@ pub fn bench(c: &mut Criterion) { use criterion::BenchmarkId; use verified_garbage::argon2::{Variant, derive}; - use crate::{OPENSSL, VG}; - type Oracle = fn( - Option<&openssl::lib_ctx::LibCtxRef>, - &[u8], - &[u8], - Option<&[u8]>, - Option<&[u8]>, - u32, - u32, - u32, - &mut [u8], - ) -> Result<(), openssl::error::ErrorStack>; - for (variant, name, openssl) in [ - (Variant::Argon2d, "argon2d", openssl::kdf::argon2d as Oracle), - (Variant::Argon2i, "argon2i", openssl::kdf::argon2i), - (Variant::Argon2id, "argon2id", openssl::kdf::argon2id), + use crate::VG; + for (variant, name) in [ + (Variant::Argon2d, "argon2d"), + (Variant::Argon2i, "argon2i"), + (Variant::Argon2id, "argon2id"), ] { let mut g = c.benchmark_group(name); g.sample_size(10); @@ -49,22 +41,30 @@ pub fn bench(c: &mut Criterion) { .unwrap() }) }); - g.bench_function(BenchmarkId::new(OPENSSL, memory), |b| { - b.iter(|| { - openssl( - None, - black_box(b"password"), - black_box(b"saltsalt"), - None, - None, - 3, - 1, - memory, - &mut out, - ) - .unwrap() - }) - }); + #[cfg(feature = "openssl-argon2")] + { + let openssl = match variant { + Variant::Argon2d => openssl::kdf::argon2d, + Variant::Argon2i => openssl::kdf::argon2i, + Variant::Argon2id => openssl::kdf::argon2id, + }; + g.bench_function(BenchmarkId::new(crate::OPENSSL, memory), |b| { + b.iter(|| { + openssl( + None, + black_box(b"password"), + black_box(b"saltsalt"), + None, + None, + 3, + 1, + memory, + &mut out, + ) + .unwrap() + }) + }); + } } g.finish(); } diff --git a/bench/tests/argon2.rs b/bench/tests/argon2.rs index 9f21d473f..0440b141a 100644 --- a/bench/tests/argon2.rs +++ b/bench/tests/argon2.rs @@ -1,6 +1,6 @@ //! Differential complete derivations against OpenSSL, including H′ boundaries. -#![cfg(target_arch = "x86_64")] +#![cfg(all(target_arch = "x86_64", feature = "openssl-argon2"))] use verified_garbage::argon2::{Variant, derive};