diff --git a/.github/workflows/linux-x64.yaml b/.github/workflows/linux-x64.yaml index 442c6930d..c0c42e8f8 100644 --- a/.github/workflows/linux-x64.yaml +++ b/.github/workflows/linux-x64.yaml @@ -1,4 +1,4 @@ -name: Build and test EverParse based on a FStar image +name: Linux build on: push: branches-ignore: diff --git a/.github/workflows/windows.yaml b/.github/workflows/windows.yaml index 987abedb4..1c7cb0b03 100644 --- a/.github/workflows/windows.yaml +++ b/.github/workflows/windows.yaml @@ -1,4 +1,4 @@ -name: Build and test EverParse +name: Windows package build on: pull_request: workflow_dispatch: diff --git a/EverParse.fst.config.json b/EverParse.fst.config.json index 4c9cbe7fe..b8d783435 100644 --- a/EverParse.fst.config.json +++ b/EverParse.fst.config.json @@ -1,16 +1,20 @@ { "fstar_exe": "fstar.exe", "options": [ - "--max_fuel", "0", + "--initial_fuel", "0", + "--max_fuel", "2", + "--initial_ifuel", "2", "--max_ifuel", "2", - "--initial_ifuel", "2" + "--initial_ifuel", "2", + "--ext", "context_pruning" ], "include_dirs": [ - "./src/lowparse", + "./src/lowparse", "${KRML_HOME}/krmllib", "${KRML_HOME}/krmllib/obj", "./src/3d", - "./src/3d/prelude" + "./src/3d/prelude", + "./src/ASN1" ] } \ No newline at end of file diff --git a/Makefile b/Makefile index 683ed386e..5d99e91e5 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -all: quackyducky lowparse 3d +all: quackyducky lowparse 3d asn1 lowparse: +$(MAKE) -C src/lowparse @@ -6,6 +6,9 @@ lowparse: 3d: lowparse +$(MAKE) -C src/3d +asn1: lowparse + +$(MAKE) -C src/ASN1 verify + quackyducky: +$(MAKE) -C src/qd @@ -25,6 +28,9 @@ lowparse-unit-test: lowparse 3d-test: 3d-unit-test 3d-doc-test +asn1-test: asn1 + +$(MAKE) -C src/ASN1 test + lowparse-bitfields-test: lowparse +$(MAKE) -C tests/bitfields @@ -44,7 +50,7 @@ quackyducky-sample0-test: quackyducky lowparse quackyducky-test: quackyducky-unit-test quackyducky-sample-test quackyducky-sample0-test quackyducky-sample-low-test -test: all lowparse-test quackyducky-test 3d-test +test: all lowparse-test quackyducky-test 3d-test asn1-test ci: test @@ -60,7 +66,7 @@ clean-quackyducky: clean: clean-3d clean-lowparse clean-quackyducky rm -rf bin -.PHONY: all gen verify test gen-test clean quackyducky lowparse lowparse-test quackyducky-test lowparse-fstar-test quackyducky-sample-test quackyducky-sample0-test quackyducky-unit-test package 3d 3d-test lowparse-unit-test lowparse-bitfields-test release everparse 3d-unit-test 3d-doc-test ci clean-3d clean-lowparse clean-quackyducky +.PHONY: all gen verify test gen-test clean quackyducky lowparse lowparse-test quackyducky-test lowparse-fstar-test quackyducky-sample-test quackyducky-sample0-test quackyducky-unit-test package 3d 3d-test lowparse-unit-test lowparse-bitfields-test release everparse 3d-unit-test 3d-doc-test ci clean-3d clean-lowparse clean-quackyducky asn1 asn1-test release: +src/package/release.sh diff --git a/doc/3d-snapshot/Base.c b/doc/3d-snapshot/Base.c index dab659d58..373cab2ee 100644 --- a/doc/3d-snapshot/Base.c +++ b/doc/3d-snapshot/Base.c @@ -65,66 +65,14 @@ BaseValidatePair( uint64_t StartPosition ) { - /* Validating field first */ - /* Checking that we have enough space for a UINT32, i.e., 4 bytes */ - BOOLEAN hasBytes0 = 4ULL <= (InputLength - StartPosition); - uint64_t positionAfterPair; - if (hasBytes0) - { - positionAfterPair = StartPosition + 4ULL; - } - else - { - positionAfterPair = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - StartPosition); - } - uint64_t res; - if (EverParseIsSuccess(positionAfterPair)) - { - res = positionAfterPair; - } - else - { - ErrorHandlerFn("_Pair", - "first", - EverParseErrorReasonOfResult(positionAfterPair), - EverParseGetValidatorErrorKind(positionAfterPair), - Ctxt, - Input, - StartPosition); - res = positionAfterPair; - } - uint64_t positionAfterfirst = res; - if (EverParseIsError(positionAfterfirst)) - { - return positionAfterfirst; - } - /* Validating field second */ - /* Checking that we have enough space for a UINT32, i.e., 4 bytes */ - BOOLEAN hasBytes = 4ULL <= (InputLength - positionAfterfirst); - uint64_t positionAfterPair0; + KRML_MAYBE_UNUSED_VAR(Ctxt); + KRML_MAYBE_UNUSED_VAR(ErrorHandlerFn); + KRML_MAYBE_UNUSED_VAR(Input); + BOOLEAN hasBytes = 8ULL <= (InputLength - StartPosition); if (hasBytes) { - positionAfterPair0 = positionAfterfirst + 4ULL; - } - else - { - positionAfterPair0 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - positionAfterfirst); - } - if (EverParseIsSuccess(positionAfterPair0)) - { - return positionAfterPair0; + return StartPosition + 8ULL; } - ErrorHandlerFn("_Pair", - "second", - EverParseErrorReasonOfResult(positionAfterPair0), - EverParseGetValidatorErrorKind(positionAfterPair0), - Ctxt, - Input, - positionAfterfirst); - return positionAfterPair0; + return EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, StartPosition); } diff --git a/doc/3d-snapshot/Color.c b/doc/3d-snapshot/Color.c index 7a6e7d67c..3f81a663d 100644 --- a/doc/3d-snapshot/Color.c +++ b/doc/3d-snapshot/Color.c @@ -74,66 +74,13 @@ ColorValidateColoredPoint( { return positionAftercol_refinement0; } - /* Validating field x */ - /* Checking that we have enough space for a UINT32, i.e., 4 bytes */ - BOOLEAN hasBytes1 = 4ULL <= (InputLength - positionAftercol_refinement0); - uint64_t positionAfterColoredPoint0; - if (hasBytes1) - { - positionAfterColoredPoint0 = positionAftercol_refinement0 + 4ULL; - } - else - { - positionAfterColoredPoint0 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - positionAftercol_refinement0); - } - uint64_t res; - if (EverParseIsSuccess(positionAfterColoredPoint0)) - { - res = positionAfterColoredPoint0; - } - else - { - ErrorHandlerFn("_coloredPoint", - "x", - EverParseErrorReasonOfResult(positionAfterColoredPoint0), - EverParseGetValidatorErrorKind(positionAfterColoredPoint0), - Ctxt, - Input, - positionAftercol_refinement0); - res = positionAfterColoredPoint0; - } - uint64_t positionAfterx = res; - if (EverParseIsError(positionAfterx)) - { - return positionAfterx; - } - /* Validating field y */ - /* Checking that we have enough space for a UINT32, i.e., 4 bytes */ - BOOLEAN hasBytes = 4ULL <= (InputLength - positionAfterx); - uint64_t positionAfterColoredPoint1; + BOOLEAN hasBytes = 8ULL <= (InputLength - positionAftercol_refinement0); if (hasBytes) { - positionAfterColoredPoint1 = positionAfterx + 4ULL; + return positionAftercol_refinement0 + 8ULL; } - else - { - positionAfterColoredPoint1 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - positionAfterx); - } - if (EverParseIsSuccess(positionAfterColoredPoint1)) - { - return positionAfterColoredPoint1; - } - ErrorHandlerFn("_coloredPoint", - "y", - EverParseErrorReasonOfResult(positionAfterColoredPoint1), - EverParseGetValidatorErrorKind(positionAfterColoredPoint1), - Ctxt, - Input, - positionAfterx); - return positionAfterColoredPoint1; + return + EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, + positionAftercol_refinement0); } diff --git a/doc/3d-snapshot/ColoredPoint.c b/doc/3d-snapshot/ColoredPoint.c index 4d6faad69..f6f3c7d0c 100644 --- a/doc/3d-snapshot/ColoredPoint.c +++ b/doc/3d-snapshot/ColoredPoint.c @@ -2,87 +2,6 @@ #include "ColoredPoint.h" -static inline uint64_t -ValidatePoint( - uint8_t *Ctxt, - void - (*ErrorHandlerFn)( - EVERPARSE_STRING x0, - EVERPARSE_STRING x1, - EVERPARSE_STRING x2, - uint64_t x3, - uint8_t *x4, - uint8_t *x5, - uint64_t x6 - ), - uint8_t *Input, - uint64_t InputLength, - uint64_t StartPosition -) -{ - /* Validating field x */ - /* Checking that we have enough space for a UINT16, i.e., 2 bytes */ - BOOLEAN hasBytes0 = 2ULL <= (InputLength - StartPosition); - uint64_t positionAfterPoint; - if (hasBytes0) - { - positionAfterPoint = StartPosition + 2ULL; - } - else - { - positionAfterPoint = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - StartPosition); - } - uint64_t res; - if (EverParseIsSuccess(positionAfterPoint)) - { - res = positionAfterPoint; - } - else - { - ErrorHandlerFn("_point", - "x", - EverParseErrorReasonOfResult(positionAfterPoint), - EverParseGetValidatorErrorKind(positionAfterPoint), - Ctxt, - Input, - StartPosition); - res = positionAfterPoint; - } - uint64_t positionAfterx = res; - if (EverParseIsError(positionAfterx)) - { - return positionAfterx; - } - /* Validating field y */ - /* Checking that we have enough space for a UINT16, i.e., 2 bytes */ - BOOLEAN hasBytes = 2ULL <= (InputLength - positionAfterx); - uint64_t positionAfterPoint0; - if (hasBytes) - { - positionAfterPoint0 = positionAfterx + 2ULL; - } - else - { - positionAfterPoint0 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - positionAfterx); - } - if (EverParseIsSuccess(positionAfterPoint0)) - { - return positionAfterPoint0; - } - ErrorHandlerFn("_point", - "y", - EverParseErrorReasonOfResult(positionAfterPoint0), - EverParseGetValidatorErrorKind(positionAfterPoint0), - Ctxt, - Input, - positionAfterx); - return positionAfterPoint0; -} - uint64_t ColoredPointValidateColoredPoint1( uint8_t *Ctxt, @@ -101,61 +20,15 @@ ColoredPointValidateColoredPoint1( uint64_t StartPosition ) { - /* Validating field color */ - /* Checking that we have enough space for a UINT8, i.e., 1 byte */ - BOOLEAN hasBytes = 1ULL <= (InputLength - StartPosition); - uint64_t positionAfterColoredPoint1; + KRML_MAYBE_UNUSED_VAR(Ctxt); + KRML_MAYBE_UNUSED_VAR(ErrorHandlerFn); + KRML_MAYBE_UNUSED_VAR(Input); + BOOLEAN hasBytes = 5ULL <= (InputLength - StartPosition); if (hasBytes) { - positionAfterColoredPoint1 = StartPosition + 1ULL; + return StartPosition + 5ULL; } - else - { - positionAfterColoredPoint1 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - StartPosition); - } - uint64_t res; - if (EverParseIsSuccess(positionAfterColoredPoint1)) - { - res = positionAfterColoredPoint1; - } - else - { - ErrorHandlerFn("_coloredPoint1", - "color", - EverParseErrorReasonOfResult(positionAfterColoredPoint1), - EverParseGetValidatorErrorKind(positionAfterColoredPoint1), - Ctxt, - Input, - StartPosition); - res = positionAfterColoredPoint1; - } - uint64_t positionAftercolor = res; - if (EverParseIsError(positionAftercolor)) - { - return positionAftercolor; - } - /* Validating field pt */ - uint64_t - positionAfterColoredPoint10 = - ValidatePoint(Ctxt, - ErrorHandlerFn, - Input, - InputLength, - positionAftercolor); - if (EverParseIsSuccess(positionAfterColoredPoint10)) - { - return positionAfterColoredPoint10; - } - ErrorHandlerFn("_coloredPoint1", - "pt", - EverParseErrorReasonOfResult(positionAfterColoredPoint10), - EverParseGetValidatorErrorKind(positionAfterColoredPoint10), - Ctxt, - Input, - positionAftercolor); - return positionAfterColoredPoint10; + return EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, StartPosition); } uint64_t @@ -176,59 +49,14 @@ ColoredPointValidateColoredPoint2( uint64_t StartPosition ) { - /* Validating field pt */ - uint64_t - positionAfterColoredPoint2 = - ValidatePoint(Ctxt, - ErrorHandlerFn, - Input, - InputLength, - StartPosition); - uint64_t positionAfterpt; - if (EverParseIsSuccess(positionAfterColoredPoint2)) - { - positionAfterpt = positionAfterColoredPoint2; - } - else - { - ErrorHandlerFn("_coloredPoint2", - "pt", - EverParseErrorReasonOfResult(positionAfterColoredPoint2), - EverParseGetValidatorErrorKind(positionAfterColoredPoint2), - Ctxt, - Input, - StartPosition); - positionAfterpt = positionAfterColoredPoint2; - } - if (EverParseIsError(positionAfterpt)) - { - return positionAfterpt; - } - /* Validating field color */ - /* Checking that we have enough space for a UINT8, i.e., 1 byte */ - BOOLEAN hasBytes = 1ULL <= (InputLength - positionAfterpt); - uint64_t positionAfterColoredPoint20; + KRML_MAYBE_UNUSED_VAR(Ctxt); + KRML_MAYBE_UNUSED_VAR(ErrorHandlerFn); + KRML_MAYBE_UNUSED_VAR(Input); + BOOLEAN hasBytes = 5ULL <= (InputLength - StartPosition); if (hasBytes) { - positionAfterColoredPoint20 = positionAfterpt + 1ULL; - } - else - { - positionAfterColoredPoint20 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - positionAfterpt); - } - if (EverParseIsSuccess(positionAfterColoredPoint20)) - { - return positionAfterColoredPoint20; + return StartPosition + 5ULL; } - ErrorHandlerFn("_coloredPoint2", - "color", - EverParseErrorReasonOfResult(positionAfterColoredPoint20), - EverParseGetValidatorErrorKind(positionAfterColoredPoint20), - Ctxt, - Input, - positionAfterpt); - return positionAfterColoredPoint20; + return EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, StartPosition); } diff --git a/doc/3d-snapshot/Derived.c b/doc/3d-snapshot/Derived.c index b5e32f939..b6670cf3d 100644 --- a/doc/3d-snapshot/Derived.c +++ b/doc/3d-snapshot/Derived.c @@ -20,55 +20,15 @@ DerivedValidateTriple( uint64_t StartPosition ) { - /* Validating field pair */ - uint64_t - positionAfterTriple = BaseValidatePair(Ctxt, ErrorHandlerFn, Input, InputLength, StartPosition); - uint64_t positionAfterpair; - if (EverParseIsSuccess(positionAfterTriple)) - { - positionAfterpair = positionAfterTriple; - } - else - { - ErrorHandlerFn("_Triple", - "pair", - EverParseErrorReasonOfResult(positionAfterTriple), - EverParseGetValidatorErrorKind(positionAfterTriple), - Ctxt, - Input, - StartPosition); - positionAfterpair = positionAfterTriple; - } - if (EverParseIsError(positionAfterpair)) - { - return positionAfterpair; - } - /* Validating field third */ - /* Checking that we have enough space for a UINT32, i.e., 4 bytes */ - BOOLEAN hasBytes = 4ULL <= (InputLength - positionAfterpair); - uint64_t positionAfterTriple0; + KRML_MAYBE_UNUSED_VAR(Ctxt); + KRML_MAYBE_UNUSED_VAR(ErrorHandlerFn); + KRML_MAYBE_UNUSED_VAR(Input); + BOOLEAN hasBytes = 12ULL <= (InputLength - StartPosition); if (hasBytes) { - positionAfterTriple0 = positionAfterpair + 4ULL; - } - else - { - positionAfterTriple0 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - positionAfterpair); + return StartPosition + 12ULL; } - if (EverParseIsSuccess(positionAfterTriple0)) - { - return positionAfterTriple0; - } - ErrorHandlerFn("_Triple", - "third", - EverParseErrorReasonOfResult(positionAfterTriple0), - EverParseGetValidatorErrorKind(positionAfterTriple0), - Ctxt, - Input, - positionAfterpair); - return positionAfterTriple0; + return EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, StartPosition); } uint64_t @@ -89,48 +49,14 @@ DerivedValidateQuad( uint64_t StartPosition ) { - /* Validating field _12 */ - uint64_t - positionAfterQuad = BaseValidatePair(Ctxt, ErrorHandlerFn, Input, InputLength, StartPosition); - uint64_t positionAfter12; - if (EverParseIsSuccess(positionAfterQuad)) - { - positionAfter12 = positionAfterQuad; - } - else - { - ErrorHandlerFn("_Quad", - "_12", - EverParseErrorReasonOfResult(positionAfterQuad), - EverParseGetValidatorErrorKind(positionAfterQuad), - Ctxt, - Input, - StartPosition); - positionAfter12 = positionAfterQuad; - } - if (EverParseIsError(positionAfter12)) - { - return positionAfter12; - } - /* Validating field _34 */ - uint64_t - positionAfterQuad0 = - BaseValidatePair(Ctxt, - ErrorHandlerFn, - Input, - InputLength, - positionAfter12); - if (EverParseIsSuccess(positionAfterQuad0)) + KRML_MAYBE_UNUSED_VAR(Ctxt); + KRML_MAYBE_UNUSED_VAR(ErrorHandlerFn); + KRML_MAYBE_UNUSED_VAR(Input); + BOOLEAN hasBytes = 16ULL <= (InputLength - StartPosition); + if (hasBytes) { - return positionAfterQuad0; + return StartPosition + 16ULL; } - ErrorHandlerFn("_Quad", - "_34", - EverParseErrorReasonOfResult(positionAfterQuad0), - EverParseGetValidatorErrorKind(positionAfterQuad0), - Ctxt, - Input, - positionAfter12); - return positionAfterQuad0; + return EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, StartPosition); } diff --git a/doc/3d-snapshot/Derived.h b/doc/3d-snapshot/Derived.h index 905bebcd4..d98d88c36 100644 --- a/doc/3d-snapshot/Derived.h +++ b/doc/3d-snapshot/Derived.h @@ -7,7 +7,6 @@ extern "C" { #endif -#include "Base.h" #include "EverParse.h" uint64_t diff --git a/doc/3d-snapshot/GetFieldPtr.c b/doc/3d-snapshot/GetFieldPtr.c index 4a9d8484e..a24d2ccb8 100644 --- a/doc/3d-snapshot/GetFieldPtr.c +++ b/doc/3d-snapshot/GetFieldPtr.c @@ -22,70 +22,17 @@ GetFieldPtrValidateT( ) { /* Validating field f1 */ - BOOLEAN hasEnoughBytes0 = (uint64_t)(uint32_t)10U <= (InputLength - StartPosition); - uint64_t positionAfterT; - if (!hasEnoughBytes0) + BOOLEAN hasBytes0 = (uint64_t)10U <= (InputLength - StartPosition); + uint64_t res0; + if (hasBytes0) { - positionAfterT = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - StartPosition); + res0 = StartPosition + (uint64_t)10U; } else { - uint8_t *truncatedInput = Input; - uint64_t truncatedInputLength = StartPosition + (uint64_t)(uint32_t)10U; - uint64_t result = StartPosition; - while (TRUE) - { - uint64_t position = result; - BOOLEAN ite; - if (!(1ULL <= (truncatedInputLength - position))) - { - ite = TRUE; - } - else - { - /* Checking that we have enough space for a UINT8, i.e., 1 byte */ - BOOLEAN hasBytes = 1ULL <= (truncatedInputLength - position); - uint64_t positionAfterT0; - if (hasBytes) - { - positionAfterT0 = position + 1ULL; - } - else - { - positionAfterT0 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - position); - } - uint64_t res; - if (EverParseIsSuccess(positionAfterT0)) - { - res = positionAfterT0; - } - else - { - ErrorHandlerFn("_T", - "f1.element", - EverParseErrorReasonOfResult(positionAfterT0), - EverParseGetValidatorErrorKind(positionAfterT0), - Ctxt, - truncatedInput, - position); - res = positionAfterT0; - } - uint64_t result1 = res; - result = result1; - ite = EverParseIsError(result1); - } - if (ite) - { - break; - } - } - uint64_t res = result; - positionAfterT = res; + res0 = EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, StartPosition); } + uint64_t positionAfterT = res0; uint64_t positionAfterf1; if (EverParseIsSuccess(positionAfterT)) { @@ -107,70 +54,17 @@ GetFieldPtrValidateT( return positionAfterf1; } /* Validating field f2 */ - BOOLEAN hasEnoughBytes = (uint64_t)(uint32_t)20U <= (InputLength - positionAfterf1); - uint64_t positionAfterT0; - if (!hasEnoughBytes) + BOOLEAN hasBytes = (uint64_t)20U <= (InputLength - positionAfterf1); + uint64_t res; + if (hasBytes) { - positionAfterT0 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - positionAfterf1); + res = positionAfterf1 + (uint64_t)20U; } else { - uint8_t *truncatedInput = Input; - uint64_t truncatedInputLength = positionAfterf1 + (uint64_t)(uint32_t)20U; - uint64_t result = positionAfterf1; - while (TRUE) - { - uint64_t position = result; - BOOLEAN ite; - if (!(1ULL <= (truncatedInputLength - position))) - { - ite = TRUE; - } - else - { - /* Checking that we have enough space for a UINT8, i.e., 1 byte */ - BOOLEAN hasBytes = 1ULL <= (truncatedInputLength - position); - uint64_t positionAfterT1; - if (hasBytes) - { - positionAfterT1 = position + 1ULL; - } - else - { - positionAfterT1 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - position); - } - uint64_t res; - if (EverParseIsSuccess(positionAfterT1)) - { - res = positionAfterT1; - } - else - { - ErrorHandlerFn("_T", - "f2.base.element", - EverParseErrorReasonOfResult(positionAfterT1), - EverParseGetValidatorErrorKind(positionAfterT1), - Ctxt, - truncatedInput, - position); - res = positionAfterT1; - } - uint64_t result1 = res; - result = result1; - ite = EverParseIsError(result1); - } - if (ite) - { - break; - } - } - uint64_t res = result; - positionAfterT0 = res; + res = EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, positionAfterf1); } + uint64_t positionAfterT0 = res; uint64_t positionAfterf2; if (EverParseIsSuccess(positionAfterT0)) { @@ -193,16 +87,8 @@ GetFieldPtrValidateT( uint8_t *hd = Input + (uint32_t)positionAfterf1; *Out = hd; BOOLEAN actionSuccessF2 = TRUE; - if (!actionSuccessF2) - { - positionAfterT1 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_ACTION_FAILED, - positionAfterf2); - } - else - { - positionAfterT1 = positionAfterf2; - } + KRML_MAYBE_UNUSED_VAR(actionSuccessF2); + positionAfterT1 = positionAfterf2; } else { diff --git a/doc/3d-snapshot/HelloWorld.c b/doc/3d-snapshot/HelloWorld.c index cff4ac373..63fe50a39 100644 --- a/doc/3d-snapshot/HelloWorld.c +++ b/doc/3d-snapshot/HelloWorld.c @@ -20,66 +20,14 @@ HelloWorldValidatePoint( uint64_t StartPosition ) { - /* Validating field x */ - /* Checking that we have enough space for a UINT16, i.e., 2 bytes */ - BOOLEAN hasBytes0 = 2ULL <= (InputLength - StartPosition); - uint64_t positionAfterPoint; - if (hasBytes0) - { - positionAfterPoint = StartPosition + 2ULL; - } - else - { - positionAfterPoint = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - StartPosition); - } - uint64_t res; - if (EverParseIsSuccess(positionAfterPoint)) - { - res = positionAfterPoint; - } - else - { - ErrorHandlerFn("_point", - "x", - EverParseErrorReasonOfResult(positionAfterPoint), - EverParseGetValidatorErrorKind(positionAfterPoint), - Ctxt, - Input, - StartPosition); - res = positionAfterPoint; - } - uint64_t positionAfterx = res; - if (EverParseIsError(positionAfterx)) - { - return positionAfterx; - } - /* Validating field y */ - /* Checking that we have enough space for a UINT16, i.e., 2 bytes */ - BOOLEAN hasBytes = 2ULL <= (InputLength - positionAfterx); - uint64_t positionAfterPoint0; + KRML_MAYBE_UNUSED_VAR(Ctxt); + KRML_MAYBE_UNUSED_VAR(ErrorHandlerFn); + KRML_MAYBE_UNUSED_VAR(Input); + BOOLEAN hasBytes = 4ULL <= (InputLength - StartPosition); if (hasBytes) { - positionAfterPoint0 = positionAfterx + 2ULL; - } - else - { - positionAfterPoint0 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - positionAfterx); - } - if (EverParseIsSuccess(positionAfterPoint0)) - { - return positionAfterPoint0; + return StartPosition + 4ULL; } - ErrorHandlerFn("_point", - "y", - EverParseErrorReasonOfResult(positionAfterPoint0), - EverParseGetValidatorErrorKind(positionAfterPoint0), - Ctxt, - Input, - positionAfterx); - return positionAfterPoint0; + return EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, StartPosition); } diff --git a/doc/3d-snapshot/ReadPair.c b/doc/3d-snapshot/ReadPair.c index bd094812e..0d92e5427 100644 --- a/doc/3d-snapshot/ReadPair.c +++ b/doc/3d-snapshot/ReadPair.c @@ -45,16 +45,9 @@ ReadPairValidatePair( { uint32_t first = Load32Le(Input + (uint32_t)StartPosition); *X = first; - if (TRUE) - { - positionAfterPair = positionAfterfirst0; - } - else - { - positionAfterPair = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_ACTION_FAILED, - positionAfterfirst0); - } + BOOLEAN actionResult = TRUE; + KRML_MAYBE_UNUSED_VAR(actionResult); + positionAfterPair = positionAfterfirst0; } uint64_t positionAfterfirst; if (EverParseIsSuccess(positionAfterPair)) @@ -99,16 +92,9 @@ ReadPairValidatePair( { uint32_t second = Load32Le(Input + (uint32_t)positionAfterfirst); *Y = second; - if (TRUE) - { - positionAfterPair0 = positionAftersecond; - } - else - { - positionAfterPair0 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_ACTION_FAILED, - positionAftersecond); - } + BOOLEAN actionResult = TRUE; + KRML_MAYBE_UNUSED_VAR(actionResult); + positionAfterPair0 = positionAftersecond; } if (EverParseIsSuccess(positionAfterPair0)) { diff --git a/doc/3d-snapshot/Triangle.c b/doc/3d-snapshot/Triangle.c index c5b2aea0f..a8b9dfe91 100644 --- a/doc/3d-snapshot/Triangle.c +++ b/doc/3d-snapshot/Triangle.c @@ -2,87 +2,6 @@ #include "Triangle.h" -static inline uint64_t -ValidatePoint( - uint8_t *Ctxt, - void - (*ErrorHandlerFn)( - EVERPARSE_STRING x0, - EVERPARSE_STRING x1, - EVERPARSE_STRING x2, - uint64_t x3, - uint8_t *x4, - uint8_t *x5, - uint64_t x6 - ), - uint8_t *Input, - uint64_t InputLength, - uint64_t StartPosition -) -{ - /* Validating field x */ - /* Checking that we have enough space for a UINT16, i.e., 2 bytes */ - BOOLEAN hasBytes0 = 2ULL <= (InputLength - StartPosition); - uint64_t positionAfterPoint; - if (hasBytes0) - { - positionAfterPoint = StartPosition + 2ULL; - } - else - { - positionAfterPoint = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - StartPosition); - } - uint64_t res; - if (EverParseIsSuccess(positionAfterPoint)) - { - res = positionAfterPoint; - } - else - { - ErrorHandlerFn("_point", - "x", - EverParseErrorReasonOfResult(positionAfterPoint), - EverParseGetValidatorErrorKind(positionAfterPoint), - Ctxt, - Input, - StartPosition); - res = positionAfterPoint; - } - uint64_t positionAfterx = res; - if (EverParseIsError(positionAfterx)) - { - return positionAfterx; - } - /* Validating field y */ - /* Checking that we have enough space for a UINT16, i.e., 2 bytes */ - BOOLEAN hasBytes = 2ULL <= (InputLength - positionAfterx); - uint64_t positionAfterPoint0; - if (hasBytes) - { - positionAfterPoint0 = positionAfterx + 2ULL; - } - else - { - positionAfterPoint0 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - positionAfterx); - } - if (EverParseIsSuccess(positionAfterPoint0)) - { - return positionAfterPoint0; - } - ErrorHandlerFn("_point", - "y", - EverParseErrorReasonOfResult(positionAfterPoint0), - EverParseGetValidatorErrorKind(positionAfterPoint0), - Ctxt, - Input, - positionAfterx); - return positionAfterPoint0; -} - uint64_t TriangleValidateTriangle( uint8_t *Ctxt, @@ -101,76 +20,14 @@ TriangleValidateTriangle( uint64_t StartPosition ) { - /* Validating field a */ - uint64_t - positionAfterTriangle = ValidatePoint(Ctxt, ErrorHandlerFn, Input, InputLength, StartPosition); - uint64_t positionAftera; - if (EverParseIsSuccess(positionAfterTriangle)) - { - positionAftera = positionAfterTriangle; - } - else - { - ErrorHandlerFn("_triangle", - "a", - EverParseErrorReasonOfResult(positionAfterTriangle), - EverParseGetValidatorErrorKind(positionAfterTriangle), - Ctxt, - Input, - StartPosition); - positionAftera = positionAfterTriangle; - } - if (EverParseIsError(positionAftera)) - { - return positionAftera; - } - /* Validating field b */ - uint64_t - positionAfterTriangle0 = - ValidatePoint(Ctxt, - ErrorHandlerFn, - Input, - InputLength, - positionAftera); - uint64_t positionAfterb; - if (EverParseIsSuccess(positionAfterTriangle0)) - { - positionAfterb = positionAfterTriangle0; - } - else - { - ErrorHandlerFn("_triangle", - "b", - EverParseErrorReasonOfResult(positionAfterTriangle0), - EverParseGetValidatorErrorKind(positionAfterTriangle0), - Ctxt, - Input, - positionAftera); - positionAfterb = positionAfterTriangle0; - } - if (EverParseIsError(positionAfterb)) - { - return positionAfterb; - } - /* Validating field c */ - uint64_t - positionAfterTriangle1 = - ValidatePoint(Ctxt, - ErrorHandlerFn, - Input, - InputLength, - positionAfterb); - if (EverParseIsSuccess(positionAfterTriangle1)) + KRML_MAYBE_UNUSED_VAR(Ctxt); + KRML_MAYBE_UNUSED_VAR(ErrorHandlerFn); + KRML_MAYBE_UNUSED_VAR(Input); + BOOLEAN hasBytes = 12ULL <= (InputLength - StartPosition); + if (hasBytes) { - return positionAfterTriangle1; + return StartPosition + 12ULL; } - ErrorHandlerFn("_triangle", - "c", - EverParseErrorReasonOfResult(positionAfterTriangle1), - EverParseGetValidatorErrorKind(positionAfterTriangle1), - Ctxt, - Input, - positionAfterb); - return positionAfterTriangle1; + return EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, StartPosition); } diff --git a/doc/3d-snapshot/Triangle2.c b/doc/3d-snapshot/Triangle2.c index 3a60a2855..7425f375b 100644 --- a/doc/3d-snapshot/Triangle2.c +++ b/doc/3d-snapshot/Triangle2.c @@ -2,87 +2,6 @@ #include "Triangle2.h" -static inline uint64_t -ValidatePoint( - uint8_t *Ctxt, - void - (*ErrorHandlerFn)( - EVERPARSE_STRING x0, - EVERPARSE_STRING x1, - EVERPARSE_STRING x2, - uint64_t x3, - uint8_t *x4, - uint8_t *x5, - uint64_t x6 - ), - uint8_t *Input, - uint64_t InputLength, - uint64_t StartPosition -) -{ - /* Validating field x */ - /* Checking that we have enough space for a UINT16, i.e., 2 bytes */ - BOOLEAN hasBytes0 = 2ULL <= (InputLength - StartPosition); - uint64_t positionAfterPoint; - if (hasBytes0) - { - positionAfterPoint = StartPosition + 2ULL; - } - else - { - positionAfterPoint = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - StartPosition); - } - uint64_t res; - if (EverParseIsSuccess(positionAfterPoint)) - { - res = positionAfterPoint; - } - else - { - ErrorHandlerFn("_point", - "x", - EverParseErrorReasonOfResult(positionAfterPoint), - EverParseGetValidatorErrorKind(positionAfterPoint), - Ctxt, - Input, - StartPosition); - res = positionAfterPoint; - } - uint64_t positionAfterx = res; - if (EverParseIsError(positionAfterx)) - { - return positionAfterx; - } - /* Validating field y */ - /* Checking that we have enough space for a UINT16, i.e., 2 bytes */ - BOOLEAN hasBytes = 2ULL <= (InputLength - positionAfterx); - uint64_t positionAfterPoint0; - if (hasBytes) - { - positionAfterPoint0 = positionAfterx + 2ULL; - } - else - { - positionAfterPoint0 = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - positionAfterx); - } - if (EverParseIsSuccess(positionAfterPoint0)) - { - return positionAfterPoint0; - } - ErrorHandlerFn("_point", - "y", - EverParseErrorReasonOfResult(positionAfterPoint0), - EverParseGetValidatorErrorKind(positionAfterPoint0), - Ctxt, - Input, - positionAfterx); - return positionAfterPoint0; -} - uint64_t Triangle2ValidateTriangle( uint8_t *Ctxt, @@ -102,63 +21,17 @@ Triangle2ValidateTriangle( ) { /* Validating field corners */ - BOOLEAN hasEnoughBytes = (uint64_t)(4U * (uint32_t)3U) <= (InputLength - StartPosition); - uint64_t positionAfterTriangle; - if (!hasEnoughBytes) + BOOLEAN hasBytes = (uint64_t)12U <= (InputLength - StartPosition); + uint64_t res; + if (hasBytes) { - positionAfterTriangle = - EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, - StartPosition); + res = StartPosition + (uint64_t)12U; } else { - uint8_t *truncatedInput = Input; - uint64_t truncatedInputLength = StartPosition + (uint64_t)(4U * (uint32_t)3U); - uint64_t result = StartPosition; - while (TRUE) - { - uint64_t position = result; - BOOLEAN ite; - if (!(1ULL <= (truncatedInputLength - position))) - { - ite = TRUE; - } - else - { - uint64_t - positionAfterTriangle0 = - ValidatePoint(Ctxt, - ErrorHandlerFn, - truncatedInput, - truncatedInputLength, - position); - uint64_t result1; - if (EverParseIsSuccess(positionAfterTriangle0)) - { - result1 = positionAfterTriangle0; - } - else - { - ErrorHandlerFn("_triangle", - "corners.element", - EverParseErrorReasonOfResult(positionAfterTriangle0), - EverParseGetValidatorErrorKind(positionAfterTriangle0), - Ctxt, - truncatedInput, - position); - result1 = positionAfterTriangle0; - } - result = result1; - ite = EverParseIsError(result1); - } - if (ite) - { - break; - } - } - uint64_t res = result; - positionAfterTriangle = res; + res = EverParseSetValidatorErrorPos(EVERPARSE_VALIDATOR_ERROR_NOT_ENOUGH_DATA, StartPosition); } + uint64_t positionAfterTriangle = res; if (EverParseIsSuccess(positionAfterTriangle)) { return positionAfterTriangle; diff --git a/src/3d/Ast.fst b/src/3d/Ast.fst index a6ded6310..484320463 100644 --- a/src/3d/Ast.fst +++ b/src/3d/Ast.fst @@ -1108,17 +1108,21 @@ let print_bitfield (bf:option field_bitwidth_t) = (print_typ a.bitfield_type) a.bitfield_from a.bitfield_to -let rec print_field (f:field) : ML string = +let rec print_field' (f:field) (with_comments:bool) : ML string = let field = match f.v with | AtomicField f -> print_atomic_field f | RecordField f i -> Printf.sprintf "%s %s" (print_record f) i.v.name | SwitchCaseField f i -> Printf.sprintf "%s %s" (print_switch_case f) i.v. name in - match f.comments with - | [] -> field - | comms -> Printf.sprintf "//%s\n%s" (String.concat "; " comms) field - + if with_comments then + match f.comments with + | [] -> field + | comms -> Printf.sprintf "//%s\n%s" (String.concat "; " comms) field + else field + +and print_field f : ML string = print_field' f true + and print_record (f:record) : ML string = List.map print_field f |> String.concat ";\n" diff --git a/src/3d/BitFields.fst b/src/3d/BitFields.fst index 9fd6e1d24..0351b21c5 100644 --- a/src/3d/BitFields.fst +++ b/src/3d/BitFields.fst @@ -168,7 +168,7 @@ let eliminate_one_decl (env:B.global_env) (d:decl) : ML decl = match d.d_decl.v with | Record names params where fields -> let i = with_dummy_range (to_ident' "_") in - let { v = RecordField fields _ } = rewrite_field env (with_dummy_range (RecordField fields i)) in + let { v = RecordField fields _ } = rewrite_field env (with_range (RecordField fields i) d.d_decl.range) in List.iter (fun f -> Options.debug_print_string (Printf.sprintf "Bitfields: Field %s has comments <%s>\n" diff --git a/src/3d/InterpreterTarget.fst b/src/3d/InterpreterTarget.fst index 9da6a5d7c..c16df15c8 100644 --- a/src/3d/InterpreterTarget.fst +++ b/src/3d/InterpreterTarget.fst @@ -212,11 +212,11 @@ let dtyp_of_app (en: env) (hd:A.ident) (args:list T.index) DT_IType i | _ -> - let readable = match H.try_find en hd.v with + let has_action, readable = match H.try_find en hd.v with | None -> failwith "type not found" - | Some td -> td.allow_reading + | Some td -> td.has_action, td.allow_reading in - DT_App readable hd + DT_App has_action readable hd (List.map (function Inl _ -> failwith "Unexpected type application" | Inr e -> e) @@ -227,10 +227,10 @@ let tag_of_parser p match p.p_parser with | Parse_return _ -> "Parse_return" | Parse_app _ _ -> "Parse_app" - | Parse_nlist _ _ -> "Parse_nlist" + | Parse_nlist _ _ _ -> "Parse_nlist" | Parse_t_at_most _ _ -> "Parse_t_at_most" | Parse_t_exact _ _ -> "Parse_t_exact" - | Parse_pair _ _ _ -> "Parse_pair" + | Parse_pair _ _ _ _ _ -> "Parse_pair" | Parse_dep_pair _ _ _ -> "Parse_dep_pair" | Parse_dep_pair_with_refinement _ _ _ _ -> "Parse_dep_pair_with_refinement" | Parse_dep_pair_with_action _ _ _ -> "Parse_dep_pair_with_action" @@ -317,7 +317,7 @@ let rec typ_indexes_of_parser (en:env) (p:T.parser) match dt with | DT_IType _ -> typ_indexes_nil - | DT_App _ hd args -> + | DT_App _ _ hd args -> let td = match H.try_find en hd.v with | Some td -> td @@ -337,7 +337,7 @@ let rec typ_indexes_of_parser (en:env) (p:T.parser) end | T.Parse_if_else _ p q - | T.Parse_pair _ p q -> + | T.Parse_pair _ _ p _ q -> typ_indexes_union (typ_indexes_of_parser p) (typ_indexes_of_parser q) | T.Parse_dep_pair _ p (_, q) @@ -348,7 +348,7 @@ let rec typ_indexes_of_parser (en:env) (p:T.parser) | T.Parse_weaken_right p _ | T.Parse_refinement _ p _ | T.Parse_with_comment p _ - | T.Parse_nlist _ p + | T.Parse_nlist _ _ p | T.Parse_t_at_most _ p | T.Parse_t_exact _ p -> typ_indexes_of_parser p @@ -418,14 +418,14 @@ let typ_of_parser (en: env) : Tot (T.parser -> ML typ) | T.Parse_app _ _ -> T_denoted fn (dtyp_of_parser p) - | T.Parse_pair _ p q -> - T_pair (nes p.p_fieldname) (typ_of_parser p) (typ_of_parser q) + | T.Parse_pair _ p_const p q_const q -> + T_pair (nes p.p_fieldname) p_const (typ_of_parser p) q_const (typ_of_parser q) | T.Parse_with_comment p c -> T_with_comment fn (typ_of_parser p) (String.concat "; " c) - | T.Parse_nlist n p -> - T_nlist fn n (typ_of_parser p) + | T.Parse_nlist fixed_size n p -> + T_nlist fn fixed_size n (typ_of_parser p) | T.Parse_t_at_most n p -> T_at_most fn n (typ_of_parser p) @@ -537,10 +537,43 @@ let rec allow_reading_of_typ (t:typ) begin match dt with | DT_IType i -> allow_reader_of_itype i - | DT_App readable _ _ -> readable + | DT_App has_action readable _ _ -> readable end - | _ -> false + | _ -> + false + +let rec has_action_of_typ (t:typ) + : Tot bool + = + match t with + | T_false _ -> false + | T_with_comment _ t _ -> has_action_of_typ t + | T_denoted _ dt -> + begin + match dt with + | DT_IType i -> false + | DT_App has_action readable _ _ -> has_action + end + | T_pair _ _ t1 _ t2 -> + has_action_of_typ t1 || has_action_of_typ t2 + | T_dep_pair _ t1 (_, t2) -> + has_action_of_dtyp t1 || has_action_of_typ t2 + | T_refine _ t _ -> has_action_of_dtyp t + | T_refine_with_action _ _ _ _ -> true + | T_dep_pair_with_refinement _ t _ (_, t2) -> has_action_of_dtyp t || has_action_of_typ t2 + | T_dep_pair_with_action _ _ _ _ -> true + | T_dep_pair_with_refinement_and_action _ _ _ _ _ -> true + | T_if_else _ t1 t2 -> has_action_of_typ t1 || has_action_of_typ t2 + | T_with_action _ _ _ -> true + | T_with_dep_action _ _ _ -> true + | T_drop t -> has_action_of_typ t + | T_with_comment _ t _ -> has_action_of_typ t + | T_nlist _ _ _ t -> has_action_of_typ t + | T_at_most _ _ t -> has_action_of_typ t + | T_exact _ _ t -> has_action_of_typ t + | T_string _ t _ -> has_action_of_dtyp t + | T_probe_then_validate _ t _ _ _ -> true let check_validity_of_typ_indexes (td:T.type_decl) indexes = let rec atomic_locs_of l = @@ -610,6 +643,7 @@ let translate_decls (en:env) (ds:T.decls) typ = typ; kind = td.decl_parser.p_kind; typ_indexes; + has_action = has_action_of_typ typ; allow_reading = ar; attrs = attrs; enum_typ = refined @@ -649,7 +683,7 @@ let print_dtyp (mname:string) (dt:dtyp) = | DT_IType i -> Printf.sprintf "(DT_IType %s)" (print_ityp i) - | DT_App _ hd args -> + | DT_App _ _ hd args -> Printf.sprintf "(%s %s)" (print_derived_name mname "dtyp" hd) (List.map (T.print_expr mname) args |> String.concat " ") @@ -664,9 +698,14 @@ let rec print_action (mname:string) (a:T.action) = let print_atomic_action (a:T.atomic_action) : ML string = match a with - | T.Action_return e -> - Printf.sprintf "(Action_return %s)" - (T.print_expr mname e) + | T.Action_return e -> ( + match fst e with + | T.Constant (A.Bool true) -> + "Action_return_true" + | _ -> + Printf.sprintf "(Action_return %s)" + (T.print_expr mname e) + ) | T.Action_abort -> "Action_abort" @@ -738,10 +777,12 @@ let rec print_typ (mname:string) (t:typ) fn (print_dtyp mname dt) - | T_pair fn t1 t2 -> - Printf.sprintf "(T_pair \"%s\" %s %s)" + | T_pair fn t1_const t1 t2_const t2 -> + Printf.sprintf "(T_pair \"%s\" %s %s %s %s)" fn + (if t1_const then "true" else "false") (print_typ mname t1) + (if t2_const then "true" else "false") (print_typ mname t2) | T_dep_pair fn t k -> @@ -813,10 +854,25 @@ let rec print_typ (mname:string) (t:typ) (print_typ mname t) c - | T_nlist fn n t -> - Printf.sprintf "(T_nlist \"%s\" %s %s)" + | T_nlist fn fixed_size n t -> + let is_const, n = + match T.as_constant n with + | None -> false, n + | Some m -> true, (T.Constant m, snd n) + in + let n_is_const = + if is_const + then + match fst n with + | T.Constant (A.Int _ n) -> Printf.sprintf "(Some %d)" n + | _ -> "None" + else "None" + in + Printf.sprintf "(T_nlist \"%s\" %s %s %b %s)" fn (T.print_expr mname n) + n_is_const + fixed_size (print_typ mname t) | T_at_most fn n t -> @@ -859,7 +915,7 @@ let print_type_decl mname (td:type_decl) = FStar.Printf.sprintf "[@@specialize; noextract_to \"krml\"]\n\ noextract\n\ - let def_%s = ( %s <: Tot (typ _ _ _ _ _) by (T.norm [delta_attr [`%%specialize]; zeta; iota; primops]; T.smt()))\n" + let def_%s = ( %s <: Tot (typ _ _ _ _ _ _) by (T.norm [delta_attr [`%%specialize]; zeta; iota; primops]; T.smt()))\n" (print_typedef_name mname td.name) (print_typ mname td.typ) @@ -895,7 +951,7 @@ let rec print_disj' mname (d:disj) let print_disj mname = print_index (print_disj' mname) let print_td_iface is_entrypoint mname root_name binders args - inv eloc disj ar pk_wk pk_nz = + inv eloc disj ha ar pk_wk pk_nz = let ar = if is_entrypoint then false else ar in let kind_t = Printf.sprintf "[@@noextract_to \"krml\"]\n\ @@ -909,11 +965,12 @@ let print_td_iface is_entrypoint mname root_name binders args let def'_t = Printf.sprintf "[@@noextract_to \"krml\"]\n\ noextract\n\ - val def'_%s %s: typ kind_%s %s %s %s %b" + val def'_%s %s: typ kind_%s %s %s %s %b %b" root_name binders root_name inv disj eloc + ha ar in let validator_t = @@ -972,13 +1029,14 @@ let print_binding mname (td:type_decl) "[@@specialize; noextract_to \"krml\"]\n\ noextract\n\ let def'_%s %s\n\ - : typ kind_%s %s %s %s %s\n\ + : typ kind_%s %s %s %s %b %b\n\ = coerce (_ by (coerce_validator [`%%kind_%s])) (def_%s %s)" root_name binders root_name inv disj eloc - (string_of_bool td.allow_reading) + td.has_action + td.allow_reading root_name root_name args @@ -1026,7 +1084,7 @@ let print_binding mname (td:type_decl) Printf.sprintf "[@@specialize; noextract_to \"krml\"]\n\ noextract\n\ let dtyp_%s %s\n\ - : dtyp kind_%s %b %s %s %s\n\ + : dtyp kind_%s %b %b %s %s %s\n\ = mk_dtyp_app\n\ kind_%s\n\ %s\n\ @@ -1036,10 +1094,11 @@ let print_binding mname (td:type_decl) (coerce (_ by (T.norm [delta_only [`%%type_%s]]; T.trefl())) (parser_%s %s))\n\ %s\n\ %b\n\ + %b\n\ (coerce (_ by %s) (validate_%s %s))\n\ (_ by (T.norm [delta_only [`%%Some?]; iota]; T.trefl()))\n" root_name binders - root_name td.allow_reading + root_name td.has_action td.allow_reading inv disj eloc root_name inv disj eloc @@ -1047,6 +1106,7 @@ let print_binding mname (td:type_decl) root_name root_name args reader + td.has_action td.allow_reading coerce_validator root_name args in @@ -1079,7 +1139,7 @@ let print_binding mname (td:type_decl) let iface = print_td_iface td.name.td_entrypoint mname root_name binders args - inv eloc disj td.allow_reading + inv eloc disj td.has_action td.allow_reading weak_kind k.pk_nz in impl, iface diff --git a/src/3d/InterpreterTarget.fsti b/src/3d/InterpreterTarget.fsti index 08c67f1de..5df605509 100644 --- a/src/3d/InterpreterTarget.fsti +++ b/src/3d/InterpreterTarget.fsti @@ -50,6 +50,7 @@ type dtyp : Type = i:itype -> dtyp | DT_App: + has_action:bool -> readable: bool -> hd:A.ident -> args:list expr -> @@ -58,8 +59,13 @@ type dtyp : Type = let allow_reader_of_dtyp (d: dtyp) : Tot bool = match d with | DT_IType i -> allow_reader_of_itype i - | DT_App readable _ _ -> readable + | DT_App _ readable _ _ -> readable +let has_action_of_dtyp d : Tot bool = + match d with + | DT_IType _ -> false + | DT_App has_action _ _ _ -> has_action + let readable_dtyp = (d: dtyp { allow_reader_of_dtyp d == true }) let non_empty_string = s:string { s <> "" } @@ -81,7 +87,9 @@ type typ : Type = | T_pair: fn:non_empty_string -> + k1_const: bool -> // whether t1 is a total compile-time constant size type without actions t1:typ -> + k2_const: bool -> t2:typ -> typ @@ -156,6 +164,7 @@ type typ : Type = | T_nlist: fn:non_empty_string -> + fixed_size_t:bool -> //does t have a fixed size? n:expr -> t:typ -> typ @@ -193,6 +202,7 @@ type type_decl = { typ : typ; kind : T.parser_kind; typ_indexes : typ_indexes; + has_action: bool; allow_reading: bool; attrs : (attrs: T.decl_attributes { attrs.is_entrypoint ==> ~ allow_reading }); enum_typ: option (t:T.typ {T.T_refine? t }) diff --git a/src/3d/Main.fst b/src/3d/Main.fst index 38ee8a757..90a948ed5 100644 --- a/src/3d/Main.fst +++ b/src/3d/Main.fst @@ -183,11 +183,8 @@ let emit_fstar_code_for_interpreter (en:env) module T = FStar.Tactics\n\ module A = EverParse3d.Actions.All\n\ module P = EverParse3d.Prelude\n\ - #push-options \"--fuel 0 --ifuel 0\"\n\ - #push-options \"--using_facts_from 'Prims FStar.UInt FStar.UInt8 \ - FStar.UInt16 FStar.UInt32 FStar.UInt64 \ - EverParse3d FStar.Int.Cast %s'\"\n" - modul maybe_open_external_api (all_modules |> String.concat " ") + #set-options \"--fuel 0 --ifuel 0 --ext context_pruning\"\n" + modul maybe_open_external_api in let fst_file = diff --git a/src/3d/Makefile b/src/3d/Makefile index d6c075bc7..2eea7ec41 100644 --- a/src/3d/Makefile +++ b/src/3d/Makefile @@ -18,7 +18,7 @@ export KRML_HOME?=$(realpath ../../../karamel) INCLUDE_PATHS= OTHERFLAGS?= -FSTAR=$(FSTAR_HOME)/bin/fstar.exe $(OTHERFLAGS) $(addprefix --include , $(INCLUDE_PATHS) $(EVERPARSE_HOME)/src/3d/prelude) --already_cached 'Prims FStar -FStarC' +FSTAR=$(FSTAR_HOME)/bin/fstar.exe --ext context_pruning $(OTHERFLAGS) $(addprefix --include , $(INCLUDE_PATHS) $(EVERPARSE_HOME)/src/3d/prelude) --already_cached 'Prims FStar -FStarC' all: 3d prelude diff --git a/src/3d/Simplify.fst b/src/3d/Simplify.fst index 810a2b7d8..a7c05a849 100644 --- a/src/3d/Simplify.fst +++ b/src/3d/Simplify.fst @@ -119,6 +119,7 @@ let simplify_atomic_field (env:T.env_t) (f:atomic_field) let ft = simplify_typ env sf.field_type in let fa = simplify_field_array env sf.field_array_opt in let fc = sf.field_constraint |> map_opt (simplify_expr env) in + let fp = sf.field_probe |> map_opt (fun fp -> { fp with probe_length=simplify_expr env fp.probe_length } ) in let fact = match sf.field_action with | None -> None @@ -127,7 +128,8 @@ let simplify_atomic_field (env:T.env_t) (f:atomic_field) let sf = { sf with field_type = ft; field_array_opt = fa; field_constraint = fc; - field_action = fact } in + field_action = fact; + field_probe = fp } in { f with v = sf } let rec simplify_field (env:T.env_t) (f:field) diff --git a/src/3d/Target.fst b/src/3d/Target.fst index 70042755d..f1a94d9e5 100644 --- a/src/3d/Target.fst +++ b/src/3d/Target.fst @@ -69,7 +69,7 @@ let rec parser_kind_eq k k' = match k.pk_kind, k'.pk_kind with | PK_return, PK_return -> true | PK_impos, PK_impos -> true - | PK_list, PK_list -> true + | PK_list k0 n0, PK_list k1 n1 -> parser_kind_eq k0 k1 && n0=n1 | PK_t_at_most, PK_t_at_most -> true | PK_t_exact, PK_t_exact -> true | PK_base hd1, PK_base hd2 -> A.(hd1.v = hd2.v) @@ -414,6 +414,14 @@ let rec print_typ (mname:string) (t:typ) : ML string = //(decreases t) = Printf.sprintf "(%s %s)" hd' (String.concat " " (print_indexes mname args)) + | T_nlist elt n -> + Printf.sprintf "(nlist %s %s)" + (print_expr mname n) + (print_typ mname elt) + | T_pair t1 t2 -> + Printf.sprintf "(%s & %s)" + (print_typ mname t1) + (print_typ mname t2) | T_dep_pair t1 (x, t2) -> Printf.sprintf "(%s:%s & %s)" (print_ident x) @@ -446,8 +454,12 @@ let rec print_kind (mname:string) (k:parser_kind) : Tot string = Printf.sprintf "%skind_%s" (maybe_mname_prefix mname hd) (print_ident hd) - | PK_list -> - "kind_nlist" + | PK_list k0 n -> + Printf.sprintf "(kind_nlist %s %s)" + (print_kind mname k0) + (match n with + | None -> "None" + | Some n -> Printf.sprintf "(Some %d)" n) | PK_t_at_most -> "kind_t_at_most" | PK_t_exact -> diff --git a/src/3d/Target.fsti b/src/3d/Target.fsti index 3495b679e..f20de5c14 100644 --- a/src/3d/Target.fsti +++ b/src/3d/Target.fsti @@ -61,6 +61,32 @@ type expr' = and expr = expr' & A.range +let rec as_constant n = + match fst n with + | Constant (A.Int sw i) -> + Some (A.Int sw i) + | App (Cast _from to) [ n ] -> ( + match as_constant n with + | Some (A.Int _ i) -> Some (A.Int to i) + | _ -> None + ) + | App (Plus _) [ n; m ] -> ( + match as_constant n, as_constant m with + | Some (A.Int sw i), Some (A.Int _ j) -> Some (A.Int sw (i + j)) + | _ -> None + ) + | App (Minus _) [ n; m ] -> ( + match as_constant n, as_constant m with + | Some (A.Int sw i), Some (A.Int _ j) -> Some (A.Int sw (i - j)) + | _ -> None + ) + | App (Mul _) [ n; m ] -> ( + match as_constant n, as_constant m with + | Some (A.Int sw i), Some (A.Int _ j) -> Some (A.Int sw (i `op_Multiply` j)) + | _ -> None + ) + | _ -> None + let subst = list (A.ident' & expr) val subst_expr (s:subst) (e:expr) : expr let mk_expr (e:expr') = e, A.dummy_range @@ -93,6 +119,8 @@ noeq type typ = | T_false : typ | T_app : hd:A.ident -> A.t_kind -> args:list index -> typ + | T_nlist : elt: typ -> bytesize: expr -> typ + | T_pair : fst: typ -> snd: typ -> typ | T_dep_pair : dfst:typ -> dsnd:(A.ident & typ) -> typ | T_refine : base:typ -> refinement:lam expr -> typ | T_if_else : e:expr -> t:typ -> f:typ -> typ @@ -154,7 +182,7 @@ type parser_kind' = | PK_return | PK_impos | PK_base : hd:A.ident -> parser_kind' - | PK_list : parser_kind' + | PK_list : elt_kind:parser_kind -> option nat -> parser_kind' | PK_t_at_most: parser_kind' | PK_t_exact : parser_kind' | PK_filter : k:parser_kind -> parser_kind' @@ -165,7 +193,7 @@ type parser_kind' = and parser_kind = { pk_kind : parser_kind'; pk_weak_kind : A.weak_kind ; - pk_nz: bool + pk_nz: bool; } val expr_eq (e1 e1:expr) : bool @@ -177,10 +205,10 @@ noeq type parser' = | Parse_return : v:expr -> parser' | Parse_app : hd:A.ident -> args:list index -> parser' - | Parse_nlist : n:expr -> t:parser -> parser' + | Parse_nlist : t_size_constant:bool -> n:expr -> t:parser -> parser' | Parse_t_at_most : n:expr -> t:parser -> parser' | Parse_t_exact : n:expr -> t:parser -> parser' - | Parse_pair : n1: A.ident -> p:parser -> q:parser -> parser' + | Parse_pair : n1: A.ident -> p_is_const: bool -> p:parser -> q_is_const: bool -> q:parser -> parser' // p_is_const, q_is_const record whether p and q are total compile-time constant size parsers | Parse_dep_pair : n1: A.ident -> p:parser -> k:lam parser -> parser' | Parse_dep_pair_with_refinement: n1: A.ident -> dfst:parser -> refinement:lam expr -> dsnd:lam parser -> parser' | Parse_dep_pair_with_action: dfst:parser -> a:lam action -> dsnd:lam parser -> parser' diff --git a/src/3d/TranslateForInterpreter.fst b/src/3d/TranslateForInterpreter.fst index cc4ea426f..1d90709e4 100644 --- a/src/3d/TranslateForInterpreter.fst +++ b/src/3d/TranslateForInterpreter.fst @@ -128,8 +128,8 @@ let pk_base id nz wk = T.({ pk_weak_kind = wk; pk_nz = nz }) -let pk_list = T.({ - pk_kind = PK_list; +let pk_list k0 n = T.({ + pk_kind = PK_list k0 n; pk_weak_kind = WeakKindStrongPrefix; pk_nz = false }) @@ -164,6 +164,30 @@ let pk_glb k1 k2 = T.({ pk_nz = k1.pk_nz && k2.pk_nz }) +let rec is_compile_time_fixed_size (env:global_env) (t:T.typ) +: ML bool += match t with + | T.T_false -> true + | T.T_app hd _ _ -> + begin + try + let size = TypeSizes.size_of_typename env.size_env hd in + TS.Fixed? size + with _ -> false + end + | T.T_pointer _ -> true + | T.T_refine base _ -> is_compile_time_fixed_size env base + | T.T_with_comment t _ -> is_compile_time_fixed_size env t + | T.T_nlist elt n -> // this is the main reason why we need T.T_pair + if Some? (T.as_constant n) + then is_compile_time_fixed_size env elt + else false + | T.T_pair t1 t2 -> // this is the main reason why we need T.T_pair + if is_compile_time_fixed_size env t1 + then is_compile_time_fixed_size env t2 + else false + | _ -> false + let false_typ = T.T_false let unit_typ = T.T_app (with_dummy_range (to_ident' "unit")) KindSpec [] @@ -173,20 +197,22 @@ let unit_parser = let unit_id = with_dummy_range (to_ident' "unit") in mk_parser pk_return unit_typ unit_id "none" (T.Parse_return unit_val) let pair_typ t1 t2 = - T.T_app (with_dummy_range (to_ident' "tuple2")) KindSpec [Inl t1; Inl t2] + T.T_pair t1 t2 let pair_value x y = T.Record (with_dummy_range (to_ident' "tuple2")) [(with_dummy_range (to_ident' "fst"), T.mk_expr (T.Identifier x)); - (with_dummy_range (to_ident' "snd"), T.mk_expr (T.Identifier y))] -let pair_parser n1 p1 p2 = + (with_dummy_range (to_ident' "snd"), T.mk_expr (T.Identifier y))] +let pair_parser env n1 p1 p2 = let open T in let pt = pair_typ p1.p_typ p2.p_typ in let t_id = with_dummy_range (to_ident' "tuple2") in + let p1_is_const = is_compile_time_fixed_size env p1.p_typ in + let p2_is_const = is_compile_time_fixed_size env p2.p_typ in mk_parser (pk_and_then p1.p_kind p2.p_kind) pt t_id (ident_to_string n1) - (Parse_pair n1 p1 p2) + (Parse_pair n1 p1_is_const p1 p2_is_const p2) let dep_pair_typ t1 (t2:(A.ident & T.typ)) : T.typ = T.T_dep_pair t1 t2 let dep_pair_value x y : T.expr = @@ -243,9 +269,7 @@ let dep_pair_with_action_parser p1 (a:T.lam T.action) (p2:A.ident & T.parser) = let extend_fieldname fieldname e = Printf.sprintf "%s.%s" fieldname e let ite_parser typename fieldname (e:T.expr) (then_:T.parser) (else_:T.parser) : ML T.parser = let k, p1, p2 = - if T.parser_kind_eq then_.p_kind else_.p_kind - then then_.p_kind, then_, else_ - else let k = pk_glb then_.p_kind else_.p_kind in + let k = pk_glb then_.p_kind else_.p_kind in k, mk_parser k then_.p_typ typename (extend_fieldname fieldname "case_left") (T.Parse_weaken_right then_ else_.p_kind), mk_parser k else_.p_typ typename (extend_fieldname fieldname "case_right") (T.Parse_weaken_left else_ then_.p_kind) @@ -464,13 +488,19 @@ let rec parse_typ (env:global_env) | T.T_app _ A.KindExtern _ -> failwith "Impossible, did not expect parse_typ to be called with an output/extern type!" - | T.T_app {v={name="nlist"}} KindSpec [Inr e; Inl t] -> - let pt = parse_typ env typename (extend_fieldname "element") t in - mk_parser pk_list + | T.T_nlist telt e -> + let pt = parse_typ env typename (extend_fieldname "element") telt in + let t_size_constant = is_compile_time_fixed_size env telt in + let n_is_const = + match T.as_constant e with + | Some (A.Int _ n) -> if n >= 0 then Some n else None + | _ -> None + in + mk_parser (pk_list pt.p_kind n_is_const) t typename fieldname - (T.Parse_nlist e pt) + (T.Parse_nlist t_size_constant e pt) | T.T_app {v={name="t_at_most"}} KindSpec [Inr e; Inl t] -> let pt = parse_typ env typename (extend_fieldname "element") t in @@ -513,6 +543,11 @@ let rec parse_typ (env:global_env) let p2 = parse_typ env typename fieldname t2 in ite_parser typename fieldname e p1 p2 + | T.T_pair t1 t2 -> + pair_parser env typename + (parse_typ env typename (extend_fieldname "first") t1) + (parse_typ env typename (extend_fieldname "second") t1) + | T.T_dep_pair t1 (x, t2) -> dep_pair_parser typename (parse_typ env typename (extend_fieldname "first") t1) (x, parse_typ env typename (extend_fieldname "second") t2) @@ -695,12 +730,12 @@ let rec parser_is_constant_size_without_actions -> true | T.Parse_app hd _ -> parser_kind_is_constant_size env hd - | T.Parse_nlist array_size parse_elem + | T.Parse_nlist _ array_size parse_elem -> begin match fst array_size with | T.Constant (A.Int _ array_size) -> parser_is_constant_size_without_actions env parse_elem | _ -> false end - | T.Parse_pair _ hd tl + | T.Parse_pair _ _ hd _ tl -> if parser_is_constant_size_without_actions env hd then parser_is_constant_size_without_actions env tl else false @@ -772,7 +807,7 @@ let translate_atomic_field (f:A.atomic_field) : ML (T.struct_field & T.decls) = | FieldArrayQualified (e, ByteArrayByteSize) | FieldArrayQualified (e, ArrayByteSize) -> let e = translate_expr e in - T.T_app (with_range (to_ident' "nlist") sf.field_type.range) KindSpec [Inr e; Inl t] + T.T_nlist t e | FieldArrayQualified (e, ArrayByteSizeAtMost) -> mk_at_most t e | FieldArrayQualified (e, ArrayByteSizeSingleElementArray) -> @@ -885,7 +920,7 @@ let parse_grouped_fields (env:global_env) (typename:A.ident) (gfs:grouped_fields parse_typ sf.sf_ident sf.sf_typ | Some rest -> - pair_parser sf.sf_ident + pair_parser env sf.sf_ident (parse_typ sf.sf_ident sf.sf_typ) (aux rest) ) @@ -902,7 +937,7 @@ let parse_grouped_fields (env:global_env) (typename:A.ident) (gfs:grouped_fields aux gfs | Some rest -> - pair_parser id + pair_parser env id (aux gfs) (aux rest) ) @@ -972,7 +1007,12 @@ let rec hoist_typ = let open T in match t with | T_false -> [], t + | T_nlist _ _ | T_app _ _ _ -> [], t + | T_pair t1 t2 -> + let ds, t1 = hoist_typ fn genv env t1 in + let ds', t2 = hoist_typ fn genv env t2 in + ds@ds', T_pair t1 t2 | T_dep_pair t1 (x, t2) -> let ds, t1 = hoist_typ fn genv env t1 in let ds', t2 = hoist_typ fn genv ((x, t1)::env) t2 in diff --git a/src/3d/TypeSizes.fst b/src/3d/TypeSizes.fst index 4fc2eff43..9fa2704e9 100644 --- a/src/3d/TypeSizes.fst +++ b/src/3d/TypeSizes.fst @@ -300,7 +300,7 @@ let rec size_and_alignment_of_field (env:env_t) : ML (size & alignment & list field) = let field, field_size, field_alignment = size_and_alignment_of_field env should_align diag_enclosing_type_name f in let pad_size, padding_field = - let msg = Printf.sprintf "(preceding field %s)" (print_field field) in + let msg = Printf.sprintf "(preceding field %s)" (print_field' field false) in alignment_padding env should_align diag_enclosing_type_name msg offset field_alignment in let offset = @@ -390,7 +390,7 @@ let rec size_and_alignment_of_field (env:env_t) if all_cases_fixed && not (Fixed? size) then error - "With the --align option, \ + "With the 'aligned' qualifier, \ all cases of a union with a fixed size \ must have the same size; \ union padding is not yet supported" diff --git a/src/3d/Z3TestGen.fst b/src/3d/Z3TestGen.fst index 3b44b3fa5..d7e4aa523 100644 --- a/src/3d/Z3TestGen.fst +++ b/src/3d/Z3TestGen.fst @@ -516,7 +516,7 @@ let parse_readable_dtyp : Tot (parser reading) = match d with | I.DT_IType i -> parse_readable_itype i - | I.DT_App _ hd args -> parse_readable_app hd args + | I.DT_App _ _ hd args -> parse_readable_app hd args let parse_not_readable_app' (hd: string) @@ -537,7 +537,7 @@ let parse_dtyp then wrap_parser (parse_readable_dtyp d) else match d with | I.DT_IType i -> parse_itype i - | I.DT_App _ hd args -> parse_not_readable_app hd args + | I.DT_App _ _ hd args -> parse_not_readable_app hd args let parse_false : parser not_reading = maybe_toplevel_parser (fun _ _ _ _ -> { call = "parse-false" }) @@ -912,7 +912,7 @@ let rec typ_depth (t: I.typ) : GTot nat = match t with | I.T_if_else _ t1 t2 // 2 accounts for the call to parse_then_else_with_branch_trace -> 2 + typ_depth t1 + typ_depth t2 - | I.T_pair _ t1 t2 + | I.T_pair _ _ t1 _ t2 -> 1 + typ_depth t1 + typ_depth t2 | I.T_dep_pair _ _ (_, t') | I.T_dep_pair_with_action _ _ (_, t') _ @@ -923,7 +923,7 @@ let rec typ_depth (t: I.typ) : GTot nat | I.T_with_comment _ t' _ | I.T_at_most _ _ t' | I.T_exact _ _ t' - | I.T_nlist _ _ t' + | I.T_nlist _ _ _ t' -> 1 + typ_depth t' | I.T_with_dep_action _ _ _ | I.T_refine _ _ _ @@ -941,7 +941,7 @@ let rec parse_typ (t : I.typ) : Tot (parser not_reading) | I.T_false _ -> parse_false | I.T_with_dep_action _ d _ | I.T_denoted _ d -> parse_denoted d - | I.T_pair _ t1 t2 -> parse_pair (parse_typ t1) (parse_typ t2) + | I.T_pair _ _ t1 _ t2 -> parse_pair (parse_typ t1) (parse_typ t2) | I.T_dep_pair_with_action _ t1 (lam, t2) _ | I.T_dep_pair _ t1 (lam, t2) -> parse_dep_pair (parse_readable_dtyp t1) lam (parse_typ t2) | I.T_refine_with_action _ base (lam, cond) _ @@ -955,7 +955,7 @@ let rec parse_typ (t : I.typ) : Tot (parser not_reading) | I.T_at_most _ size body -> parse_at_most (fun _ -> mk_expr size) (parse_typ body) | I.T_exact _ size body -> parse_exact (fun _ -> mk_expr size) (parse_typ body) | I.T_string _ elt terminator -> parse_string (parse_readable_dtyp elt) (fun _ -> mk_expr terminator) - | I.T_nlist _ size body -> + | I.T_nlist _ _ size body -> if match body with | I.T_denoted _ (I.DT_IType i) -> Some? (itype_byte_size i) | _ -> false diff --git a/src/3d/prelude/EverParse3d.Actions.All.fsti b/src/3d/prelude/EverParse3d.Actions.All.fsti index 9f0bea8af..18735bedb 100644 --- a/src/3d/prelude/EverParse3d.Actions.All.fsti +++ b/src/3d/prelude/EverParse3d.Actions.All.fsti @@ -10,7 +10,7 @@ noextract inline_for_extraction val action_field_ptr (u:squash (EverParse3d.Actions.BackendFlag.backend_flag == BackendFlagBuffer)) - : action true_inv disjointness_trivial eloc_none true ___PUINT8 + : action true_inv disjointness_trivial eloc_none true false ___PUINT8 noextract inline_for_extraction @@ -18,7 +18,7 @@ val action_field_ptr_after (u:squash (EverParse3d.Actions.BackendFlag.backend_flag == BackendFlagExtern)) (sz: FStar.UInt64.t) (write_to: bpointer ___PUINT8) - : action (ptr_inv write_to) disjointness_trivial (ptr_loc write_to) false bool // if action returns true, writes some value to write_to. if false, do nothing + : action (ptr_inv write_to) disjointness_trivial (ptr_loc write_to) false false bool // if action returns true, writes some value to write_to. if false, do nothing noextract inline_for_extraction @@ -27,10 +27,10 @@ val action_field_ptr_after_with_setter (sz: FStar.UInt64.t) (#output_loc: eloc) (write_to: (___PUINT8 -> Tot (external_action unit output_loc))) - : action true_inv disjointness_trivial output_loc false bool // if action returns true, writes some value to write_to. if false, do nothing + : action true_inv disjointness_trivial output_loc false false bool // if action returns true, writes some value to write_to. if false, do nothing noextract inline_for_extraction val action_field_pos_32 (u:squash (EverParse3d.Actions.BackendFlag.backend_flag == BackendFlagBuffer)) - : action true_inv disjointness_trivial eloc_none false FStar.UInt32.t + : action true_inv disjointness_trivial eloc_none false false FStar.UInt32.t diff --git a/src/3d/prelude/EverParse3d.Actions.Base.fst b/src/3d/prelude/EverParse3d.Actions.Base.fst index 7e68e3734..6860cb70c 100644 --- a/src/3d/prelude/EverParse3d.Actions.Base.fst +++ b/src/3d/prelude/EverParse3d.Actions.Base.fst @@ -116,6 +116,9 @@ let app_ctxt = AppCtxt.app_ctxt let app_loc (x:AppCtxt.app_ctxt) (l:eloc) : eloc = AppCtxt.properties x; AppCtxt.loc_of x `loc_union` l +let app_loc_fp (x:AppCtxt.app_ctxt) (has_action:bool) (l:eloc) : eloc = + if has_action then AppCtxt.ghost_loc_of x `loc_union` app_loc x l + else app_loc x l inline_for_extraction noextract @@ -135,9 +138,9 @@ let error_handler = I.live sl h /\ true_inv h /\ B.live h ctxt /\ - loc_not_unused_in h `loc_includes` app_loc ctxt eloc_none /\ - address_liveness_insensitive_locs `loc_includes` app_loc ctxt eloc_none /\ - app_loc ctxt eloc_none `loc_disjoint` I.footprint sl /\ + loc_not_unused_in h `loc_includes` app_loc_fp ctxt true eloc_none /\ + address_liveness_insensitive_locs `loc_includes` app_loc_fp ctxt true eloc_none /\ + app_loc_fp ctxt true eloc_none `loc_disjoint` I.footprint sl /\ U64.v pos <= Seq.length (I.get_read sl h) ) (ensures fun h0 _ h1 -> @@ -147,7 +150,7 @@ let error_handler = true_inv h1) let action - inv disj l on_success a + inv disj l on_success returns_true a = (# [EverParse3d.Util.solve_from_ctx ()] I.extra_t #input_buffer_t) -> ctxt: app_ctxt -> error_handler_fn : error_handler -> @@ -161,17 +164,19 @@ let action disj /\ inv h /\ B.live h ctxt /\ - loc_not_unused_in h `loc_includes` app_loc ctxt l /\ - address_liveness_insensitive_locs `loc_includes` app_loc ctxt l /\ - app_loc ctxt l `loc_disjoint` I.footprint sl /\ + B.live h (AppCtxt.action_ghost_ptr ctxt) /\ + loc_not_unused_in h `loc_includes` app_loc_fp ctxt true l /\ + address_liveness_insensitive_locs `loc_includes` app_loc_fp ctxt true l /\ + app_loc_fp ctxt true l `loc_disjoint` I.footprint sl /\ U64.v pos <= U64.v posf /\ U64.v posf == Seq.length (I.get_read sl h) ) - (ensures fun h0 _ h1 -> + (ensures fun h0 res h1 -> let sl = Ghost.reveal sl in - modifies (app_loc ctxt l) h0 h1 /\ + modifies (app_loc_fp ctxt true l) h0 h1 /\ B.live h1 ctxt /\ - inv h1) + inv h1 /\ + (returns_true ==> res === true)) module LP = LowParse.Spec.Base module LPL = LowParse.Low.Base @@ -237,6 +242,7 @@ let validate_with_action_t' (inv:slice_inv) (disj:disjointness_pre) (l:eloc) + (has_action:bool) (allow_reading:bool) : Type = (# [EverParse3d.Util.solve_from_ctx ()] I.extra_t #input_buffer_t) -> @@ -251,14 +257,15 @@ let validate_with_action_t' disj /\ inv h /\ B.live h ctxt /\ - loc_not_unused_in h `loc_includes` app_loc ctxt l /\ - address_liveness_insensitive_locs `loc_includes` app_loc ctxt l /\ + B.live h (AppCtxt.action_ghost_ptr ctxt) /\ + loc_not_unused_in h `loc_includes` app_loc_fp ctxt true l /\ + address_liveness_insensitive_locs `loc_includes` app_loc_fp ctxt true l /\ U64.v pos == Seq.length (I.get_read sl h) /\ - app_loc ctxt l `loc_disjoint` I.footprint sl + app_loc_fp ctxt true l `loc_disjoint` I.footprint sl ) (ensures fun h res h' -> I.live sl h' /\ - modifies (app_loc ctxt l `loc_union` I.perm_footprint sl) h h' /\ + modifies (app_loc_fp ctxt has_action l `loc_union` I.perm_footprint sl) h h' /\ inv h' /\ B.live h' ctxt /\ (((~ allow_reading) \/ LPE.is_error res) ==> U64.v (LPE.get_validator_error_pos res) == Seq.length (I.get_read sl h')) /\ @@ -277,7 +284,7 @@ let validate_with_action_t' end ) -let validate_with_action_t p inv disj l allow_reading = validate_with_action_t' p inv disj l allow_reading +let validate_with_action_t p inv disj l has_action allow_reading = validate_with_action_t' p inv disj l has_action allow_reading let validate_eta v = fun ctxt error_handler_fn sl pos -> v ctxt error_handler_fn sl pos @@ -324,15 +331,15 @@ let validate_with_success_action' (name: string) #nz #wk (#k1:parser_kind nz wk) #t1 (#p1:parser k1 t1) - (#inv1:_) (#disj1:_) (#l1:eloc) - (v1:validate_with_action_t p1 inv1 disj1 l1 false) - (#inv2:_) (#disj2:_) (#l2:eloc) #b - (a:action inv2 disj2 l2 b bool) + (#inv1:_) (#disj1:_) (#l1:eloc) #ha + (v1:validate_with_action_t p1 inv1 disj1 l1 ha false) + (#inv2:_) (#disj2:_) (#l2:eloc) #b #rt + (a:action inv2 disj2 l2 b rt bool) : validate_with_action_t p1 (conj_inv inv1 inv2) (conj_disjointness disj1 disj2) (l1 `eloc_union` l2) - false + true false = fun ctxt error_handler_fn input input_length start_position -> [@inline_let] let pos0 = start_position in let h0 = HST.get () in @@ -346,9 +353,13 @@ let validate_with_success_action' let b = a ctxt error_handler_fn input input_length pos0 pos1 in let h2 = HST.get () in modifies_address_liveness_insensitive_unused_in h1 h2; - if not b - then LPE.set_validator_error_pos LPE.validator_error_action_failed pos1 - else pos1 + if rt + then pos1 + else ( + if not b + then LPE.set_validator_error_pos LPE.validator_error_action_failed pos1 + else pos1 + ) else pos1 @@ -360,9 +371,9 @@ let validate_drop_true (#p:LP.parser k t) (#inv:slice_inv) (#disj:disjointness_pre) - (#l:eloc) - (v: validate_with_action_t' p inv disj l true) -: Tot (validate_with_action_t' p inv disj l false) + (#l:eloc) #ha + (v: validate_with_action_t' p inv disj l ha true) +: Tot (validate_with_action_t' p inv disj l ha false) = fun ctxt error_handler_fn input input_length start_position -> [@inline_let] let pos = start_position in let res = v ctxt error_handler_fn input input_length pos in @@ -378,9 +389,9 @@ let validate_drop (#inv:slice_inv) (#disj:disjointness_pre) (#l:eloc) - #allow_reading - (v: validate_with_action_t' p inv disj l allow_reading) -: Tot (validate_with_action_t' p inv disj l false) + #ha #allow_reading + (v: validate_with_action_t' p inv disj l ha allow_reading) +: Tot (validate_with_action_t' p inv disj l ha false) = if allow_reading then validate_drop_true v else v @@ -402,9 +413,9 @@ let validate_with_error_handler (#p1:parser k1 t1) (#inv1 #disj1:_) (#l1:eloc) - (#ar:_) - (v1:validate_with_action_t p1 inv1 disj1 l1 ar) - : validate_with_action_t p1 inv1 disj1 l1 ar + (#ha #ar:_) + (v1:validate_with_action_t p1 inv1 disj1 l1 ha ar) + : validate_with_action_t p1 inv1 disj1 l1 ha ar = fun ctxt error_handler_fn input input_length start_position -> [@inline_let] let pos0 = start_position in let h0 = HST.get () in @@ -421,7 +432,7 @@ let validate_with_error_handler inline_for_extraction noextract let validate_ret - : validate_with_action_t (parse_ret ()) true_inv disjointness_trivial eloc_none true + : validate_with_action_t (parse_ret ()) true_inv disjointness_trivial eloc_none false true = fun ctxt error_handler_fn input input_length start_position -> start_position @@ -429,14 +440,76 @@ let validate_ret module LPC = LowParse.Spec.Combinators +inline_for_extraction +noextract +let validate_total_constant_size_no_read' + (#k: LP.parser_kind) + (#t: Type) + (p: LP.parser k t) + (sz: U64.t) + (u: unit { + k.LP.parser_kind_high == Some k.LP.parser_kind_low /\ + k.LP.parser_kind_low == U64.v sz /\ + k.LP.parser_kind_metadata == Some LP.ParserKindMetadataTotal + }) + inv disj l +: validate_with_action_t' p inv disj l false true += fun ctxt error_handler_fn input input_length start_position -> + [@inline_let] let pos = start_position in + let h = HST.get () in + LP.parser_kind_prop_equiv k p; + let hasBytes = I.has input input_length pos sz in + let h2 = HST.get () in + modifies_address_liveness_insensitive_unused_in h h2; + if hasBytes + then pos `U64.add` sz + else LPE.set_validator_error_pos LPE.validator_error_not_enough_data pos + +inline_for_extraction +noextract +let validate_total_constant_size_no_read + #nz #wk + (#k: parser_kind nz wk) + (#t: Type) + (p: parser k t) + (sz: U64.t) + (u: unit { + k.LP.parser_kind_high == Some k.LP.parser_kind_low /\ + k.LP.parser_kind_low == U64.v sz /\ + k.LP.parser_kind_metadata == Some LP.ParserKindMetadataTotal + }) + inv disj l +: Tot (validate_with_action_t p inv disj l false true) += validate_total_constant_size_no_read' p sz u inv disj l + inline_for_extraction noextract let validate_pair (name1: string) #nz1 (#k1:parser_kind nz1 WeakKindStrongPrefix) #t1 (#p1:parser k1 t1) - (#inv1 #disj1:_) (#l1:eloc) (#ar1:_) (v1:validate_with_action_t p1 inv1 disj1 l1 ar1) + (k1_const: bool) + (#inv1 #disj1:_) (#l1:eloc) (#ha1 #ar1:_) (v1:validate_with_action_t p1 inv1 disj1 l1 ha1 ar1) #nz2 #wk2 (#k2:parser_kind nz2 wk2) #t2 (#p2:parser k2 t2) - (#inv2 #disj2:_) (#l2:eloc) (#ar2:_) (v2:validate_with_action_t p2 inv2 disj2 l2 ar2) - = fun ctxt error_handler_fn input input_length start_position -> + (k2_const: bool) + (#inv2 #disj2:_) (#l2:eloc) (#ha2 #ar2:_) (v2:validate_with_action_t p2 inv2 disj2 l2 ha2 ar2) + : validate_with_action_t + (p1 `parse_pair` p2) + (conj_inv inv1 inv2) + (conj_disjointness disj1 disj2) + (l1 `eloc_union` l2) + (ha1 || ha2) + false + = + if k1_const && k2_const && + (not ha1) && (not ha2) && // IMPORTANT: do not erase actions from v1, v2 + k1.parser_kind_high = Some k1.parser_kind_low && + k1.parser_kind_metadata = Some LP.ParserKindMetadataTotal && + k2.parser_kind_high = Some k2.parser_kind_low && + k2.parser_kind_metadata = Some LP.ParserKindMetadataTotal && + k1.parser_kind_low + k2.parser_kind_low < 4294967296 + then + validate_drop (validate_total_constant_size_no_read (p1 `parse_pair` p2) (U64.uint_to_t (k1.parser_kind_low + k2.parser_kind_low)) () (conj_inv inv1 inv2) (conj_disjointness disj1 disj2) (l1 `eloc_union` l2)) + else + fun ctxt error_handler_fn input input_length start_position -> [@inline_let] let pos = start_position in let h = HST.get () in LPC.nondep_then_eq p1 p2 (I.get_remaining input h); @@ -454,9 +527,9 @@ inline_for_extraction noextract let validate_dep_pair (name1: string) #nz1 (#k1:parser_kind nz1 _) #t1 (#p1:parser k1 t1) - #inv1 #disj1 #l1 (v1:validate_with_action_t p1 inv1 disj1 l1 true) (r1: leaf_reader p1) + #inv1 #disj1 #l1 #ha1 (v1:validate_with_action_t p1 inv1 disj1 l1 ha1 true) (r1: leaf_reader p1) #nz2 #wk2 (#k2:parser_kind nz2 wk2) (#t2:t1 -> Type) (#p2:(x:t1 -> parser k2 (t2 x))) - #inv2 #disj2 #l2 #ar2 (v2:(x:t1 -> validate_with_action_t (p2 x) inv2 disj2 l2 ar2)) + #inv2 #disj2 #l2 #ha2 #ar2 (v2:(x:t1 -> validate_with_action_t (p2 x) inv2 disj2 l2 ha2 ar2)) = fun ctxt error_handler_fn input input_length start_position -> [@inline_let] let pos = start_position in let h = HST.get () in @@ -484,18 +557,19 @@ inline_for_extraction noextract let validate_dep_pair_with_refinement_and_action' (name1: string) (#nz1: _) (#k1:parser_kind nz1 _) (#t1: _) (#p1:parser k1 t1) - (#inv1 #disj1 #l1: _) (v1:validate_with_action_t p1 inv1 disj1 l1 true) (r1: leaf_reader p1) + (#inv1 #disj1 #l1 #ha1: _) (v1:validate_with_action_t p1 inv1 disj1 l1 ha1 true) (r1: leaf_reader p1) (f: t1 -> bool) - (#inv1' #disj1' #l1' #b: _) (a:t1 -> action inv1' disj1' l1' b bool) + (#inv1' #disj1' #l1' #b #rt: _) (a:t1 -> action inv1' disj1' l1' b rt bool) (#nz2 #wk2: _) (#k2:parser_kind nz2 wk2) (#t2:refine _ f -> Type) (#p2:(x:refine _ f) -> parser k2 (t2 x)) - (#inv2 #disj2 #l2 #ar2: _) (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 ar2)) + (#inv2 #disj2 #l2 #ha2 #ar2: _) (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 ha2 ar2)) : validate_with_action_t ((p1 `LPC.parse_filter` f) `(parse_dep_pair #nz1)` p2) (conj_inv inv1 (conj_inv inv1' inv2)) (conj_disjointness disj1 (conj_disjointness disj1' disj2)) (l1 `eloc_union` (l1' `eloc_union` l2)) + true false = fun ctxt error_handler_fn input input_length startPosition -> let h0 = HST.get () in @@ -523,13 +597,22 @@ let validate_dep_pair_with_refinement_and_action' res1 else begin modifies_address_liveness_insensitive_unused_in h1 h2; - if not (a field_value ctxt error_handler_fn input input_length startPosition res1) - then LPE.set_validator_error_pos LPE.validator_error_action_failed res1 //action failed - else begin + let action_result = a field_value ctxt error_handler_fn input input_length startPosition res1 in + if rt + then ( let h15 = HST.get () in let _ = modifies_address_liveness_insensitive_unused_in h0 h15 in validate_drop (v2 field_value) ctxt error_handler_fn input input_length res1 - end + ) + else ( + if not action_result + then LPE.set_validator_error_pos LPE.validator_error_action_failed res1 //action failed + else begin + let h15 = HST.get () in + let _ = modifies_address_liveness_insensitive_unused_in h0 h15 in + validate_drop (v2 field_value) ctxt error_handler_fn input input_length res1 + end + ) end end @@ -540,15 +623,16 @@ let validate_dep_pair_with_refinement_and_action_total_zero_parser' (#t1: _) (#p1:parser k1 t1) (r1: leaf_reader p1) (inv1 disj1 l1: _) (f: t1 -> bool) - (#inv1' #disj1' #l1' #b: _) (a:t1 -> action inv1' disj1' l1' b bool) + (#inv1' #disj1' #l1' #b #rt: _) (a:t1 -> action inv1' disj1' l1' b rt bool) (#nz2 #wk2: _) (#k2:parser_kind nz2 wk2) (#t2:refine _ f -> Type) (#p2:(x:refine _ f -> parser k2 (t2 x))) - (#inv2 #disj2 #l2 #ar2: _) (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 ar2)) + (#inv2 #disj2 #l2 #ha2 #ar2: _) (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 ha2 ar2)) : Pure (validate_with_action_t ((p1 `LPC.parse_filter` f) `(parse_dep_pair #nz1)` p2) (conj_inv inv1 (conj_inv inv1' inv2)) (conj_disjointness disj1 (conj_disjointness disj1' disj2)) (l1 `eloc_union` (l1' `eloc_union` l2)) + true false) (requires ( let open LP in @@ -574,13 +658,22 @@ let validate_dep_pair_with_refinement_and_action_total_zero_parser' res1 else let h2 = HST.get() in modifies_address_liveness_insensitive_unused_in h0 h2; - if not (a field_value ctxt error_handler_fn input input_length startPosition res1) - then LPE.set_validator_error_pos LPE.validator_error_action_failed startPosition //action failed - else begin + let action_result = a field_value ctxt error_handler_fn input input_length startPosition res1 in + if rt + then ( + let h15 = HST.get () in + let _ = modifies_address_liveness_insensitive_unused_in h0 h15 in + validate_drop (v2 field_value) ctxt error_handler_fn input input_length res1 + ) + else ( + if not action_result + then LPE.set_validator_error_pos LPE.validator_error_action_failed startPosition //action failed + else begin let h15 = HST.get () in let _ = modifies_address_liveness_insensitive_unused_in h0 h15 in validate_drop (v2 field_value) ctxt error_handler_fn input input_length res1 - end + end + ) end inline_for_extraction noextract @@ -588,12 +681,12 @@ let validate_dep_pair_with_refinement_and_action (p1_is_constant_size_without_actions: bool) (name1: string) #nz1 (#k1:parser_kind nz1 _) #t1 (#p1:parser k1 t1) - #inv1 #disj1 #l1 (v1:validate_with_action_t p1 inv1 disj1 l1 true) + #inv1 #disj1 #l1 #ha1 (v1:validate_with_action_t p1 inv1 disj1 l1 ha1 true) (r1: leaf_reader p1) (f: t1 -> bool) - #inv1' #disj1' #l1' #b (a:t1 -> action inv1' disj1' l1' b bool) + #inv1' #disj1' #l1' #b #rt (a:t1 -> action inv1' disj1' l1' b rt bool) #nz2 #wk2 (#k2:parser_kind nz2 wk2) (#t2:refine _ f -> Type) (#p2:(x:refine _ f -> parser k2 (t2 x))) - #inv2 #disj2 #l2 #ar2 (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 ar2)) + #inv2 #disj2 #l2 #ha2 #ar2 (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 ha2 ar2)) = if p1_is_constant_size_without_actions `LP.bool_and` (k1.LP.parser_kind_high = Some 0) `LP.bool_and` @@ -607,10 +700,10 @@ let validate_dep_pair_with_refinement_and_action inline_for_extraction noextract let validate_dep_pair_with_action #nz1 (#k1:parser_kind nz1 _) #t1 (#p1:parser k1 t1) - #inv1 #disj1 #l1 (v1:validate_with_action_t p1 inv1 disj1 l1 true) (r1: leaf_reader p1) - #inv1' #disj1' #l1' #b (a:t1 -> action inv1' disj1' l1' b bool) + #inv1 #disj1 #l1 #ha1 (v1:validate_with_action_t p1 inv1 disj1 l1 ha1 true) (r1: leaf_reader p1) + #inv1' #disj1' #l1' #b #rt (a:t1 -> action inv1' disj1' l1' b rt bool) #nz2 #wk2 (#k2:parser_kind nz2 wk2) (#t2:t1 -> Type) (#p2:(x:t1 -> parser k2 (t2 x))) - #inv2 #disj2 #l2 #ar2 (v2:(x:t1 -> validate_with_action_t (p2 x) inv2 disj2 l2 ar2)) + #inv2 #disj2 #l2 #ha2 #ar2 (v2:(x:t1 -> validate_with_action_t (p2 x) inv2 disj2 l2 ha2 ar2)) = fun ctxt error_handler_fn input input_length startPosition -> let h0 = HST.get () in LPC.parse_dtuple2_eq' #_ #_ p1 #_ #t2 p2 (I.get_remaining input h0); @@ -628,25 +721,33 @@ let validate_dep_pair_with_action let action_result = a field_value ctxt error_handler_fn input input_length startPosition res in let h3 = HST.get () in modifies_address_liveness_insensitive_unused_in h2 h3; - if not action_result - then LPE.set_validator_error_pos LPE.validator_error_action_failed res //action failed - else - validate_drop (v2 field_value) ctxt error_handler_fn input input_length res + if rt + then ( + validate_drop (v2 field_value) ctxt error_handler_fn input input_length res + ) + else ( + if not action_result + then LPE.set_validator_error_pos LPE.validator_error_action_failed res //action failed + else + validate_drop (v2 field_value) ctxt error_handler_fn input input_length res + ) + end inline_for_extraction noextract let validate_dep_pair_with_refinement' (name1: string) #nz1 (#k1:parser_kind nz1 _) #t1 (#p1:parser k1 t1) - #inv1 #disj1 #l1 (v1:validate_with_action_t p1 inv1 disj1 l1 true) (r1: leaf_reader p1) + #inv1 #disj1 #l1 #ha1 (v1:validate_with_action_t p1 inv1 disj1 l1 ha1 true) (r1: leaf_reader p1) (f: t1 -> bool) #nz2 #wk2 (#k2:parser_kind nz2 wk2) (#t2:refine _ f -> Type) (#p2:(x:refine _ f -> parser k2 (t2 x))) - #inv2 #disj2 #l2 #ar2 (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 ar2)) + #inv2 #disj2 #l2 #ha2 #ar2 (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 ha2 ar2)) : Tot (validate_with_action_t ((p1 `LPC.parse_filter` f) `(parse_dep_pair #nz1)` p2) (conj_inv inv1 inv2) (conj_disjointness disj1 disj2) (l1 `eloc_union` l2) + (ha1||ha2) false) = fun ctxt error_handler_fn input input_length startPosition -> let h0 = HST.get () in @@ -690,13 +791,14 @@ let validate_dep_pair_with_refinement_total_zero_parser' #nz2 #wk2 (#k2:parser_kind nz2 wk2) (#t2:refine _ f -> Type) (#p2:(x:refine _ f -> parser k2 (t2 x))) - #inv2 #disj2 #l2 #ar2 - (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 ar2)) + #inv2 #disj2 #l2 #ha2 #ar2 + ha1 (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 ha2 ar2)) : Pure (validate_with_action_t ((p1 `LPC.parse_filter` f) `(parse_dep_pair #nz1)` p2) (conj_inv inv1 inv2) (conj_disjointness disj1 disj2) (l1 `eloc_union` l2) + (ha1 || ha2) false) (requires ( let open LP in @@ -735,19 +837,19 @@ let validate_dep_pair_with_refinement (p1_is_constant_size_without_actions: bool) (name1: string) #nz1 (#k1:parser_kind nz1 _) #t1 (#p1:parser k1 t1) - #inv1 #disj1 #l1 (v1:validate_with_action_t p1 inv1 disj1 l1 true) (r1: leaf_reader p1) + #inv1 #disj1 #l1 #ha1 (v1:validate_with_action_t p1 inv1 disj1 l1 ha1 true) (r1: leaf_reader p1) (f: t1 -> bool) #nz2 #wk2 (#k2:parser_kind nz2 wk2) (#t2:refine _ f -> Type) (#p2:(x:refine _ f -> parser k2 (t2 x))) - #inv2 #disj2 #l2 #ar2 - (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 ar2)) + #inv2 #disj2 #l2 #ar2 #ha2 + (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 ha2 ar2)) = if p1_is_constant_size_without_actions `LP.bool_and` (k1.LP.parser_kind_high = Some 0) `LP.bool_and` (k1.LP.parser_kind_metadata = Some LP.ParserKindMetadataTotal) then - validate_dep_pair_with_refinement_total_zero_parser' name1 inv1 disj1 l1 r1 f v2 + validate_dep_pair_with_refinement_total_zero_parser' name1 inv1 disj1 l1 r1 f ha1 v2 else validate_dep_pair_with_refinement' name1 v1 r1 f v2 @@ -755,7 +857,7 @@ inline_for_extraction noextract let validate_filter (name: string) #nz (#k:parser_kind nz _) (#t:_) (#p:parser k t) - #inv #disj #l (v:validate_with_action_t p inv disj l true) + #inv #disj #l #ha (v:validate_with_action_t p inv disj l ha true) (r:leaf_reader p) (f:t -> bool) (cr:string) (cf:string) = fun ctxt error_handler_fn input input_length start_position -> [@inline_let] let pos = start_position in @@ -781,10 +883,10 @@ inline_for_extraction noextract let validate_filter_with_action (name: string) #nz (#k:parser_kind nz _) (#t:_) (#p:parser k t) - #inv #disj #l (v:validate_with_action_t p inv disj l true) + #inv #disj #l #ha (v:validate_with_action_t p inv disj l ha true) (r:leaf_reader p) (f:t -> bool) (cr:string) (cf:string) - (#b:bool) #inva #disja (#la:eloc) - (a: t -> action inva disja la b bool) + (#b #rt:bool) #inva #disja (#la:eloc) + (a: t -> action inva disja la b rt bool) = fun ctxt error_handler_fn input input_length start_position -> [@inline_let] let pos0 = start_position in let h = HST.get () in @@ -805,7 +907,10 @@ let validate_filter_with_action if ok then let h15 = HST.get () in let _ = modifies_address_liveness_insensitive_unused_in h h15 in - if a field_value ctxt error_handler_fn input input_length pos0 res + let action_result = a field_value ctxt error_handler_fn input input_length pos0 res in + if rt + then res + else if action_result then res else LPE.set_validator_error_pos LPE.validator_error_action_failed res else LPE.set_validator_error_pos LPE.validator_error_constraint_failed res @@ -815,11 +920,11 @@ inline_for_extraction noextract let validate_with_dep_action (name: string) #nz (#k:parser_kind nz _) (#t:_) (#p:parser k t) - #inv #disj #l - (v:validate_with_action_t p inv disj l true) + #inv #disj #l #ha + (v:validate_with_action_t p inv disj l ha true) (r:leaf_reader p) - (#b:bool) #inva #disja (#la:eloc) - (a: t -> action inva disja la b bool) + (#b #rt:bool) #inva #disja (#la:eloc) + (a: t -> action inva disja la b rt bool) = fun ctxt error_handler_fn input input_length start_position -> [@inline_let] let pos0 = start_position in let h = HST.get () in @@ -833,7 +938,9 @@ let validate_with_dep_action let field_value = r input pos0 in let h15 = HST.get () in let _ = modifies_address_liveness_insensitive_unused_in h h15 in - if a field_value ctxt error_handler_fn input input_length pos0 res + let action_result = a field_value ctxt error_handler_fn input input_length pos0 res in + if rt then res + else if action_result then res else LPE.set_validator_error_pos LPE.validator_error_action_failed res end @@ -841,9 +948,9 @@ let validate_with_dep_action inline_for_extraction noextract let validate_weaken #nz #wk (#k:parser_kind nz wk) #t (#p:parser k t) - #inv #disj #l #ar (v:validate_with_action_t p inv disj l ar) + #inv #disj #l #ha #ar (v:validate_with_action_t p inv disj l ha ar) #nz' #wk' (k':parser_kind nz' wk'{k' `is_weaker_than` k}) -: validate_with_action_t (parse_weaken p k') inv disj l ar +: validate_with_action_t (parse_weaken p k') inv disj l ha ar = fun ctxt error_handler_fn input input_length start_position -> v ctxt error_handler_fn input input_length start_position @@ -853,7 +960,7 @@ let validate_weaken inline_for_extraction noextract let validate_weaken_left #nz #wk (#k:parser_kind nz wk) (#t:_) (#p:parser k t) - #inv #disj #l #ar (v:validate_with_action_t p inv disj l ar) + #inv #disj #l #ar #ha (v:validate_with_action_t p inv disj l ha ar) #nz' #wk' (k':parser_kind nz' wk') = validate_weaken v (glb k' k) @@ -862,7 +969,7 @@ let validate_weaken_left inline_for_extraction noextract let validate_weaken_right #nz #wk (#k:parser_kind nz wk) (#t:_) (#p:parser k t) - #inv #disj #l #ar (v:validate_with_action_t p inv disj l ar) + #inv #disj #l #ar #ha (v:validate_with_action_t p inv disj l ha ar) #nz' #wk' (k':parser_kind nz' wk') = validate_weaken v (glb k k') @@ -892,6 +999,7 @@ let validate_list_inv (ctxt:app_ctxt) (sl: input_buffer_t) (bres: pointer U64.t) + (ha:bool) (h: HS.mem) (stop: bool) : GTot Type0 @@ -900,15 +1008,16 @@ let validate_list_inv let res = Seq.index (as_seq h bres) 0 in inv h0 /\ disj /\ - loc_not_unused_in h0 `loc_includes` app_loc ctxt l /\ - app_loc ctxt l `loc_disjoint` I.footprint sl /\ - app_loc ctxt l `loc_disjoint` loc_buffer bres /\ - address_liveness_insensitive_locs `loc_includes` app_loc ctxt l /\ + loc_not_unused_in h0 `loc_includes` app_loc_fp ctxt true l /\ + app_loc_fp ctxt true l `loc_disjoint` I.footprint sl /\ + app_loc_fp ctxt true l `loc_disjoint` loc_buffer bres /\ + address_liveness_insensitive_locs `loc_includes` app_loc_fp ctxt true l /\ B.loc_buffer bres `B.loc_disjoint` I.footprint sl /\ I.live sl h0 /\ I.live sl h /\ live h0 ctxt /\ live h ctxt /\ + live h (AppCtxt.action_ghost_ptr ctxt) /\ live h1 bres /\ begin let s = I.get_remaining sl h0 in @@ -930,7 +1039,7 @@ let validate_list_inv valid (LPLL.parse_list p) h sl) /\ (stop == true ==> (valid (LPLL.parse_list p) h sl /\ Seq.length (I.get_remaining sl h) == 0)) ) /\ - modifies (app_loc ctxt l `loc_union` loc_buffer bres `loc_union` I.perm_footprint sl) h1 h + modifies (app_loc_fp ctxt ha l `loc_union` loc_buffer bres `loc_union` I.perm_footprint sl) h1 h inline_for_extraction noextract @@ -939,8 +1048,8 @@ let validate_list_body (#k:LP.parser_kind) #t (#p:LP.parser k t) - #inv #disj #l #ar - (v: validate_with_action_t' p inv disj l ar) + #inv #disj #l #ha #ar + (v: validate_with_action_t' p inv disj l ha ar) (g0 g1: Ghost.erased HS.mem) (ctxt:app_ctxt) (error_handler_fn:error_handler) @@ -948,10 +1057,10 @@ let validate_list_body (sl_len: I.tlen sl) (bres: pointer U64.t) : HST.Stack bool - (requires (fun h -> validate_list_inv p inv disj l g0 g1 ctxt sl bres h false)) + (requires (fun h -> validate_list_inv p inv disj l g0 g1 ctxt sl bres ha h false)) (ensures (fun h res h' -> - validate_list_inv p inv disj l g0 g1 ctxt sl bres h false /\ - validate_list_inv p inv disj l g0 g1 ctxt sl bres h' res + validate_list_inv p inv disj l g0 g1 ctxt sl bres ha h false /\ + validate_list_inv p inv disj l g0 g1 ctxt sl bres ha h' res )) = let h = HST.get () in @@ -974,8 +1083,8 @@ let validate_list' (#k:LP.parser_kind) #t (#p:LP.parser k t) - #inv #disj #l #ar - (v: validate_with_action_t' p inv disj l ar) + #inv #disj #l #ha #ar + (v: validate_with_action_t' p inv disj l ha ar) (ctxt: app_ctxt) (error_handler_fn: error_handler) (sl: input_buffer_t) @@ -985,11 +1094,12 @@ let validate_list' (requires (fun h -> inv h /\ disj /\ - loc_not_unused_in h `loc_includes` app_loc ctxt l /\ - app_loc ctxt l `loc_disjoint` I.footprint sl /\ - address_liveness_insensitive_locs `loc_includes` app_loc ctxt l /\ + loc_not_unused_in h `loc_includes` app_loc_fp ctxt true l /\ + app_loc_fp ctxt true l `loc_disjoint` I.footprint sl /\ + address_liveness_insensitive_locs `loc_includes` app_loc_fp ctxt true l /\ B.live h ctxt /\ I.live sl h /\ + B.live h (AppCtxt.action_ghost_ptr ctxt) /\ U64.v pos == Seq.length (I.get_read sl h) )) (ensures (fun h res h' -> @@ -1011,7 +1121,7 @@ let validate_list' else LPE.get_validator_error_kind res == LPE.get_validator_error_kind LPE.validator_error_action_failed end /\ (LPE.is_success res == false ==> U64.v (LPE.get_validator_error_pos res) == Seq.length (I.get_read sl h')) /\ - modifies (app_loc ctxt l `B.loc_union` I.perm_footprint sl) h h' + modifies (app_loc_fp ctxt ha l `B.loc_union` I.perm_footprint sl) h h' )) = let h0 = HST.get () in let g0 = Ghost.hide h0 in @@ -1023,13 +1133,13 @@ let validate_list' let g1 = Ghost.hide h1 in I.live_not_unused_in sl h0; C.Loops.do_while - (validate_list_inv p inv disj l g0 g1 ctxt sl result) + (validate_list_inv p inv disj l g0 g1 ctxt sl result ha) (fun _ -> validate_list_body v g0 g1 ctxt error_handler_fn sl sl_len result); let finalResult = index result 0ul in let h2 = HST.get () in HST.pop_frame (); let h' = HST.get () in - assert (B.modifies (app_loc ctxt l `B.loc_union` I.perm_footprint sl) h0 h'); + assert (B.modifies (app_loc_fp ctxt ha l `B.loc_union` I.perm_footprint sl) h0 h'); LP.parser_kind_prop_equiv LPLL.parse_list_kind (LPLL.parse_list p); finalResult @@ -1039,9 +1149,9 @@ let validate_list (#k:LP.parser_kind) #t (#p:LP.parser k t) - #inv #disj #l #ar - (v: validate_with_action_t' p inv disj l ar) -: validate_with_action_t' (LowParse.Spec.List.parse_list p) inv disj l false + #inv #disj #l #ha #ar + (v: validate_with_action_t' p inv disj l ha ar) +: validate_with_action_t' (LowParse.Spec.List.parse_list p) inv disj l ha false = fun ctxt error_handler_fn input input_length start_position -> validate_list' v ctxt error_handler_fn input input_length start_position @@ -1057,9 +1167,9 @@ let validate_fldata_consumes_all (#k: LP.parser_kind) #t (#p: LP.parser k t) - #inv #disj #l #ar - (v: validate_with_action_t' p inv disj l ar { k.LP.parser_kind_subkind == Some LP.ParserConsumesAll }) -: validate_with_action_t' (LowParse.Spec.FLData.parse_fldata p (U32.v n)) inv disj l false + #inv #disj #l #ha #ar + (v: validate_with_action_t' p inv disj l ha ar { k.LP.parser_kind_subkind == Some LP.ParserConsumesAll }) +: validate_with_action_t' (LowParse.Spec.FLData.parse_fldata p (U32.v n)) inv disj l ha false = fun ctxt error_handler_fn input input_length start_position -> [@inline_let] let pos = start_position in let h = HST.get () in @@ -1094,9 +1204,9 @@ let validate_fldata (#k: LP.parser_kind) #t (#p: LP.parser k t) - #inv #disj #l #ar - (v: validate_with_action_t' p inv disj l ar) -: validate_with_action_t' (LowParse.Spec.FLData.parse_fldata p (U32.v n)) inv disj l false + #inv #disj #l #ha #ar + (v: validate_with_action_t' p inv disj l ha ar) +: validate_with_action_t' (LowParse.Spec.FLData.parse_fldata p (U32.v n)) inv disj l ha false = fun ctxt error_handler_fn input input_length start_position -> [@inline_let] let pos = start_position in let h = HST.get () in @@ -1134,63 +1244,21 @@ noextract inline_for_extraction let validate_nlist (n:U32.t) + (n_is_const:option nat { memoizes_n_as_const n_is_const n}) #wk (#k:parser_kind true wk) #t (#p:parser k t) - #inv #disj #l #ar - (v: validate_with_action_t p inv disj l ar) -: Tot (validate_with_action_t (parse_nlist n p) inv disj l false) -= validate_weaken - #false #WeakKindStrongPrefix #(LowParse.Spec.FLData.parse_fldata_kind (U32.v n) LowParse.Spec.List.parse_list_kind) #(list t) - (validate_fldata_consumes_all n (validate_list v)) - kind_nlist - -inline_for_extraction -noextract -let validate_total_constant_size_no_read' - (#k: LP.parser_kind) - (#t: Type) - (p: LP.parser k t) - (sz: U64.t) - (u: unit { - k.LP.parser_kind_high == Some k.LP.parser_kind_low /\ - k.LP.parser_kind_low == U64.v sz /\ - k.LP.parser_kind_metadata == Some LP.ParserKindMetadataTotal - }) - inv disj l -: validate_with_action_t' p inv disj l true + #inv #disj #l #ha #ar + (v: validate_with_action_t p inv disj l ha ar) +: Tot (validate_with_action_t (parse_nlist n n_is_const p) inv disj l ha false) = fun ctxt error_handler_fn input input_length start_position -> - [@inline_let] let pos = start_position in - let h = HST.get () in - LP.parser_kind_prop_equiv k p; - let hasBytes = I.has input input_length pos sz in - let h2 = HST.get () in - modifies_address_liveness_insensitive_unused_in h h2; - if hasBytes - then pos `U64.add` sz - else LPE.set_validator_error_pos LPE.validator_error_not_enough_data pos - -inline_for_extraction -noextract -let validate_total_constant_size_no_read - #nz #wk - (#k: parser_kind nz wk) - (#t: Type) - (p: parser k t) - (sz: U64.t) - (u: unit { - k.LP.parser_kind_high == Some k.LP.parser_kind_low /\ - k.LP.parser_kind_low == U64.v sz /\ - k.LP.parser_kind_metadata == Some LP.ParserKindMetadataTotal - }) - inv disj l -: Tot (validate_with_action_t p inv disj l true) -= validate_total_constant_size_no_read' p sz u inv disj l + validate_fldata_consumes_all n (validate_list v) ctxt error_handler_fn input input_length start_position inline_for_extraction noextract let validate_nlist_total_constant_size_mod_ok (n:U32.t) + (n_is_const:option nat { memoizes_n_as_const n_is_const n}) #wk (#k:parser_kind true wk) (#t: Type) @@ -1198,7 +1266,7 @@ let validate_nlist_total_constant_size_mod_ok inv disj l - : Pure (validate_with_action_t (parse_nlist n p) inv disj l true) + : Pure (validate_with_action_t (parse_nlist n n_is_const p) inv disj l false true) (requires ( let open LP in k.parser_kind_subkind == Some ParserStrong /\ @@ -1210,22 +1278,23 @@ let validate_nlist_total_constant_size_mod_ok (ensures (fun _ -> True)) = [@inline_let] let _ = - parse_nlist_total_fixed_size_kind_correct n p + parse_nlist_total_fixed_size_kind_correct n n_is_const p in validate_total_constant_size_no_read' - (LP.strengthen (LP.total_constant_size_parser_kind (U32.v n)) (parse_nlist n p)) + (LP.strengthen (LP.total_constant_size_parser_kind (U32.v n)) (parse_nlist n n_is_const p)) (Cast.uint32_to_uint64 n) () inv disj l inline_for_extraction noextract let validate_nlist_constant_size_mod_ko (n:U32.t) + (n_is_const:option nat{ memoizes_n_as_const n_is_const n}) (#wk: _) (#k:parser_kind true wk) #t (p:parser k t) inv disj l - : Pure (validate_with_action_t (parse_nlist n p) inv disj l true) + : Pure (validate_with_action_t (parse_nlist n n_is_const p) inv disj l false true) (requires ( let open LP in k.parser_kind_subkind == Some ParserStrong /\ @@ -1239,12 +1308,12 @@ let validate_nlist_constant_size_mod_ko let h = FStar.HyperStack.ST.get () in [@inline_let] let f () : Lemma - (requires (Some? (LP.parse (parse_nlist n p) (I.get_remaining input h)))) + (requires (Some? (LP.parse (parse_nlist n n_is_const p) (I.get_remaining input h)))) (ensures False) = let sq = I.get_remaining input h in let sq' = Seq.slice sq 0 (U32.v n) in LowParse.Spec.List.list_length_constant_size_parser_correct p sq' ; - let Some (l, _) = LP.parse (parse_nlist n p) sq in + let Some (l, _) = LP.parse (parse_nlist n n_is_const p) sq in assert (U32.v n == FStar.List.Tot.length l `Prims.op_Multiply` k.LP.parser_kind_low) ; FStar.Math.Lemmas.cancel_mul_mod (FStar.List.Tot.length l) k.LP.parser_kind_low ; assert (U32.v n % k.LP.parser_kind_low == 0) @@ -1257,12 +1326,13 @@ let validate_nlist_constant_size_mod_ko inline_for_extraction noextract let validate_nlist_total_constant_size' (n:U32.t) + (n_is_const:option nat { memoizes_n_as_const n_is_const n }) #wk (#k:parser_kind true wk) #t (p:parser k t) inv disj l - : Pure (validate_with_action_t (parse_nlist n p) inv disj l true) + : Pure (validate_with_action_t (parse_nlist n n_is_const p) inv disj l false true) (requires ( let open LP in k.parser_kind_subkind == Some ParserStrong /\ @@ -1273,19 +1343,19 @@ let validate_nlist_total_constant_size' (ensures (fun _ -> True)) = fun ctxt error_handler_fn input start_position -> // n is not an integer constant, so we need to eta-expand and swap fun and if if n `U32.rem` U32.uint_to_t k.LP.parser_kind_low = 0ul - then validate_nlist_total_constant_size_mod_ok n p inv disj l ctxt error_handler_fn input start_position - else validate_nlist_constant_size_mod_ko n p inv disj l ctxt error_handler_fn input start_position + then validate_nlist_total_constant_size_mod_ok n n_is_const p inv disj l ctxt error_handler_fn input start_position + else validate_nlist_constant_size_mod_ko n n_is_const p inv disj l ctxt error_handler_fn input start_position inline_for_extraction noextract let validate_nlist_total_constant_size - (n_is_const: bool) (n:U32.t) + (n_is_const: option nat { memoizes_n_as_const n_is_const n }) #wk (#k:parser_kind true wk) (#t: Type) (p:parser k t) inv disj l -: Pure (validate_with_action_t (parse_nlist n p) inv disj l true) +: Pure (validate_with_action_t (parse_nlist n n_is_const p) inv disj l false true) (requires ( let open LP in k.parser_kind_subkind = Some ParserStrong /\ @@ -1298,41 +1368,47 @@ let validate_nlist_total_constant_size if if k.LP.parser_kind_low = 1 then true - else if n_is_const - then U32.v n % k.LP.parser_kind_low = 0 - else false + else match n_is_const with + | Some n -> n % k.LP.parser_kind_low = 0 + | _ -> false then - validate_nlist_total_constant_size_mod_ok n p inv disj l + validate_nlist_total_constant_size_mod_ok n n_is_const p inv disj l else if - if n_is_const - then U32.v n % k.LP.parser_kind_low <> 0 - else false + match n_is_const with + | Some n -> n % k.LP.parser_kind_low <> 0 + | _ -> false then - validate_nlist_constant_size_mod_ko n p inv disj l + validate_nlist_constant_size_mod_ko n n_is_const p inv disj l else - validate_nlist_total_constant_size' n p inv disj l + validate_nlist_total_constant_size' n n_is_const p inv disj l noextract inline_for_extraction let validate_nlist_constant_size_without_actions - (n_is_const: bool) (n:U32.t) + (n_is_const:option nat { memoizes_n_as_const n_is_const n }) + (payload_is_constant_size: bool) #wk (#k:parser_kind true wk) #t (#p:parser k t) #inv #disj #l #ar - (v: validate_with_action_t p inv disj l ar) -: Tot (validate_with_action_t (parse_nlist n p) inv disj l false) + (v: validate_with_action_t p inv disj l false ar) +: Tot (validate_with_action_t (parse_nlist n n_is_const p) inv disj l false false) = - if - let open LP in - k.parser_kind_subkind = Some ParserStrong && - k.parser_kind_high = Some k.parser_kind_low && - k.parser_kind_metadata = Some ParserKindMetadataTotal && - k.parser_kind_low < 4294967296 - then - validate_drop (validate_nlist_total_constant_size n_is_const n p inv disj l) + if payload_is_constant_size + then ( + if + let open LP in + k.parser_kind_subkind = Some ParserStrong && + k.parser_kind_high = Some k.parser_kind_low && + k.parser_kind_metadata = Some ParserKindMetadataTotal && + k.parser_kind_low < 4294967296 + then + validate_drop (validate_nlist_total_constant_size n n_is_const p inv disj l) + else + validate_nlist n n_is_const v + ) else - validate_nlist n v + validate_nlist n n_is_const v #push-options "--z3rlimit_factor 16 --z3cliopt smt.arith.nl=false" #restart-solver @@ -1340,8 +1416,8 @@ let validate_nlist_constant_size_without_actions noextract inline_for_extraction let validate_t_at_most (n:U32.t) #nz #wk (#k:parser_kind nz wk) (#t:_) (#p:parser k t) - #inv #disj #l #ar (v:validate_with_action_t p inv disj l ar) - : Tot (validate_with_action_t (parse_t_at_most n p) inv disj l false) + #inv #disj #l #ha #ar (v:validate_with_action_t p inv disj l ha ar) + : Tot (validate_with_action_t (parse_t_at_most n p) inv disj l ha false) = fun ctxt error_handler_fn input input_length start_position -> [@inline_let] let pos = start_position in let h = HST.get () in @@ -1380,9 +1456,9 @@ let validate_t_at_most noextract inline_for_extraction let validate_t_exact (n:U32.t) #nz #wk (#k:parser_kind nz wk) (#t:_) (#p:parser k t) - #inv #disj #l #ar - (v:validate_with_action_t p inv disj l ar) -: validate_with_action_t (parse_t_exact n p) inv disj l false + #inv #disj #l #ha #ar + (v:validate_with_action_t p inv disj l ha ar) +: validate_with_action_t (parse_t_exact n p) inv disj l ha false = fun ctxt error_handler_fn input input_length start_position -> [@inline_let] let pos = start_position in let h = HST.get () in @@ -1421,8 +1497,8 @@ inline_for_extraction noextract let validate_with_comment (c:string) #nz #wk (#k:parser_kind nz wk) #t (#p:parser k t) - #inv #disj #l #ar (v:validate_with_action_t p inv disj l ar) -: validate_with_action_t p inv disj l ar + #inv #disj #l #ha #ar (v:validate_with_action_t p inv disj l ha ar) +: validate_with_action_t p inv disj l ha ar = fun ctxt error_handler_fn input input_length start_position -> LowParse.Low.Base.comment c; v ctxt error_handler_fn input input_length start_position @@ -1430,12 +1506,12 @@ let validate_with_comment inline_for_extraction noextract let validate_weaken_inv_loc #nz #wk (#k:parser_kind nz wk) #t (#p:parser k t) - #inv #disj (#l:eloc) #ar + #inv #disj (#l:eloc) #ha #ar (inv':slice_inv{inv' `inv_implies` inv}) (disj':_{ disj' `imp_disjointness` disj}) (l':eloc{l' `eloc_includes` l}) - (v:validate_with_action_t p inv disj l ar) - : Tot (validate_with_action_t p inv' disj' l' ar) + (v:validate_with_action_t p inv disj l ha ar) + : Tot (validate_with_action_t p inv' disj' l' ha ar) = v @@ -1617,6 +1693,7 @@ let validate_list_up_to_inv (sl: input_buffer_t) (h0: HS.mem) (bres: B.pointer U64.t) + (ha:bool) (h: HS.mem) (stop: bool) : GTot Type0 @@ -1626,12 +1703,14 @@ let validate_list_up_to_inv B.live h0 bres /\ I.live sl h0 /\ I.live sl h /\ - B.loc_disjoint (I.footprint sl) (B.loc_buffer bres `B.loc_union` app_loc ctxt loc_none) /\ - B.loc_disjoint (B.loc_buffer bres) (app_loc ctxt loc_none) /\ + B.loc_disjoint (I.footprint sl) (B.loc_buffer bres `B.loc_union` app_loc_fp ctxt true loc_none) /\ + B.loc_disjoint (B.loc_buffer bres) (app_loc_fp ctxt true loc_none) /\ B.live h0 ctxt /\ B.live h ctxt /\ - address_liveness_insensitive_locs `loc_includes` (app_loc ctxt loc_none) /\ - B.modifies (B.loc_buffer bres `B.loc_union` I.perm_footprint sl `B.loc_union` app_loc ctxt loc_none) h0 h /\ + B.live h (AppCtxt.action_ghost_ptr ctxt) /\ + loc_not_unused_in h `loc_includes` app_loc_fp ctxt true loc_none /\ + address_liveness_insensitive_locs `loc_includes` (app_loc_fp ctxt true loc_none) /\ + B.modifies (B.loc_buffer bres `B.loc_union` I.perm_footprint sl `B.loc_union` app_loc_fp ctxt ha loc_none) h0 h /\ begin let s = I.get_remaining sl h0 in let s' = I.get_remaining sl h in @@ -1659,10 +1738,10 @@ let validate_list_up_to_body (# [EverParse3d.Util.solve_from_ctx ()] _extra_t : I.extra_t #input_buffer_t ) (#k: parser_kind true WeakKindStrongPrefix) (#t: eqtype) - (#p: parser k t) + (#p: parser k t) (#ha:bool) (terminator: t) (prf: LUT.consumes_if_not_cond (cond_string_up_to terminator) p) - (v: validator p) + (v: validator_maybe_action p ha) (r: leaf_reader p) (ctxt:app_ctxt) (error_handler_fn:error_handler) @@ -1672,14 +1751,15 @@ let validate_list_up_to_body (bres: B.pointer U64.t) : HST.Stack bool (requires (fun h -> - validate_list_up_to_inv p terminator prf ctxt sl h0 bres h false + validate_list_up_to_inv p terminator prf ctxt sl h0 bres ha h false )) (ensures (fun h stop h' -> - validate_list_up_to_inv p terminator prf ctxt sl h0 bres h false /\ - validate_list_up_to_inv p terminator prf ctxt sl h0 bres h' stop + validate_list_up_to_inv p terminator prf ctxt sl h0 bres ha h false /\ + validate_list_up_to_inv p terminator prf ctxt sl h0 bres ha h' stop )) = let h = HST.get () in + assert ( loc_not_unused_in h `loc_includes` app_loc_fp ctxt true loc_none); LUT.parse_list_up_to_eq (cond_string_up_to terminator) p prf (I.get_remaining sl h); let position = !* bres in let result = v ctxt error_handler_fn sl sl_len position in @@ -1697,14 +1777,14 @@ noextract let validate_list_up_to (#k: parser_kind true WeakKindStrongPrefix) (#t: eqtype) - (#p: parser k t) - (v: validator p) + (#p: parser k t) (#ha:bool) + (v: validator_maybe_action p ha) (r: leaf_reader p) (terminator: t) (prf: LUT.consumes_if_not_cond (cond_string_up_to terminator) p) : validate_with_action_t #true #WeakKindStrongPrefix (LUT.parse_list_up_to (cond_string_up_to terminator) p prf) - true_inv disjointness_trivial eloc_none false + true_inv disjointness_trivial eloc_none ha false = fun ctxt error_handler_fn sl sl_len pos -> let h0 = HST.get () in HST.push_frame (); @@ -1714,7 +1794,7 @@ let validate_list_up_to let h2 = HST.get () in I.live_not_unused_in sl h0; C.Loops.do_while - (validate_list_up_to_inv p terminator prf ctxt sl h2 bres) + (validate_list_up_to_inv p terminator prf ctxt sl h2 bres ha) (fun _ -> validate_list_up_to_body terminator prf v r ctxt error_handler_fn sl sl_len h2 bres) ; let result = B.index bres 0ul in @@ -1725,11 +1805,12 @@ let validate_string (#k: parser_kind true WeakKindStrongPrefix) (#t: eqtype) (#[@@@erasable] p: parser k t) - (v: validator p) + (#ha:_) + (v: validator_maybe_action p ha) (r: leaf_reader p) (terminator: t) = LP.parser_kind_prop_equiv k p; - validate_weaken (validate_list_up_to v r terminator (fun _ _ _ -> ())) _ + validate_list_up_to v r terminator (fun _ _ _ -> ()) let validate_all_bytes = fun _ _ input input_length start_position -> I.empty input input_length start_position @@ -1746,14 +1827,19 @@ let action_return (#a:Type) (x:a) = fun _ _ _ _ _ _ -> x +noextract +inline_for_extraction +let action_return_true + = fun _ _ _ _ _ _ -> true + noextract inline_for_extraction let action_bind (name: string) (#invf:slice_inv) #disjf (#lf:eloc) - #bf (#a:Type) (f: action invf disjf lf bf a) - (#invg:slice_inv) #disjg (#lg:eloc) #bg - (#b:Type) (g: (a -> action invg disjg lg bg b)) + #bf #rtf (#a:Type) (f: action invf disjf lf bf rtf a) + (#invg:slice_inv) #disjg (#lg:eloc) #bg #rtg + (#b:Type) (g: (a -> action invg disjg lg bg rtg b)) = fun ctxt error_handler_fn input input_length pos posf -> let h0 = HST.get () in [@(rename_let ("" ^ name))] @@ -1766,9 +1852,9 @@ noextract inline_for_extraction let action_seq (#invf:slice_inv) #disjf (#lf:eloc) - #bf (#a:Type) (f: action invf disjf lf bf a) - (#invg:slice_inv) #disjg (#lg:eloc) #bg - (#b:Type) (g: action invg disjg lg bg b) + #bf #rtf (#a:Type) (f: action invf disjf lf bf rtf a) + (#invg:slice_inv) #disjg (#lg:eloc) #bg #rtg + (#b:Type) (g: action invg disjg lg bg rtg b) = fun ctxt error_handler_fn input input_length pos posf -> let h0 = HST.get () in let _ = f ctxt error_handler_fn input input_length pos posf in @@ -1781,9 +1867,9 @@ inline_for_extraction let action_ite (#invf:slice_inv) #disjf (#lf:eloc) (guard:bool) - #bf (#a:Type) (then_: squash guard -> action invf disjf lf bf a) - (#invg:slice_inv) #disjg (#lg:eloc) #bg - (else_: squash (not guard) -> action invg disjg lg bg a) + #bf #rtf (#a:Type) (then_: squash guard -> action invf disjf lf bf rtf a) + (#invg:slice_inv) #disjg (#lg:eloc) #bg #rtg + (else_: squash (not guard) -> action invg disjg lg bg rtg a) = fun ctxt error_handler_fn input input_length pos posf -> if guard then then_ () ctxt error_handler_fn input input_length pos posf @@ -1863,8 +1949,8 @@ let probe_then_validate (#inv:slice_inv) (#disj:_) (#l:eloc) - (#allow_reading:bool) - (v:validate_with_action_t p inv disj l allow_reading) + (#ha #allow_reading:bool) + (v:validate_with_action_t p inv disj l ha allow_reading) (src:U64.t) (len:U64.t) (dest:CP.copy_buffer_t) @@ -1873,10 +1959,10 @@ let probe_then_validate CP.properties dest; let h0 = HST.get () in let b = probe src len dest in + let h1 = HST.get () in + modifies_address_liveness_insensitive_unused_in h0 h1; if b then ( - let h1 = HST.get () in - modifies_address_liveness_insensitive_unused_in h0 h1; let result = v ctxt error_handler_fn (CP.stream_of dest) (CP.stream_len dest) 0uL in not (LPE.is_error result) ) diff --git a/src/3d/prelude/EverParse3d.Actions.Base.fsti b/src/3d/prelude/EverParse3d.Actions.Base.fsti index 6aedd39c8..a58543b10 100644 --- a/src/3d/prelude/EverParse3d.Actions.Base.fsti +++ b/src/3d/prelude/EverParse3d.Actions.Base.fsti @@ -141,7 +141,8 @@ val action (disj:disjointness_pre) (modifies_l:eloc) (on_success:bool) - (a:Type) + (always_succeeds:bool) + (a:Type0) : Type0 inline_for_extraction noextract @@ -154,6 +155,7 @@ val validate_with_action_t (liveness_inv:slice_inv) (disj:disjointness_pre) (l:eloc) + (has_action:bool) (allow_reading:bool) : Type0 @@ -167,9 +169,9 @@ val validate_eta (#[@@@erasable] inv:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) - (#allow_reading:bool) - (v: validate_with_action_t p inv disj l allow_reading) -: Tot (validate_with_action_t p inv disj l allow_reading) + (#has_action #allow_reading:bool) + (v: validate_with_action_t p inv disj l has_action allow_reading) +: Tot (validate_with_action_t p inv disj l has_action allow_reading) inline_for_extraction noextract val act_with_comment @@ -177,10 +179,10 @@ val act_with_comment (#[@@@erasable] inv:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) - (#b:_) + (#b #rt:_) (res:Type) - (a: action inv disj l b res) -: Tot (action inv disj l b res) + (a: action inv disj l b rt res) +: Tot (action inv disj l b rt res) inline_for_extraction noextract val leaf_reader @@ -200,9 +202,9 @@ val validate_without_reading (#[@@@erasable] inv:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) - (#allow_reading:bool) - (v: validate_with_action_t p inv disj l allow_reading) -: Tot (validate_with_action_t p inv disj l false) + (#has_action #allow_reading:bool) + (v: validate_with_action_t p inv disj l has_action allow_reading) +: Tot (validate_with_action_t p inv disj l has_action false) inline_for_extraction noextract val validate_with_success_action @@ -215,14 +217,14 @@ val validate_with_success_action (#[@@@erasable] inv1:slice_inv) (#[@@@erasable] disj1:disjointness_pre) (#[@@@erasable] l1:eloc) - (#allow_reading:bool) - (v1:validate_with_action_t p1 inv1 disj1 l1 allow_reading) + (#has_action #allow_reading:bool) + (v1:validate_with_action_t p1 inv1 disj1 l1 has_action allow_reading) (#[@@@erasable] inv2:slice_inv) (#[@@@erasable] disj2:disjointness_pre) (#[@@@erasable] l2:eloc) - (#b:bool) - (a:action inv2 disj2 l2 b bool) - : validate_with_action_t p1 (conj_inv inv1 inv2) (conj_disjointness disj1 disj2) (l1 `eloc_union` l2) false + (#b #rt:bool) + (a:action inv2 disj2 l2 b rt bool) + : validate_with_action_t p1 (conj_inv inv1 inv2) (conj_disjointness disj1 disj2) (l1 `eloc_union` l2) true false inline_for_extraction noextract val validate_with_error_handler @@ -236,13 +238,13 @@ val validate_with_error_handler (#[@@@erasable] inv1:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l1:eloc) - (#ar:_) - (v1:validate_with_action_t p1 inv1 disj l1 ar) - : validate_with_action_t p1 inv1 disj l1 ar + (#has_action #ar:_) + (v1:validate_with_action_t p1 inv1 disj l1 has_action ar) + : validate_with_action_t p1 inv1 disj l1 has_action ar inline_for_extraction noextract val validate_ret - : validate_with_action_t (parse_ret ()) true_inv disjointness_trivial eloc_none true + : validate_with_action_t (parse_ret ()) true_inv disjointness_trivial eloc_none false true inline_for_extraction noextract val validate_pair @@ -251,26 +253,29 @@ val validate_pair (#k1:parser_kind nz1 WeakKindStrongPrefix) (#[@@@erasable] t1:Type) (#[@@@erasable] p1:parser k1 t1) + (k1_const: bool) (#[@@@erasable] inv1:slice_inv) (#[@@@erasable] disj1:disjointness_pre) (#[@@@erasable] l1:eloc) - (#allow_reading1:bool) - (v1:validate_with_action_t p1 inv1 disj1 l1 allow_reading1) + (#has_action1 #allow_reading1:bool) + (v1:validate_with_action_t p1 inv1 disj1 l1 has_action1 allow_reading1) (#nz2:_) (#wk2: _) (#k2:parser_kind nz2 wk2) (#[@@@erasable] t2:Type) (#[@@@erasable] p2:parser k2 t2) + (k2_const: bool) (#[@@@erasable] inv2:slice_inv) (#[@@@erasable] disj2:disjointness_pre) (#[@@@erasable] l2:eloc) - (#allow_reading2:bool) - (v2:validate_with_action_t p2 inv2 disj2 l2 allow_reading2) + (#has_action2 #allow_reading2:bool) + (v2:validate_with_action_t p2 inv2 disj2 l2 has_action2 allow_reading2) : validate_with_action_t (p1 `parse_pair` p2) (conj_inv inv1 inv2) (conj_disjointness disj1 disj2) (l1 `eloc_union` l2) + (has_action1 || has_action2) false inline_for_extraction noextract @@ -283,7 +288,8 @@ val validate_dep_pair (#[@@@erasable] inv1:slice_inv) (#[@@@erasable] disj1:disjointness_pre) (#[@@@erasable] l1:eloc) - (v1:validate_with_action_t p1 inv1 disj1 l1 true) + (#has_action1:_) + (v1:validate_with_action_t p1 inv1 disj1 l1 has_action1 true) (r1: leaf_reader p1) (#nz2:_) (#wk2: _) @@ -293,13 +299,14 @@ val validate_dep_pair (#[@@@erasable] inv2:slice_inv) (#[@@@erasable] disj2:disjointness_pre) (#[@@@erasable] l2:eloc) - (#allow_reading2:bool) - (v2:(x:t1 -> validate_with_action_t (p2 x) inv2 disj2 l2 allow_reading2)) + (#has_action2 #allow_reading2:bool) + (v2:(x:t1 -> validate_with_action_t (p2 x) inv2 disj2 l2 has_action2 allow_reading2)) : validate_with_action_t (p1 `parse_dep_pair` p2) (conj_inv inv1 inv2) (conj_disjointness disj1 disj2) (l1 `eloc_union` l2) + (has_action1 || has_action2) false inline_for_extraction noextract @@ -313,14 +320,15 @@ val validate_dep_pair_with_refinement_and_action (#[@@@erasable] inv1:slice_inv) (#[@@@erasable] disj1:disjointness_pre) (#[@@@erasable] l1:eloc) - (v1:validate_with_action_t p1 inv1 disj1 l1 true) + (#has_action1:bool) + (v1:validate_with_action_t p1 inv1 disj1 l1 has_action1 true) (r1: leaf_reader p1) (f: t1 -> bool) (#[@@@erasable] inv1':slice_inv) (#[@@@erasable] disj1':disjointness_pre) (#[@@@erasable] l1':eloc) - (#b:_) - (a:t1 -> action inv1' disj1' l1' b bool) + (#b #rt:_) + (a:t1 -> action inv1' disj1' l1' b rt bool) (#nz2:_) (#wk2: _) (#k2:parser_kind nz2 wk2) @@ -329,13 +337,14 @@ val validate_dep_pair_with_refinement_and_action (#[@@@erasable] inv2:slice_inv) (#[@@@erasable] disj2:disjointness_pre) (#[@@@erasable] l2:eloc) - (#allow_reading2:bool) - (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 allow_reading2)) + (#has_action2 #allow_reading2:bool) + (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 has_action2 allow_reading2)) : validate_with_action_t ((p1 `parse_filter` f) `parse_dep_pair` p2) (conj_inv inv1 (conj_inv inv1' inv2)) (conj_disjointness disj1 (conj_disjointness disj1' disj2)) (l1 `eloc_union` (l1' `eloc_union` l2)) + true false inline_for_extraction noextract @@ -347,13 +356,14 @@ val validate_dep_pair_with_action (#[@@@erasable] inv1:slice_inv) (#[@@@erasable] disj1:disjointness_pre) (#[@@@erasable] l1:eloc) - (v1:validate_with_action_t p1 inv1 disj1 l1 true) + (#has_action1:_) + (v1:validate_with_action_t p1 inv1 disj1 l1 has_action1 true) (r1: leaf_reader p1) (#[@@@erasable] inv1':slice_inv) (#[@@@erasable] disj1':disjointness_pre) (#[@@@erasable] l1':eloc) - (#b:_) - (a:t1 -> action inv1' disj1' l1' b bool) + (#b #rt:_) + (a:t1 -> action inv1' disj1' l1' b rt bool) (#nz2:_) (#wk2: _) (#k2:parser_kind nz2 wk2) @@ -362,13 +372,14 @@ val validate_dep_pair_with_action (#[@@@erasable] inv2:slice_inv) (#[@@@erasable] disj2:disjointness_pre) (#[@@@erasable] l2:eloc) - (#allow_reading2:_) - (v2:(x:t1 -> validate_with_action_t (p2 x) inv2 disj2 l2 allow_reading2)) + (#has_action2 #allow_reading2:_) + (v2:(x:t1 -> validate_with_action_t (p2 x) inv2 disj2 l2 has_action2 allow_reading2)) : validate_with_action_t (p1 `(parse_dep_pair #nz1)` p2) (conj_inv inv1 (conj_inv inv1' inv2)) (conj_disjointness disj1 (conj_disjointness disj1' disj2)) (l1 `eloc_union` (l1' `eloc_union` l2)) + true false inline_for_extraction noextract @@ -382,7 +393,8 @@ val validate_dep_pair_with_refinement (#[@@@erasable] inv1:slice_inv) (#[@@@erasable] disj1:disjointness_pre) (#[@@@erasable] l1:eloc) - (v1:validate_with_action_t p1 inv1 disj1 l1 true) + (#has_action1:_) + (v1:validate_with_action_t p1 inv1 disj1 l1 has_action1 true) (r1: leaf_reader p1) (f: t1 -> bool) (#nz2:_) @@ -394,12 +406,14 @@ val validate_dep_pair_with_refinement (#[@@@erasable] disj2:disjointness_pre) (#[@@@erasable] l2:eloc) (#allow_reading2:bool) - (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 allow_reading2)) + (#has_action2:_) + (v2:(x:refine _ f -> validate_with_action_t (p2 x) inv2 disj2 l2 has_action2 allow_reading2)) : validate_with_action_t ((p1 `parse_filter` f) `parse_dep_pair` p2) (conj_inv inv1 inv2) (conj_disjointness disj1 disj2) (l1 `eloc_union` l2) + (has_action1 || has_action2) false inline_for_extraction noextract @@ -412,12 +426,13 @@ val validate_filter (#[@@@erasable] inv:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) - (v:validate_with_action_t p inv disj l true) + (#has_action:_) + (v:validate_with_action_t p inv disj l has_action true) (r:leaf_reader p) (f:t -> bool) (cr:string) (cf:string) - : validate_with_action_t (p `parse_filter` f) inv disj l false + : validate_with_action_t (p `parse_filter` f) inv disj l has_action false inline_for_extraction noextract val validate_filter_with_action @@ -429,21 +444,23 @@ val validate_filter_with_action (#[@@@erasable] inv:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) - (v:validate_with_action_t p inv disj l true) + (#has_action:_) + (v:validate_with_action_t p inv disj l has_action true) (r:leaf_reader p) (f:t -> bool) (cr:string) (cf:string) - (#b:bool) + (#b #rt:bool) (#[@@@erasable] inva:slice_inv) (#[@@@erasable] disja:disjointness_pre) (#[@@@erasable] la:eloc) - (a: t -> action inva disja la b bool) + (a: t -> action inva disja la b rt bool) : validate_with_action_t #nz (p `parse_filter` f) (conj_inv inv inva) (conj_disjointness disj disja) (eloc_union l la) + true false inline_for_extraction noextract @@ -456,18 +473,20 @@ val validate_with_dep_action (#[@@@erasable] inv:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) - (v:validate_with_action_t p inv disj l true) + (#has_action:_) + (v:validate_with_action_t p inv disj l has_action true) (r:leaf_reader p) - (#b:bool) + (#b #rt:bool) (#[@@@erasable] inva:slice_inv) (#[@@@erasable] disja:disjointness_pre) (#[@@@erasable] la:eloc) - (a: t -> action inva disja la b bool) + (a: t -> action inva disja la b rt bool) : validate_with_action_t #nz p (conj_inv inv inva) (conj_disjointness disj disja) (eloc_union l la) + true false inline_for_extraction noextract @@ -481,11 +500,12 @@ val validate_weaken_left (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) (#allow_reading:bool) - (v:validate_with_action_t p inv disj l allow_reading) + (#has_action:_) + (v:validate_with_action_t p inv disj l has_action allow_reading) (#nz':_) (#wk': _) (k':parser_kind nz' wk') - : validate_with_action_t (parse_weaken_left p k') inv disj l allow_reading + : validate_with_action_t (parse_weaken_left p k') inv disj l has_action allow_reading inline_for_extraction noextract val validate_weaken_right @@ -498,16 +518,17 @@ val validate_weaken_right (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) (#allow_reading:bool) - (v:validate_with_action_t p inv disj l allow_reading) + (#has_action:_) + (v:validate_with_action_t p inv disj l has_action allow_reading) (#nz':_) (#wk': _) (k':parser_kind nz' wk') - : validate_with_action_t (parse_weaken_right p k') inv disj l allow_reading + : validate_with_action_t (parse_weaken_right p k') inv disj l has_action allow_reading inline_for_extraction noextract val validate_impos (_:unit) - : validate_with_action_t (parse_impos ()) true_inv disjointness_trivial eloc_none true + : validate_with_action_t (parse_impos ()) true_inv disjointness_trivial eloc_none false true noextract inline_for_extraction val validate_ite @@ -520,25 +541,27 @@ val validate_ite (#[@@@erasable] inv1:slice_inv) (#[@@@erasable] disj1:disjointness_pre) (#[@@@erasable] l1:eloc) - (#ar1:_) + (#ha1 #ar1:_) (#[@@@erasable] inv2:slice_inv) (#[@@@erasable] disj2:disjointness_pre) (#[@@@erasable] l2:eloc) - (#ar2:_) + (#ha2 #ar2:_) ([@@@erasable] p1:squash e -> parser k (a())) - (v1:(squash e -> validate_with_action_t (p1()) inv1 disj1 l1 ar1)) + (v1:(squash e -> validate_with_action_t (p1()) inv1 disj1 l1 ha1 ar1)) ([@@@erasable] p2:squash (not e) -> parser k (b())) - (v2:(squash (not e) -> validate_with_action_t (p2()) inv2 disj2 l2 ar2)) + (v2:(squash (not e) -> validate_with_action_t (p2()) inv2 disj2 l2 ha2 ar2)) : validate_with_action_t (parse_ite e p1 p2) (conj_inv inv1 inv2) (conj_disjointness disj1 disj2) (l1 `eloc_union` l2) + (ha1 || ha2) false noextract inline_for_extraction val validate_nlist (n:U32.t) + (n_is_const:option nat { memoizes_n_as_const n_is_const n}) (#wk: _) (#k:parser_kind true wk) (#[@@@erasable] t:Type) @@ -546,14 +569,15 @@ val validate_nlist (#[@@@erasable] inv:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) - (#allow_reading:bool) - (v: validate_with_action_t p inv disj l allow_reading) -: validate_with_action_t (parse_nlist n p) inv disj l false + (#ha #allow_reading:bool) + (v: validate_with_action_t p inv disj l ha allow_reading) +: validate_with_action_t (parse_nlist n n_is_const p) inv disj l ha false noextract inline_for_extraction val validate_nlist_constant_size_without_actions - (n_is_const: bool) (n:U32.t) + (n_is_const: option nat { memoizes_n_as_const n_is_const n }) + (payload_is_constant_size:bool) (#wk: _) (#k:parser_kind true wk) (#[@@@erasable] t:Type) @@ -562,8 +586,8 @@ val validate_nlist_constant_size_without_actions (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) (#allow_reading:bool) - (v: validate_with_action_t p inv disj l allow_reading) -: Tot (validate_with_action_t (parse_nlist n p) inv disj l false) + (v: validate_with_action_t p inv disj l false allow_reading) +: Tot (validate_with_action_t (parse_nlist n n_is_const p) inv disj l false false) noextract inline_for_extraction val validate_t_at_most @@ -576,9 +600,9 @@ val validate_t_at_most (#[@@@erasable] inv:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) - (#ar:_) - (v:validate_with_action_t p inv disj l ar) - : Tot (validate_with_action_t (parse_t_at_most n p) inv disj l false) + (#ha #ar:_) + (v:validate_with_action_t p inv disj l ha ar) + : Tot (validate_with_action_t (parse_t_at_most n p) inv disj l ha false) noextract inline_for_extraction val validate_t_exact @@ -591,9 +615,9 @@ val validate_t_exact (#[@@@erasable] inv:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) - (#ar:_) - (v:validate_with_action_t p inv disj l ar) - : Tot (validate_with_action_t (parse_t_exact n p) inv disj l false) + (#ha #ar:_) + (v:validate_with_action_t p inv disj l ha ar) + : Tot (validate_with_action_t (parse_t_exact n p) inv disj l ha false) inline_for_extraction noextract val validate_with_comment @@ -606,9 +630,9 @@ val validate_with_comment (#[@@@erasable] inv:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) - (#allow_reading:bool) - (v:validate_with_action_t p inv disj l allow_reading) - : validate_with_action_t p inv disj l allow_reading + (#ha #allow_reading:bool) + (v:validate_with_action_t p inv disj l ha allow_reading) + : validate_with_action_t p inv disj l ha allow_reading inline_for_extraction noextract val validate_weaken_inv_loc @@ -620,12 +644,12 @@ val validate_weaken_inv_loc (#[@@@erasable] inv:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) - (#allow_reading:bool) + (#ha #allow_reading:bool) ([@@@erasable] inv':slice_inv{inv' `inv_implies` inv}) ([@@@erasable] disj':disjointness_pre { disj' `imp_disjointness` disj }) ([@@@erasable] l':eloc{l' `eloc_includes` l}) - (v:validate_with_action_t p inv disj l allow_reading) - : Tot (validate_with_action_t p inv' disj' l' allow_reading) + (v:validate_with_action_t p inv disj l ha allow_reading) + : Tot (validate_with_action_t p inv' disj' l' ha allow_reading) inline_for_extraction noextract val read_filter @@ -643,7 +667,11 @@ val read_impos inline_for_extraction let validator #nz #wk (#k:parser_kind nz wk) (#t:Type) (p:parser k t) - = validate_with_action_t p true_inv disjointness_trivial eloc_none true + = validate_with_action_t p true_inv disjointness_trivial eloc_none false true + +inline_for_extraction +let validator_maybe_action #nz #wk (#k:parser_kind nz wk) (#t:Type) (p:parser k t) (has_action:bool) + = validate_with_action_t p true_inv disjointness_trivial eloc_none has_action true inline_for_extraction noextract val validate____UINT8 @@ -726,18 +754,19 @@ val validate_string (#k: parser_kind true WeakKindStrongPrefix) (#t: eqtype) (#[@@@erasable] p: parser k t) - (v: validator p) + (#ha:_) + (v: validator_maybe_action p ha) (r: leaf_reader p) (terminator: t) - : Tot (validate_with_action_t (parse_string p terminator) true_inv disjointness_trivial eloc_none false) + : Tot (validate_with_action_t (parse_string p terminator) true_inv disjointness_trivial eloc_none ha false) inline_for_extraction noextract val validate_all_bytes - : validate_with_action_t parse_all_bytes true_inv disjointness_trivial eloc_none false // could be true + : validate_with_action_t parse_all_bytes true_inv disjointness_trivial eloc_none false false // could be true inline_for_extraction noextract val validate_all_zeros - : validate_with_action_t parse_all_zeros true_inv disjointness_trivial eloc_none false + : validate_with_action_t parse_all_zeros true_inv disjointness_trivial eloc_none false false //////////////////////////////////////////////////////////////////////////////// @@ -746,7 +775,12 @@ inline_for_extraction val action_return (#a:Type) (x:a) - : action true_inv disjointness_trivial eloc_none false a + : action true_inv disjointness_trivial eloc_none false false a + +noextract +inline_for_extraction +val action_return_true + : action true_inv disjointness_trivial eloc_none false true bool noextract inline_for_extraction @@ -755,20 +789,21 @@ val action_bind (#[@@@erasable] invf:slice_inv) (#[@@@erasable] disjf:disjointness_pre) (#[@@@erasable] lf:eloc) - (#bf:_) + (#bf #rtf:_) (#a:Type) - (f: action invf disjf lf bf a) + (f: action invf disjf lf bf rtf a) (#[@@@erasable] invg:slice_inv) (#[@@@erasable] disjg:disjointness_pre) (#[@@@erasable] lg:eloc) - (#bg:_) + (#bg #rt:_) (#b:Type) - (g: (a -> action invg disjg lg bg b)) + (g: (a -> action invg disjg lg bg rt b)) : action (conj_inv invf invg) (conj_disjointness disjf disjg) (eloc_union lf lg) (bf || bg) + rt b noextract @@ -777,20 +812,21 @@ val action_seq (#[@@@erasable] invf:slice_inv) (#[@@@erasable] disjf:disjointness_pre) (#[@@@erasable] lf:eloc) - (#bf:_) + (#bf #rtf:_) (#a:Type) - (f: action invf disjf lf bf a) + (f: action invf disjf lf bf rtf a) (#[@@@erasable] invg:slice_inv) (#[@@@erasable] disjg:disjointness_pre) (#[@@@erasable] lg:eloc) - (#bg:_) + (#bg #rtg:_) (#b:Type) - (g: action invg disjg lg bg b) + (g: action invg disjg lg bg rtg b) : action (conj_inv invf invg) (conj_disjointness disjf disjg) (eloc_union lf lg) (bf || bg) + rtg b noextract @@ -800,37 +836,38 @@ val action_ite (#[@@@erasable] disjf:disjointness_pre) (#[@@@erasable] lf:eloc) (guard:bool) - (#bf:_) + (#bf #rtf:_) (#a:Type) - (then_: squash guard -> action invf disjf lf bf a) + (then_: squash guard -> action invf disjf lf bf rtf a) (#[@@@erasable] invg:slice_inv) (#[@@@erasable] disjg:disjointness_pre) (#[@@@erasable] lg:eloc) - (#bg:_) - (else_: squash (not guard) -> action invg disjg lg bg a) + (#bg #rtg:_) + (else_: squash (not guard) -> action invg disjg lg bg rtg a) : action (conj_inv invf invg) (conj_disjointness disjf disjg) (eloc_union lf lg) (bf || bg) + (rtf && rtg) a noextract inline_for_extraction val action_abort - : action true_inv disjointness_trivial eloc_none false bool + : action true_inv disjointness_trivial eloc_none false false bool noextract inline_for_extraction val action_field_pos_64 - : action true_inv disjointness_trivial eloc_none false U64.t + : action true_inv disjointness_trivial eloc_none false false U64.t noextract inline_for_extraction val action_deref (#a:_) (x:bpointer a) - : action (ptr_inv x) disjointness_trivial eloc_none false a + : action (ptr_inv x) disjointness_trivial eloc_none false false a noextract inline_for_extraction @@ -838,7 +875,7 @@ val action_assignment (#a:_) (x:bpointer a) (v:a) - : action (ptr_inv x) disjointness_trivial (ptr_loc x) false unit + : action (ptr_inv x) disjointness_trivial (ptr_loc x) false false unit noextract inline_for_extraction @@ -846,13 +883,13 @@ val action_weaken (#[@@@erasable] inv:slice_inv) (#[@@@erasable] disj:disjointness_pre) (#[@@@erasable] l:eloc) - (#b:_) + (#b #rt:_) (#a:_) - (act:action inv disj l b a) + (act:action inv disj l b rt a) (#[@@@erasable] inv':slice_inv{inv' `inv_implies` inv}) (#[@@@erasable] disj':disjointness_pre { disj' `imp_disjointness` disj }) (#l':eloc{l' `eloc_includes` l}) - : action inv' disj' l' b a + : action inv' disj' l' b rt a inline_for_extraction noextract @@ -863,7 +900,7 @@ inline_for_extraction val mk_external_action (#t: Type) (#l:eloc) ($f: external_action t l) - : action true_inv disjointness_trivial l false t + : action true_inv disjointness_trivial l false false t val copy_buffer_inv (x:CP.copy_buffer_t) : slice_inv val copy_buffer_loc (x:CP.copy_buffer_t) : eloc @@ -879,8 +916,8 @@ val probe_then_validate (#inv:slice_inv) (#disj:disjointness_pre) (#l:eloc) - (#allow_reading:bool) - (v:validate_with_action_t p inv disj l allow_reading) + (#ha #allow_reading:bool) + (v:validate_with_action_t p inv disj l ha allow_reading) (src:U64.t) (len:U64.t) (dest:CP.copy_buffer_t) @@ -889,6 +926,7 @@ val probe_then_validate (conj_disjointness disj (disjoint (copy_buffer_loc dest) l)) (eloc_union l (copy_buffer_loc dest)) true + false bool // Some actions are valid only for specific backends (buffer, extern, etc.) diff --git a/src/3d/prelude/EverParse3d.AppCtxt.fsti b/src/3d/prelude/EverParse3d.AppCtxt.fsti index 7f8220233..b7bb64e5f 100644 --- a/src/3d/prelude/EverParse3d.AppCtxt.fsti +++ b/src/3d/prelude/EverParse3d.AppCtxt.fsti @@ -7,10 +7,18 @@ open LowStar.Buffer open FStar.HyperStack.ST val region : HS.rid let app_ctxt = x:B.pointer U8.t { B.frameOf x == region } +val action_ghost_ptr (x:app_ctxt) : GTot (y:B.pointer U8.t { + loc_disjoint (B.loc_buffer x) (B.loc_buffer y) /\ + B.frameOf y == region +}) let loc_of (x:app_ctxt) : GTot B.loc = B.loc_buffer x +let ghost_loc_of (x:app_ctxt) : GTot B.loc = B.loc_buffer (action_ghost_ptr x) let properties (x:app_ctxt) : Lemma ( B.loc_region_only true region `loc_includes` loc_of x /\ - B.address_liveness_insensitive_locs `B.loc_includes` loc_of x + B.loc_region_only true region `loc_includes` ghost_loc_of x /\ + B.address_liveness_insensitive_locs `B.loc_includes` loc_of x /\ + B.address_liveness_insensitive_locs `B.loc_includes` ghost_loc_of x /\ + loc_disjoint (loc_of x) (ghost_loc_of x) ) = () diff --git a/src/3d/prelude/EverParse3d.Interpreter.fst b/src/3d/prelude/EverParse3d.Interpreter.fst index a7f7e6595..47211b536 100644 --- a/src/3d/prelude/EverParse3d.Interpreter.fst +++ b/src/3d/prelude/EverParse3d.Interpreter.fst @@ -176,6 +176,7 @@ let itype_as_validator (i:itype) A.true_inv A.disjointness_trivial A.eloc_none + false (allow_reader_of_itype i) = match i with | UInt8 -> A.validate____UINT8 @@ -265,7 +266,6 @@ let disjoint (e1 e2:loc_index) | _, Trivial -> disj_none | NonTrivial e1, NonTrivial e2 -> NonTrivial (A.disjoint e1 e2) - (* A context is a list of bindings, where each binding is a pair of a name and a denotation of the name. *) (* global_binding: @@ -279,6 +279,7 @@ type global_binding = { parser_kind_nz:bool; // Does it consume non-zero bytes? parser_weak_kind: P.weak_kind; parser_kind: P.parser_kind parser_kind_nz parser_weak_kind; + parser_has_action: bool; //Memory invariant of any actions it contains inv:inv_index; //Disjointness precondition @@ -297,6 +298,7 @@ type global_binding = { (interp_inv inv) (interp_disj disj) (interp_loc loc) + parser_has_action (Some? p_reader); } @@ -304,6 +306,7 @@ let projector_names : list string = [ `%Mkglobal_binding?.parser_kind_nz; `%Mkglobal_binding?.parser_weak_kind; `%Mkglobal_binding?.parser_kind; + `%Mkglobal_binding?.parser_has_action; `%Mkglobal_binding?.inv; `%Mkglobal_binding?.disj; `%Mkglobal_binding?.loc; @@ -316,6 +319,7 @@ let projector_names : list string = [ let nz_of_binding = Mkglobal_binding?.parser_kind_nz let wk_of_binding = Mkglobal_binding?.parser_weak_kind let pk_of_binding = Mkglobal_binding?.parser_kind +let has_action_of_binding = Mkglobal_binding?.parser_has_action let inv_of_binding = Mkglobal_binding?.inv let disj_of_bindng = Mkglobal_binding?.disj let loc_of_binding = Mkglobal_binding?.loc @@ -363,6 +367,7 @@ noeq type dtyp : #nz:bool -> #wk:P.weak_kind -> P.parser_kind nz wk -> + has_action:bool -> has_reader:bool -> inv_index -> disj_index -> @@ -371,6 +376,7 @@ type dtyp | DT_IType: i:itype -> dtyp (parser_kind_of_itype i) + false (allow_reader_of_itype i) inv_none disj_none loc_none @@ -381,6 +387,7 @@ type dtyp #nz:bool -> #wk:P.weak_kind -> pk:P.parser_kind nz wk -> + ha:bool -> hr:bool -> inv:inv_index -> disj:disj_index -> @@ -389,25 +396,26 @@ type dtyp _:squash (nz == nz_of_binding x /\ wk == wk_of_binding x /\ pk == pk_of_binding x /\ + ha == has_action_of_binding x /\ hr == has_reader x /\ inv == inv_of_binding x /\ disj == disj_of_bindng x /\ loc == loc_of_binding x) -> - dtyp #nz #wk pk hr inv disj loc + dtyp #nz #wk pk ha hr inv disj loc [@@specialize] -let dtyp_as_type #nz #wk (#pk:P.parser_kind nz wk) #hr #i #disj #l - (d:dtyp pk hr i disj l) +let dtyp_as_type #nz #wk (#pk:P.parser_kind nz wk) #ha #hr #i #disj #l + (d:dtyp pk ha hr i disj l) : Type = match d with | DT_IType i -> itype_as_type i - | DT_App _ _ _ _ _ b _ -> + | DT_App _ _ _ _ _ _ b _ -> type_of_binding b -let dtyp_as_eqtype_lemma #nz #wk (#pk:P.parser_kind nz wk) #i #disj #l - (d:dtyp pk true i disj l) +let dtyp_as_eqtype_lemma #nz #wk (#pk:P.parser_kind nz wk) #ha #i #disj #l + (d:dtyp pk ha true i disj l) : Lemma (ensures hasEq (dtyp_as_type d)) [SMTPat (hasEq (dtyp_as_type d))] @@ -415,33 +423,33 @@ let dtyp_as_eqtype_lemma #nz #wk (#pk:P.parser_kind nz wk) #i #disj #l | DT_IType i -> () - | DT_App _ _ _ _ _ b _ -> + | DT_App _ _ _ _ _ _ b _ -> let (| _, _ |) = get_leaf_reader b in () -let dtyp_as_parser #nz #wk (#pk:P.parser_kind nz wk) #hr #i #disj #l - (d:dtyp pk hr i disj l) +let dtyp_as_parser #nz #wk (#pk:P.parser_kind nz wk) #ha #hr #i #disj #l + (d:dtyp pk ha hr i disj l) : P.parser pk (dtyp_as_type d) = match d returns Tot (P.parser pk (dtyp_as_type d)) with | DT_IType i -> itype_as_parser i - | DT_App _ _ _ _ _ b _ -> + | DT_App _ _ _ _ _ _ b _ -> parser_of_binding b [@@specialize] let dtyp_as_validator #nz #wk (#pk:P.parser_kind nz wk) - (#hr:_) + (#ha #hr:_) (#[@@@erasable] i:inv_index) (#[@@@erasable] disj:disj_index) (#[@@@erasable] l:loc_index) - (d:dtyp pk hr i disj l) + (d:dtyp pk ha hr i disj l) : A.validate_with_action_t #nz #wk #pk #(dtyp_as_type d) (dtyp_as_parser d) (interp_inv i) (interp_disj disj) (interp_loc l) - hr + ha hr = match d returns A.validate_with_action_t #nz #wk #pk #(dtyp_as_type d) @@ -449,29 +457,29 @@ let dtyp_as_validator #nz #wk (#pk:P.parser_kind nz wk) (interp_inv i) (interp_disj disj) (interp_loc l) - hr + ha hr with | DT_IType i -> itype_as_validator i - | DT_App _ _ _ _ _ b _ -> + | DT_App _ _ _ _ _ _ b _ -> // assert_norm (dtyp_as_type (DT_App_Alt ps b args) == (type_of_binding_alt (apply_arrow b args))); // assert_norm (dtyp_as_parser (DT_App_Alt ps b args) == parser_of_binding_alt (apply_arrow b args)); validator_of_binding b [@@specialize] -let dtyp_as_leaf_reader #nz (#pk:P.parser_kind nz P.WeakKindStrongPrefix) +let dtyp_as_leaf_reader #nz (#pk:P.parser_kind nz P.WeakKindStrongPrefix) #ha (#[@@@erasable] i:inv_index) (#[@@@erasable] disj:disj_index) (#[@@@erasable] l:loc_index) - (d:dtyp pk true i disj l) + (d:dtyp pk ha true i disj l) : A.leaf_reader (dtyp_as_parser d) = match d with | DT_IType i -> itype_as_leaf_reader i - | DT_App _ _ _ _ _ b _ -> + | DT_App _ _ _ _ _ _ b _ -> let (| _, lr |) = get_leaf_reader b in lr @@ -483,7 +491,7 @@ let action_binding (on_success:bool) (a:Type) : Type u#0 - = A.action (interp_inv inv) A.disjointness_trivial (interp_loc l) on_success a + = A.action (interp_inv inv) A.disjointness_trivial (interp_loc l) on_success false a inline_for_extraction let extern_action (t: Type) (l:loc_index) = A.external_action t (interp_loc l) @@ -523,49 +531,52 @@ let mk_action_binding *) noeq type atomic_action - : inv_index -> disj_index -> loc_index -> bool -> Type0 -> Type u#1 = + : inv_index -> disj_index -> loc_index -> bool -> bool -> Type0 -> Type u#1 = | Action_return: #a:Type0 -> x:a -> - atomic_action inv_none disj_none loc_none false a + atomic_action inv_none disj_none loc_none false false a + + | Action_return_true: + atomic_action inv_none disj_none loc_none false true bool | Action_abort: - atomic_action inv_none disj_none loc_none false bool + atomic_action inv_none disj_none loc_none false false bool | Action_field_pos_64: - atomic_action inv_none disj_none loc_none false U64.t + atomic_action inv_none disj_none loc_none false false U64.t | Action_field_pos_32: squash (EverParse3d.Actions.BackendFlag.backend_flag == A.BackendFlagBuffer) -> - atomic_action inv_none disj_none loc_none false U32.t + atomic_action inv_none disj_none loc_none false false U32.t | Action_field_ptr: squash (EverParse3d.Actions.BackendFlag.backend_flag == A.BackendFlagBuffer) -> - atomic_action inv_none disj_none loc_none true A.___PUINT8 + atomic_action inv_none disj_none loc_none true false A.___PUINT8 | Action_field_ptr_after: squash (EverParse3d.Actions.BackendFlag.backend_flag == A.BackendFlagExtern) -> (sz: FStar.UInt64.t) -> write_to: A.bpointer A.___PUINT8 -> - atomic_action (NonTrivial (A.ptr_inv write_to)) disj_none (NonTrivial (A.ptr_loc write_to)) false bool + atomic_action (NonTrivial (A.ptr_inv write_to)) disj_none (NonTrivial (A.ptr_loc write_to)) false false bool | Action_field_ptr_after_with_setter: squash (EverParse3d.Actions.BackendFlag.backend_flag == A.BackendFlagExtern) -> sz: FStar.UInt64.t -> #out_loc:loc_index -> write_to: (A.___PUINT8 -> Tot (extern_action unit out_loc)) -> - atomic_action inv_none disj_none out_loc false bool + atomic_action inv_none disj_none out_loc false false bool | Action_deref: #a:Type0 -> x:A.bpointer a -> - atomic_action (NonTrivial (A.ptr_inv x)) disj_none loc_none false a + atomic_action (NonTrivial (A.ptr_inv x)) disj_none loc_none false false a | Action_assignment: #a:Type0 -> x:A.bpointer a -> rhs:a -> - atomic_action (NonTrivial (A.ptr_inv x)) disj_none (NonTrivial (A.ptr_loc x)) false unit + atomic_action (NonTrivial (A.ptr_inv x)) disj_none (NonTrivial (A.ptr_loc x)) false false unit | Action_call: #inv:inv_index -> @@ -573,17 +584,18 @@ type atomic_action #b:bool -> #t:Type0 -> action_binding inv loc b t -> - atomic_action inv disj_none loc b t + atomic_action inv disj_none loc b false t | Action_probe_then_validate: #nz:bool -> #wk:_ -> #k:P.parser_kind nz wk -> + #ha:bool -> #has_reader:bool -> #inv:inv_index -> #disj:disj_index -> #l:loc_index -> - dt:dtyp k has_reader inv disj l -> + dt:dtyp k ha has_reader inv disj l -> src:U64.t -> len:U64.t -> dest:CP.copy_buffer_t -> @@ -591,18 +603,20 @@ type atomic_action atomic_action (join_inv inv (NonTrivial (A.copy_buffer_inv dest))) (join_disj disj (disjoint (NonTrivial (A.copy_buffer_loc dest)) l)) (join_loc l (NonTrivial (A.copy_buffer_loc dest))) - true bool + true false bool (* Denotation of atomic_actions as A.action *) [@@specialize] let atomic_action_as_action - (#i #d #l #b #t:_) - (a:atomic_action i d l b t) - : Tot (A.action (interp_inv i) (interp_disj d) (interp_loc l) b t) + (#i #d #l #b #rt #t:_) + (a:atomic_action i d l b rt t) + : Tot (A.action (interp_inv i) (interp_disj d) (interp_loc l) b rt t) = match a with | Action_return x -> A.action_return x + | Action_return_true -> + A.action_return_true | Action_abort -> A.action_abort | Action_field_pos_64 -> @@ -633,39 +647,39 @@ let atomic_action_as_action *) noeq type action - : inv_index -> disj_index -> loc_index -> bool -> Type0 -> Type u#1 = + : inv_index -> disj_index -> loc_index -> bool -> bool -> Type0 -> Type u#1 = | Atomic_action: - #i:_ -> #d:_ -> #l:_ -> #b:_ -> #t:_ -> - atomic_action i d l b t -> - action i d l b t + #i:_ -> #d:_ -> #l:_ -> #b:_ -> #rt:_ -> #t:_ -> + atomic_action i d l b rt t -> + action i d l b rt t | Action_seq: - #i0:_ -> #l0:_ -> #b0:_ -> hd:atomic_action i0 disj_none l0 b0 unit -> - #i1:_ -> #l1:_ -> #b1:_ -> #t:_ -> tl:action i1 disj_none l1 b1 t -> - action (join_inv i0 i1) disj_none (join_loc l0 l1) (b0 || b1) t + #i0:_ -> #l0:_ -> #b0:_ -> #rt1:_ -> hd:atomic_action i0 disj_none l0 b0 rt1 unit -> + #i1:_ -> #l1:_ -> #b1:_ -> #rt2:_ -> #t:_ -> tl:action i1 disj_none l1 b1 rt2 t -> + action (join_inv i0 i1) disj_none (join_loc l0 l1) (b0 || b1) rt2 t | Action_ite : hd:bool -> - #i0:_ -> #l0:_ -> #b0:_ -> #t:_ -> then_:(_:squash hd -> action i0 disj_none l0 b0 t) -> - #i1:_ -> #l1:_ -> #b1:_ -> else_:(_:squash (not hd) -> action i1 disj_none l1 b1 t) -> - action (join_inv i0 i1) disj_none (join_loc l0 l1) (b0 || b1) t + #i0:_ -> #l0:_ -> #b0:_ -> #rt0:_ -> #t:_ -> then_:(_:squash hd -> action i0 disj_none l0 b0 rt0 t) -> + #i1:_ -> #l1:_ -> #b1:_ -> #rt1:_ -> else_:(_:squash (not hd) -> action i1 disj_none l1 b1 rt1 t) -> + action (join_inv i0 i1) disj_none (join_loc l0 l1) (b0 || b1) (rt0 && rt1) t | Action_let: - #i0:_ -> #l0:_ -> #b0:_ -> #t0:_ -> head:atomic_action i0 disj_none l0 b0 t0 -> - #i1:_ -> #l1:_ -> #b1:_ -> #t1:_ -> k:(t0 -> action i1 disj_none l1 b1 t1) -> - action (join_inv i0 i1) disj_none (join_loc l0 l1) (b0 || b1) t1 + #i0:_ -> #l0:_ -> #b0:_ -> #rt1:_ -> #t0:_ -> head:atomic_action i0 disj_none l0 b0 rt1 t0 -> + #i1:_ -> #l1:_ -> #b1:_ -> #rt2:_ -> #t1:_ -> k:(t0 -> action i1 disj_none l1 b1 rt2 t1) -> + action (join_inv i0 i1) disj_none (join_loc l0 l1) (b0 || b1) rt2 t1 | Action_act: - #i0:_ -> #l0:_ -> #b0:_ -> act:action i0 disj_none l0 b0 unit -> - action i0 disj_none l0 b0 bool + #i0:_ -> #l0:_ -> #b0:_ -> #rt0:_ -> act:action i0 disj_none l0 b0 rt0 unit -> + action i0 disj_none l0 b0 true bool (* Denotation of action as A.action *) [@@specialize] let rec action_as_action - (#i #d #l #b #t:_) - (a:action i d l b t) - : Tot (A.action (interp_inv i) (interp_disj d) (interp_loc l) b t) + (#i #d #l #b #rt #t:_) + (a:action i d l b rt t) + : Tot (A.action (interp_inv i) (interp_disj d) (interp_loc l) b rt t) (decreases a) = A.index_equations(); match a with @@ -688,7 +702,7 @@ let rec action_as_action A.action_bind "hd" head k | Action_act #i0 #l0 #b0 a -> - A.action_weaken (A.action_seq (action_as_action a) (A.action_return true)) + A.action_weaken (A.action_seq (action_as_action a) A.action_return_true) #(interp_inv i0) #_ #(interp_loc l0) @@ -696,6 +710,7 @@ let rec action_as_action (* Some AST nodes contain source comments that we propagate to the output *) let comments = string + [@@ no_auto_projectors] noeq type typ @@ -705,76 +720,82 @@ type typ disj_index -> loc_index -> bool -> + bool -> Type = | T_false: fieldname:string -> - typ P.impos_kind inv_none disj_none loc_none true + typ P.impos_kind inv_none disj_none loc_none false true | T_denoted : fieldname:string -> #nz:_ -> #wk:_ -> #pk:P.parser_kind nz wk -> - #has_reader:_ -> #i:_ -> #disj:_ -> #l:_ -> - td:dtyp pk has_reader i disj l -> - typ pk i disj l has_reader + #ha:_ -> #has_reader:_ -> #i:_ -> #disj:_ -> #l:_ -> + td:dtyp pk ha has_reader i disj l -> + typ pk i disj l ha has_reader | T_pair: first_fieldname:string -> #nz1:_ -> #pk1:P.parser_kind nz1 P.WeakKindStrongPrefix -> - #i1:_ -> #d1:_ -> #l1:_ -> #b1:_ -> + #i1:_ -> #d1:_ -> #l1:_ -> #ha1:_ -> #b1:_ -> #nz2:_ -> #wk2:_ -> #pk2:P.parser_kind nz2 wk2 -> - #i2:_ -> #d2:_ -> #l2:_ -> #b2:_ -> - t1:typ pk1 i1 d1 l1 b1 -> - t2:typ pk2 i2 d2 l2 b2 -> + #i2:_ -> #d2:_ -> #l2:_ -> #ha2:_ -> #b2:_ -> + k1_const: bool -> + t1:typ pk1 i1 d1 l1 ha1 b1 -> + k2_const: bool -> + t2:typ pk2 i2 d2 l2 ha2 b2 -> typ (P.and_then_kind pk1 pk2) (join_inv i1 i2) (join_disj d1 d2) (join_loc l1 l2) - false + (ha1 || ha2) + false | T_dep_pair: first_fieldname:string -> #nz1:_ -> #pk1:P.parser_kind nz1 P.WeakKindStrongPrefix -> - #i1:_ -> #d1:_ -> #l1:_ -> + #i1:_ -> #d1:_ -> #l1:_ -> #ha1:_ -> #nz2:_ -> #wk2:_ -> #pk2:P.parser_kind nz2 wk2 -> - #i2:_ -> #d2:_ -> #l2:_ -> #b2:bool -> + #i2:_ -> #d2:_ -> #l2:_ -> #ha2:_ -> #b2:bool -> //the first component is a pre-denoted type with a reader - t1:dtyp pk1 true i1 d1 l1 -> + t1:dtyp pk1 ha1 true i1 d1 l1 -> //the second component is a function from denotations of t1 //that's why it's a small type, so that we can speak about its //denotation here - t2:(dtyp_as_type t1 -> typ pk2 i2 d2 l2 b2) -> + t2:(dtyp_as_type t1 -> typ pk2 i2 d2 l2 ha2 b2) -> typ (P.and_then_kind pk1 pk2) (join_inv i1 i2) (join_disj d1 d2) (join_loc l1 l2) + (ha1 || ha2) false | T_refine: fieldname:string -> #nz1:_ -> #pk1:P.parser_kind nz1 P.WeakKindStrongPrefix -> - #i1:_ -> #d1:_ -> #l1:_ -> + #i1:_ -> #d1:_ -> #l1:_ -> #ha1:_ -> //the first component is a pre-denoted type with a reader - base:dtyp pk1 true i1 d1 l1 -> + base:dtyp pk1 ha1 true i1 d1 l1 -> //the second component is a function from denotations of base //but notice that its codomain is bool, rather than expr //That's to ensure that the refinement is already well-typed refinement:(dtyp_as_type base -> bool) -> - typ (P.filter_kind pk1) i1 d1 l1 false + typ (P.filter_kind pk1) i1 d1 l1 ha1 false | T_refine_with_action: fieldname:string -> #nz1:_ -> #pk1:P.parser_kind nz1 P.WeakKindStrongPrefix -> - #i1:_ -> #d1:_ -> #l1:_ -> - #i2:_ -> #d2:_ -> #l2:_ -> #b2:_ -> - base:dtyp pk1 true i1 d1 l1 -> + #i1:_ -> #d1:_ -> #l1:_ -> #ha1:_ -> + #i2:_ -> #d2:_ -> #l2:_ -> #b2:_ -> #rt2:_ -> + base:dtyp pk1 ha1 true i1 d1 l1 -> refinement:(dtyp_as_type base -> bool) -> - act:(dtyp_as_type base -> action i2 d2 l2 b2 bool) -> + act:(dtyp_as_type base -> action i2 d2 l2 b2 rt2 bool) -> typ (P.filter_kind pk1) (join_inv i1 i2) (join_disj d1 d2) (join_loc l1 l2) + true false - + | T_dep_pair_with_refinement: //This construct serves two purposes // 1. To avoid double fetches, we fold the refinement @@ -783,34 +804,36 @@ type typ // to depend on the refinement of the first field first_fieldname:string -> #nz1:_ -> #pk1:P.parser_kind nz1 P.WeakKindStrongPrefix -> - #i1:_ -> #d1:_ -> #l1:_ -> + #i1:_ -> #d1:_ -> #l1:_ -> #ha1:_ -> #nz2:_ -> #wk2:_ -> #pk2:P.parser_kind nz2 wk2 -> - #i2:_ -> #d2:_ -> #l2:_ -> #b2:_ -> + #i2:_ -> #d2:_ -> #l2:_ -> #b2:_ -> #ha2:_ -> //the first component is a pre-denoted type with a reader - base:dtyp pk1 true i1 d1 l1 -> + base:dtyp pk1 ha1 true i1 d1 l1 -> //the second component is a function from denotations of base refinement:(dtyp_as_type base -> bool) -> - k:(x:dtyp_as_type base { refinement x } -> typ pk2 i2 d2 l2 b2) -> + k:(x:dtyp_as_type base { refinement x } -> typ pk2 i2 d2 l2 ha2 b2) -> typ (P.and_then_kind (P.filter_kind pk1) pk2) (join_inv i1 i2) (join_disj d1 d2) (join_loc l1 l2) + (ha1 || ha2) false | T_dep_pair_with_action: fieldname:string -> #nz1:_ -> #pk1:P.parser_kind nz1 P.WeakKindStrongPrefix -> - #i1:_ -> #d1:_ -> #l1:_ -> + #i1:_ -> #d1:_ -> #l1:_ -> #ha1:_ -> #nz2:_ -> #wk2:_ -> #pk2:P.parser_kind nz2 wk2 -> - #i2:_ -> #d2:_ -> #l2:_ -> #b2:_ -> - #i3:_ -> #d3:_ -> #l3:_ -> #b3:_ -> - base:dtyp pk1 true i1 d1 l1 -> - k:(x:dtyp_as_type base -> typ pk2 i2 d2 l2 b2) -> - act:(dtyp_as_type base -> action i3 d3 l3 b3 bool) -> + #i2:_ -> #d2:_ -> #l2:_ -> #b2:_ -> #ha2:_ -> + #i3:_ -> #d3:_ -> #l3:_ -> #b3:_ -> #rt3:_ -> + base:dtyp pk1 ha1 true i1 d1 l1 -> + k:(x:dtyp_as_type base -> typ pk2 i2 d2 l2 ha2 b2) -> + act:(dtyp_as_type base -> action i3 d3 l3 b3 rt3 bool) -> typ (P.and_then_kind pk1 pk2) (join_inv i1 (join_inv i3 i2)) (join_disj d1 (join_disj d3 d2)) (join_loc l1 (join_loc l3 l2)) + true false | T_dep_pair_with_refinement_and_action: @@ -821,111 +844,118 @@ type typ // to depend on the refinement of the first field first_fieldname:string -> #nz1:_ -> #pk1:P.parser_kind nz1 P.WeakKindStrongPrefix -> - #i1:_ -> #d1:_ -> #l1:_ -> + #i1:_ -> #d1:_ -> #l1:_ -> #ha1:_ -> #nz2:_ -> #wk2:_ -> #pk2:P.parser_kind nz2 wk2 -> - #i2:_ -> #d2:_ -> #l2:_ -> #b2:_ -> - #i3:_ -> #d3:_ -> #l3:_ -> #b3:_ -> + #i2:_ -> #d2:_ -> #l2:_ -> #b2:_ -> #ha2:_ -> + #i3:_ -> #d3:_ -> #l3:_ -> #b3:_ -> #rt3:_ -> //the first component is a pre-denoted type with a reader - base:dtyp pk1 true i1 d1 l1 -> + base:dtyp pk1 ha1 true i1 d1 l1 -> //the second component is a function from denotations of base refinement:(dtyp_as_type base -> bool) -> - k:(x:dtyp_as_type base { refinement x } -> typ pk2 i2 d2 l2 b2) -> - act:(dtyp_as_type base -> action i3 d3 l3 b3 bool) -> + k:(x:dtyp_as_type base { refinement x } -> typ pk2 i2 d2 l2 ha2 b2) -> + act:(dtyp_as_type base -> action i3 d3 l3 b3 rt3 bool) -> typ (P.and_then_kind (P.filter_kind pk1) pk2) (join_inv i1 (join_inv i3 i2)) (join_disj d1 (join_disj d3 d2)) (join_loc l1 (join_loc l3 l2)) + true false | T_if_else: #nz1:_ -> #wk1:_ -> #pk1:P.parser_kind nz1 wk1 -> - #l1:_ -> #i1:_ -> #d1:_ -> #b1:_ -> + #l1:_ -> #i1:_ -> #d1:_ -> #b1:_ -> #ha1:_ -> #nz2:_ -> #wk2:_ -> #pk2:P.parser_kind nz2 wk2 -> - #l2:_ -> #i2:_ -> #d2:_ -> #b2:_ -> + #l2:_ -> #i2:_ -> #d2:_ -> #b2:_ -> #ha2:_ -> b:bool -> //A bool, rather than an expression - t1:(squash b -> typ pk1 i1 d1 l1 b1) -> - t2:(squash (not b) -> typ pk2 i2 d2 l2 b2) -> + t1:(squash b -> typ pk1 i1 d1 l1 ha1 b1) -> + t2:(squash (not b) -> typ pk2 i2 d2 l2 ha2 b2) -> typ (P.glb pk1 pk2) (join_inv i1 i2) (join_disj d1 d2) - (join_loc l1 l2) false + (join_loc l1 l2) + (ha1 || ha2) + false | T_cases: #nz1:_ -> #wk1:_ -> #pk1:P.parser_kind nz1 wk1 -> - #l1:_ -> #i1:_ -> #d1:_ -> #b1:_ -> + #l1:_ -> #i1:_ -> #d1:_ -> #b1:_ -> #ha1:_ -> #nz2:_ -> #wk2:_ -> #pk2:P.parser_kind nz2 wk2 -> - #l2:_ -> #i2:_ -> #d2:_ -> #b2:_ -> + #l2:_ -> #i2:_ -> #d2:_ -> #b2:_ -> #ha2:_ -> b:bool -> //A bool, rather than an expression - t1:typ pk1 i1 d1 l1 b1 -> - t2:typ pk2 i2 d2 l2 b2 -> + t1:typ pk1 i1 d1 l1 ha1 b1 -> + t2:typ pk2 i2 d2 l2 ha2 b2 -> typ (P.glb pk1 pk2) (join_inv i1 i2) (join_disj d1 d2) (join_loc l1 l2) + (ha1 || ha2) false | T_with_action: fieldname:string -> #nz:_ -> #wk:_ -> #pk:P.parser_kind nz wk -> - #l1:_ -> #i1:_ -> #d1:_ -> #b1:_ -> - #l2:_ -> #i2:_ -> #d2:_ -> #b2:_ -> - base:typ pk i1 d1 l1 b1 -> - act:action i2 d2 l2 b2 bool -> - typ pk (join_inv i1 i2) (join_disj d1 d2) (join_loc l1 l2) false + #l1:_ -> #i1:_ -> #d1:_ -> #b1:_ -> #ha1:_ -> + #l2:_ -> #i2:_ -> #d2:_ -> #b2:_ -> #rt2:_ -> + base:typ pk i1 d1 l1 ha1 b1 -> + act:action i2 d2 l2 b2 rt2 bool -> + typ pk (join_inv i1 i2) (join_disj d1 d2) (join_loc l1 l2) true false | T_with_dep_action: fieldname:string -> #nz1:_ -> #pk1:P.parser_kind nz1 P.WeakKindStrongPrefix -> - #i1:_ -> #d1: _ -> #l1:_ -> - #i2:_ -> #d2:_ -> #l2:_ -> #b2:_ -> - head:dtyp pk1 true i1 d1 l1 -> - act:(dtyp_as_type head -> action i2 d2 l2 b2 bool) -> - typ pk1 (join_inv i1 i2) (join_disj d1 d2) (join_loc l1 l2) false + #i1:_ -> #d1: _ -> #l1:_ -> #ha1:_ -> + #i2:_ -> #d2:_ -> #l2:_ -> #b2:_ -> #rt2:_ -> + head:dtyp pk1 ha1 true i1 d1 l1 -> + act:(dtyp_as_type head -> action i2 d2 l2 b2 rt2 bool) -> + typ pk1 (join_inv i1 i2) (join_disj d1 d2) (join_loc l1 l2) true false | T_drop: #nz:_ -> #wk:_ -> #pk:P.parser_kind nz wk -> - #l:_ -> #i:_ -> #d:_ -> #b:_ -> - t:typ pk i d l b -> - typ pk i d l false + #l:_ -> #i:_ -> #d:_ -> #b:_ -> #ha:_ -> + t:typ pk i d l ha b -> + typ pk i d l ha false | T_with_comment: fieldname:string -> #nz:_ -> #wk:_ -> #pk:P.parser_kind nz wk -> - #l:_ -> #i:_ -> #d:_ -> #b:_ -> - t:typ pk i d l b -> + #l:_ -> #i:_ -> #d:_ -> #b:_ -> #ha:_ -> + t:typ pk i d l ha b -> c:comments -> - typ pk i d l b + typ pk i d l ha b | T_nlist: fieldname:string -> #wk:_ -> #pk:P.parser_kind true wk -> - #i:_ -> #l:_ -> #d:_ -> #b:_ -> + #i:_ -> #l:_ -> #d:_ -> #b:_ -> #ha:_ -> n:U32.t -> - t:typ pk i d l b -> - typ P.kind_nlist i d l false + n_is_constant:option nat { P.memoizes_n_as_const n_is_constant n } -> + payload_is_constant_size:bool -> + t:typ pk i d l ha b -> + typ (P.kind_nlist pk n_is_constant) i d l ha false | T_at_most: fieldname:string -> #nz:_ -> #wk:_ -> #pk:P.parser_kind nz wk -> - #i:_ -> #d:_ -> #l:_ -> #b:_ -> + #i:_ -> #d:_ -> #l:_ -> #b:_ -> #ha:_ -> n:U32.t -> - t:typ pk i d l b -> - typ P.kind_t_at_most i d l false + t:typ pk i d l ha b -> + typ P.kind_t_at_most i d l ha false | T_exact: fieldname:string -> #nz:_ -> #wk:_ -> #pk:P.parser_kind nz wk -> - #i:_ -> #d:_ -> #l:_ -> #b:_ -> + #i:_ -> #d:_ -> #l:_ -> #b:_ -> #ha:_ -> n:U32.t -> - t:typ pk i d l b -> - typ P.kind_t_exact i d l false + t:typ pk i d l ha b -> + typ P.kind_t_exact i d l ha false | T_string: fieldname:string -> #pk1:P.parser_kind true P.WeakKindStrongPrefix -> - element_type:dtyp pk1 true inv_none disj_none loc_none -> + #ha:_ -> + element_type:dtyp pk1 ha true inv_none disj_none loc_none -> terminator:dtyp_as_type element_type -> - typ P.parse_string_kind inv_none disj_none loc_none false + typ P.parse_string_kind inv_none disj_none loc_none ha false [@@specialize] @@ -944,13 +974,14 @@ let t_probe_then_validate (len:U64.t) (dest:CP.copy_buffer_t) (#nz #wk:_) (#pk:P.parser_kind nz wk) - (#has_reader #i #disj:_) + (#ha #has_reader #i #disj:_) (#l:_) - (td:dtyp pk has_reader i disj l) + (td:dtyp pk ha has_reader i disj l) : typ (parser_kind_of_itype UInt64) (join_inv i (NonTrivial (A.copy_buffer_inv dest))) (join_disj disj (disjoint (NonTrivial (A.copy_buffer_loc dest)) l)) (join_loc l (NonTrivial (A.copy_buffer_loc dest))) + true false = T_with_dep_action fieldname (DT_IType UInt64) @@ -961,8 +992,8 @@ let t_probe_then_validate (* Type denotation of `typ` *) let rec as_type #nz #wk (#pk:P.parser_kind nz wk) - #l #i #d #b - (t:typ pk l i d b) + #l #i #d #ha #b + (t:typ pk l i d ha b) : Tot Type0 (decreases t) = match t with @@ -971,7 +1002,7 @@ let rec as_type | T_denoted _ td -> dtyp_as_type td - | T_pair _ t1 t2 -> + | T_pair _ _ t1 _ t2 -> as_type t1 & as_type t2 | T_dep_pair _ i t @@ -1005,7 +1036,7 @@ let rec as_type | T_with_dep_action _ i _ -> dtyp_as_type i - | T_nlist _ n t -> + | T_nlist _fn n _n_is_const _plconst t -> P.nlist n (as_type t) | T_at_most _ n t -> @@ -1021,8 +1052,8 @@ let rec as_type (* Parser denotation of `typ` *) let rec as_parser #nz #wk (#pk:P.parser_kind nz wk) - #l #i #d #b - (t:typ pk l i d b) + #l #i #d #ha #b + (t:typ pk l i d ha b) : Tot (P.parser pk (as_type t)) (decreases t) = match t returns Tot (P.parser pk (as_type t)) with @@ -1033,7 +1064,7 @@ let rec as_parser | T_denoted _ d -> dtyp_as_parser d - | T_pair _ t1 t2 -> + | T_pair _ _ t1 _ t2 -> //assert_norm (as_type g (T_pair t1 t2) == as_type g t1 * as_type g t2); let p1 = as_parser t1 in let p2 = as_parser t2 in @@ -1094,8 +1125,8 @@ let rec as_parser //assert_norm (as_type g (T_with_comment t c) == as_type g t); as_parser t - | T_nlist _ n t -> - P.parse_nlist n (as_parser t) + | T_nlist _fn n n_is_const _plconst t -> + P.parse_nlist n n_is_const (as_parser t) | T_at_most _ n t -> P.parse_t_at_most n (as_parser t) @@ -1107,11 +1138,11 @@ let rec as_parser P.parse_string (dtyp_as_parser elt_t) terminator [@@specialize] -let rec as_reader #nz (#pk:P.parser_kind nz P.WeakKindStrongPrefix) +let rec as_reader #nz (#pk:P.parser_kind nz P.WeakKindStrongPrefix) #ha (#[@@@erasable] inv:inv_index) (#[@@@erasable] d:disj_index) (#[@@@erasable] loc:loc_index) - (t:typ pk inv d loc true) + (t:typ pk inv d loc ha true) : leaf_reader (as_parser t) = match t with | T_denoted _n dt -> @@ -1140,14 +1171,14 @@ let rec as_validator (#[@@@erasable] inv:inv_index) (#[@@@erasable] disj:disj_index) (#[@@@erasable] loc:loc_index) - #b - (t:typ pk inv disj loc b) + #ha #b + (t:typ pk inv disj loc ha b) : Tot (A.validate_with_action_t #nz #wk #pk #(as_type t) (as_parser t) (interp_inv inv) (interp_disj disj) (interp_loc loc) - b) + ha b) (decreases t) = A.index_equations(); match t @@ -1157,7 +1188,7 @@ let rec as_validator (interp_inv inv) (interp_disj disj) (interp_loc loc) - b + ha b ) with | T_false fn -> @@ -1168,11 +1199,13 @@ let rec as_validator assert_norm (as_parser (T_denoted fn td) == dtyp_as_parser td); A.validate_with_error_handler typename fn (A.validate_eta (dtyp_as_validator td)) - | T_pair fn t1 t2 -> - assert_norm (as_type (T_pair fn t1 t2) == as_type t1 * as_type t2); - assert_norm (as_parser (T_pair fn t1 t2) == P.parse_pair (as_parser t1) (as_parser t2)); + | T_pair fn k1_const t1 k2_const t2 -> + assert_norm (as_type (T_pair fn k1_const t1 k2_const t2) == as_type t1 * as_type t2); + assert_norm (as_parser (T_pair fn k1_const t1 k2_const t2) == P.parse_pair (as_parser t1) (as_parser t2)); A.validate_pair fn + k1_const (as_validator typename t1) + k2_const (as_validator typename t2) | T_dep_pair fn i t -> @@ -1300,11 +1333,18 @@ let rec as_validator assert_norm (as_parser (T_with_comment fn t c) == as_parser t); A.validate_with_comment c (as_validator typename t) - | T_nlist fn n t -> - assert_norm (as_type (T_nlist fn n t) == P.nlist n (as_type t)); - assert_norm (as_parser (T_nlist fn n t) == P.parse_nlist n (as_parser t)); - A.validate_with_error_handler typename fn - (A.validate_nlist n (as_validator typename t)) + | T_nlist fn n n_is_const payload_is_constant_size t -> + assert_norm (as_type (T_nlist fn n n_is_const payload_is_constant_size t) == P.nlist n (as_type t)); + assert_norm (as_parser (T_nlist fn n n_is_const payload_is_constant_size t) == P.parse_nlist n n_is_const (as_parser t)); + if ha + then ( + A.validate_with_error_handler typename fn + (A.validate_nlist n n_is_const (as_validator typename t)) + ) + else ( + A.validate_with_error_handler typename fn + (A.validate_nlist_constant_size_without_actions n n_is_const payload_is_constant_size (as_validator typename t)) + ) | T_at_most fn n t -> assert_norm (as_type (T_at_most fn n t) == P.t_at_most n (as_type t)); @@ -1328,16 +1368,17 @@ let rec as_validator #pop-options [@@noextract_to "krml"; specialize] inline_for_extraction noextract -let validator_of #allow_reading #nz #wk (#k:P.parser_kind nz wk) +let validator_of #ha #allow_reading #nz #wk (#k:P.parser_kind nz wk) (#[@@@erasable] i:inv_index) (#[@@@erasable] d:disj_index) (#[@@@erasable] l:loc_index) - (t:typ k i d l allow_reading) = + (t:typ k i d l ha allow_reading) = A.validate_with_action_t (as_parser t) (interp_inv i) (interp_disj d) (interp_loc l) + ha allow_reading [@@noextract_to "krml"; specialize] @@ -1346,8 +1387,8 @@ let dtyp_of #nz #wk (#k:P.parser_kind nz wk) (#[@@@erasable] i:inv_index) (#[@@@erasable] d:disj_index) (#[@@@erasable] l:loc_index) - #b (t:typ k i d l b) = - dtyp k b i d l + #ha #b (t:typ k i d l ha b) = + dtyp k ha b i d l let specialization_steps = [nbe; @@ -1361,6 +1402,7 @@ let specialization_steps = `%nz_of_binding; `%wk_of_binding; `%pk_of_binding; + `%has_action_of_binding; `%inv_of_binding; `%loc_of_binding; `%type_of_binding; @@ -1387,17 +1429,18 @@ let mk_global_binding #nz #wk ([@@@erasable] p_t : Type0) ([@@@erasable] p_p : P.parser pk p_t) (p_reader: option (leaf_reader p_p)) - (b:bool) + (#ha b:bool) (p_v : A.validate_with_action_t p_p (interp_inv inv) (interp_disj disj) - (interp_loc loc) b) + (interp_loc loc) ha b) ([@@@erasable] pf:squash (b == Some? p_reader)) : global_binding = { parser_kind_nz = nz; parser_weak_kind = wk; parser_kind = pk; + parser_has_action = ha; inv = inv; disj; loc = loc; @@ -1408,7 +1451,7 @@ let mk_global_binding #nz #wk } [@@specialize] -let mk_dt_app #nz #wk (pk:P.parser_kind nz wk) (b:bool) +let mk_dt_app #nz #wk (pk:P.parser_kind nz wk) (ha b:bool) ([@@@erasable] inv:inv_index) ([@@@erasable] disj:disj_index) ([@@@erasable] loc:loc_index) @@ -1416,12 +1459,13 @@ let mk_dt_app #nz #wk (pk:P.parser_kind nz wk) (b:bool) ([@@@erasable] pf:squash (nz == nz_of_binding x /\ wk == wk_of_binding x /\ pk == pk_of_binding x /\ + ha == has_action_of_binding x /\ b == has_reader x /\ inv == inv_of_binding x /\ disj == disj_of_bindng x /\ loc == loc_of_binding x)) - : dtyp #nz #wk pk b inv disj loc - = DT_App pk b inv disj loc x pf + : dtyp #nz #wk pk ha b inv disj loc + = DT_App pk ha b inv disj loc x pf [@@specialize] @@ -1433,18 +1477,20 @@ let mk_dtyp_app #nz #wk ([@@@erasable] p_t : Type0) ([@@@erasable] p_p : P.parser pk p_t) (p_reader: option (leaf_reader p_p)) - (b:bool) + (ha b:bool) (p_v : A.validate_with_action_t p_p (interp_inv inv) (interp_disj disj) (interp_loc loc) + ha b) ([@@@erasable] pf:squash (b == Some? p_reader)) - : dtyp #nz #wk pk b inv disj loc + : dtyp #nz #wk pk ha b inv disj loc = let gb = { parser_kind_nz = nz; parser_weak_kind = wk; parser_kind = pk; + parser_has_action = ha; inv = inv; disj; loc = loc; @@ -1453,7 +1499,7 @@ let mk_dtyp_app #nz #wk p_reader = p_reader; p_v = p_v } in - DT_App pk b inv disj loc gb () + DT_App pk ha b inv disj loc gb () //attribute to tag disjointness indexes of type definitions let specialize_disjointness = () diff --git a/src/3d/prelude/EverParse3d.Kinds.fst b/src/3d/prelude/EverParse3d.Kinds.fst index a310b3794..a5568b79f 100755 --- a/src/3d/prelude/EverParse3d.Kinds.fst +++ b/src/3d/prelude/EverParse3d.Kinds.fst @@ -75,15 +75,40 @@ let impos_kind /// Lists/arrays inline_for_extraction noextract -let kind_nlist +let kind_nlist_default +: parser_kind false WeakKindStrongPrefix += let open LP in + let open FStar.Mul in + { + parser_kind_low = 0; + parser_kind_high = None; + parser_kind_subkind = Some ParserStrong; + parser_kind_metadata = None + } + +inline_for_extraction +noextract +let kind_nlist #b #w kelt nopt : parser_kind false WeakKindStrongPrefix = let open LP in - { - parser_kind_low = 0; - parser_kind_high = None; - parser_kind_subkind = Some ParserStrong; - parser_kind_metadata = None - } + let open FStar.Mul in + match nopt with + | None -> kind_nlist_default + | Some byte_size -> + if Some kelt.parser_kind_low = kelt.parser_kind_high + && kelt.parser_kind_low <> 0 + && byte_size % kelt.parser_kind_low = 0 + && kelt.parser_kind_subkind = Some ParserStrong + && kelt.parser_kind_metadata = Some ParserKindMetadataTotal + then ( + { + parser_kind_low = byte_size; + parser_kind_high = Some byte_size; + parser_kind_subkind = Some ParserStrong; + parser_kind_metadata = Some ParserKindMetadataTotal; + } + ) + else kind_nlist_default let kind_all_bytes : parser_kind false WeakKindConsumesAll @@ -91,11 +116,11 @@ let kind_all_bytes let kind_t_at_most : parser_kind false WeakKindStrongPrefix - = kind_nlist + = kind_nlist_default let kind_t_exact : parser_kind false WeakKindStrongPrefix - = kind_nlist + = kind_nlist_default let parse_string_kind : parser_kind true WeakKindStrongPrefix diff --git a/src/3d/prelude/EverParse3d.Kinds.fsti b/src/3d/prelude/EverParse3d.Kinds.fsti index 3fab82f01..9521e3efe 100755 --- a/src/3d/prelude/EverParse3d.Kinds.fsti +++ b/src/3d/prelude/EverParse3d.Kinds.fsti @@ -68,7 +68,7 @@ val impos_kind /// Lists/arrays inline_for_extraction noextract -val kind_nlist +val kind_nlist #b #w (k:parser_kind b w) (n:option nat) : parser_kind false WeakKindStrongPrefix val kind_all_bytes diff --git a/src/3d/prelude/EverParse3d.Prelude.fst b/src/3d/prelude/EverParse3d.Prelude.fst index f272142f5..88f00ac2a 100755 --- a/src/3d/prelude/EverParse3d.Prelude.fst +++ b/src/3d/prelude/EverParse3d.Prelude.fst @@ -101,13 +101,67 @@ let parse_ite e p1 p2 let nlist (n:U32.t) (t:Type) = list t inline_for_extraction noextract -let parse_nlist n #wk #k #t p +let parse_nlist' + (n:U32.t) + (n_const:option nat { Some? n_const ==> Some?.v n_const == U32.v n }) + (#wk: _) + (#k:parser_kind true wk) + (#t:_) + (p:parser k t) + : Tot (LP.bare_parser (nlist n t)) + = LowParse.Spec.FLData.parse_fldata (LowParse.Spec.List.parse_list p) (U32.v n) + +let parse_nlist_total_fixed_size_aux + (n:U32.t) (n_is_const:option nat { memoizes_n_as_const n_is_const n }) + (#wk: _) (#k:parser_kind true wk) #t (p:parser k t) + (x: LP.bytes) +: Lemma + (requires ( + let open LP in + k.parser_kind_subkind == Some ParserStrong /\ + k.parser_kind_high == Some k.parser_kind_low /\ + U32.v n % k.parser_kind_low == 0 /\ + k.parser_kind_metadata == Some ParserKindMetadataTotal /\ + Seq.length x >= U32.v n + )) + (ensures ( + Some? (LP.parse (parse_nlist' n n_is_const p) x) + )) += let x' = Seq.slice x 0 (U32.v n) in + let cnt = (U32.v n / k.LP.parser_kind_low) in + FStar.Math.Lemmas.lemma_div_exact (U32.v n) k.LP.parser_kind_low; + FStar.Math.Lemmas.nat_over_pos_is_nat (U32.v n) k.LP.parser_kind_low; + LowParse.Spec.List.parse_list_total_constant_size p cnt x'; + LP.parser_kind_prop_equiv LowParse.Spec.List.parse_list_kind (LowParse.Spec.List.parse_list p) + +let parse_nlist_total_fixed_size_kind_correct + (n:U32.t) (n_is_const:option nat { memoizes_n_as_const n_is_const n }) + (#wk: _) (#k:parser_kind true wk) #t (p:parser k t) +: Lemma + (requires ( + let open LP in + k.parser_kind_subkind == Some ParserStrong /\ + k.parser_kind_high == Some k.parser_kind_low /\ + U32.v n % k.parser_kind_low == 0 /\ + k.parser_kind_metadata == Some ParserKindMetadataTotal + )) + (ensures ( + LP.parser_kind_prop (LP.total_constant_size_parser_kind (U32.v n)) (parse_nlist' n n_is_const p) /\ + (Some? n_is_const ==> kind_nlist k n_is_const == LP.total_constant_size_parser_kind (U32.v n)) + )) += LP.parser_kind_prop_equiv (LowParse.Spec.FLData.parse_fldata_kind (U32.v n) LowParse.Spec.List.parse_list_kind) (parse_nlist' n n_is_const p); + LP.parser_kind_prop_equiv (LP.total_constant_size_parser_kind (U32.v n)) (parse_nlist' n n_is_const p); + Classical.forall_intro (Classical.move_requires (parse_nlist_total_fixed_size_aux n n_is_const p)) + +inline_for_extraction noextract +let parse_nlist n n_is_const #wk #k #t p = let open LowParse.Spec.FLData in let open LowParse.Spec.List in - parse_weaken - #false #WeakKindStrongPrefix #(parse_fldata_kind (U32.v n) parse_list_kind) #(list t) - (LowParse.Spec.FLData.parse_fldata (LowParse.Spec.List.parse_list p) (U32.v n)) - #false kind_nlist + let p' = parse_nlist' n n_is_const p in + LP.parser_kind_prop_equiv (parse_fldata_kind (U32.v n) (parse_list_kind)) p'; + LP.parser_kind_prop_equiv (kind_nlist k n_is_const) p'; + Classical.move_requires (parse_nlist_total_fixed_size_kind_correct n n_is_const #wk #k #t) p; + p' let all_bytes = Seq.seq LP.byte let parse_all_bytes' @@ -179,48 +233,12 @@ let validator_no_read #nz #wk (#k:parser_kind nz wk) (#t:Type) (p:parser k t) : Type = LPL.validator_no_read #k #t p -let parse_nlist_total_fixed_size_aux - (n:U32.t) (#wk: _) (#k:parser_kind true wk) #t (p:parser k t) - (x: LP.bytes) -: Lemma - (requires ( - let open LP in - k.parser_kind_subkind == Some ParserStrong /\ - k.parser_kind_high == Some k.parser_kind_low /\ - U32.v n % k.parser_kind_low == 0 /\ - k.parser_kind_metadata == Some ParserKindMetadataTotal /\ - Seq.length x >= U32.v n - )) - (ensures ( - Some? (LP.parse (parse_nlist n p) x) - )) -= let x' = Seq.slice x 0 (U32.v n) in - let cnt = (U32.v n / k.LP.parser_kind_low) in - FStar.Math.Lemmas.lemma_div_exact (U32.v n) k.LP.parser_kind_low; - FStar.Math.Lemmas.nat_over_pos_is_nat (U32.v n) k.LP.parser_kind_low; - LowParse.Spec.List.parse_list_total_constant_size p cnt x'; - LP.parser_kind_prop_equiv LowParse.Spec.List.parse_list_kind (LowParse.Spec.List.parse_list p) - -let parse_nlist_total_fixed_size_kind_correct - (n:U32.t) (#wk: _) (#k:parser_kind true wk) #t (p:parser k t) -: Lemma - (requires ( - let open LP in - k.parser_kind_subkind == Some ParserStrong /\ - k.parser_kind_high == Some k.parser_kind_low /\ - U32.v n % k.parser_kind_low == 0 /\ - k.parser_kind_metadata == Some ParserKindMetadataTotal - )) - (ensures ( - LP.parser_kind_prop (LP.total_constant_size_parser_kind (U32.v n)) (parse_nlist n p) - )) -= LP.parser_kind_prop_equiv (LowParse.Spec.FLData.parse_fldata_kind (U32.v n) LowParse.Spec.List.parse_list_kind) (parse_nlist n p); - LP.parser_kind_prop_equiv (LP.total_constant_size_parser_kind (U32.v n)) (parse_nlist n p); - Classical.forall_intro (Classical.move_requires (parse_nlist_total_fixed_size_aux n p)) - inline_for_extraction noextract -let validate_nlist_total_constant_size_mod_ok (n:U32.t) #wk (#k:parser_kind true wk) (#t: Type) (p:parser k t) - : Pure (validator_no_read (parse_nlist n p)) +let validate_nlist_total_constant_size_mod_ok + (n:U32.t) + (n_is_const:option nat { memoizes_n_as_const n_is_const n }) + #wk (#k:parser_kind true wk) (#t: Type) (p:parser k t) + : Pure (validator_no_read (parse_nlist n n_is_const p)) (requires ( let open LP in k.parser_kind_subkind == Some ParserStrong /\ @@ -235,11 +253,11 @@ let validate_nlist_total_constant_size_mod_ok (n:U32.t) #wk (#k:parser_kind true let h = FStar.HyperStack.ST.get () in [@inline_let] let _ = - parse_nlist_total_fixed_size_kind_correct n p; - LPL.valid_facts (parse_nlist n p) h sl (LPL.uint64_to_uint32 pos); - LPL.valid_facts (LP.strengthen (LP.total_constant_size_parser_kind (U32.v n)) (parse_nlist n p)) h sl (LPL.uint64_to_uint32 pos) + parse_nlist_total_fixed_size_kind_correct n n_is_const p; + LPL.valid_facts (parse_nlist n n_is_const p) h sl (LPL.uint64_to_uint32 pos); + LPL.valid_facts (LP.strengthen (LP.total_constant_size_parser_kind (U32.v n)) (parse_nlist n n_is_const p)) h sl (LPL.uint64_to_uint32 pos) in - LPL.validate_total_constant_size_no_read (LP.strengthen (LP.total_constant_size_parser_kind (U32.v n)) (parse_nlist n p)) (FStar.Int.Cast.uint32_to_uint64 n) () sl len pos + LPL.validate_total_constant_size_no_read (LP.strengthen (LP.total_constant_size_parser_kind (U32.v n)) (parse_nlist n n_is_const p)) (FStar.Int.Cast.uint32_to_uint64 n) () sl len pos ) module LUT = LowParse.Spec.ListUpTo diff --git a/src/3d/prelude/EverParse3d.Prelude.fsti b/src/3d/prelude/EverParse3d.Prelude.fsti index 3b37ffd18..04bddc6ee 100644 --- a/src/3d/prelude/EverParse3d.Prelude.fsti +++ b/src/3d/prelude/EverParse3d.Prelude.fsti @@ -110,11 +110,23 @@ val parse_ite (#nz:_) (#wk: _) (#k:parser_kind nz wk) //////////////////////////////////////////////////////////////////////////////// // Variable-sized list whose size in bytes is exactly n //////////////////////////////////////////////////////////////////////////////// +unfold +let memoizes_n_as_const (n_is_const:option nat) (n:U32.t) = + match n_is_const with + | Some m -> m = U32.v n + | _ -> true + val nlist (n:U32.t) (t:Type u#r) : Type u#r inline_for_extraction noextract -val parse_nlist (n:U32.t) (#wk: _) (#k:parser_kind true wk) (#t:_) (p:parser k t) - : Tot (parser kind_nlist (nlist n t)) +val parse_nlist + (n:U32.t) + (n_const:option nat { Some? n_const ==> Some?.v n_const == U32.v n }) + (#wk: _) + (#k:parser_kind true wk) + (#t:_) + (p:parser k t) + : Tot (parser (kind_nlist k n_const) (nlist n t)) ///// // Parse all of the remaining bytes of the input buffer diff --git a/src/3d/prelude/Makefile b/src/3d/prelude/Makefile index 31f6e6d6b..5b428d77c 100644 --- a/src/3d/prelude/Makefile +++ b/src/3d/prelude/Makefile @@ -17,7 +17,7 @@ endif KRML_HOME?=$(realpath ../../../../karamel) OTHERFLAGS?= -FSTAR_OPTIONS=$(addprefix --include , $(EVERPARSE_HOME)/src/lowparse $(KRML_HOME)/krmllib $(KRML_HOME)/krmllib/obj) --max_fuel 0 --max_ifuel 2 --initial_ifuel 2 --z3cliopt 'smt.qi.eager_threshold=10' +FSTAR_OPTIONS=--ext context_pruning $(addprefix --include , $(EVERPARSE_HOME)/src/lowparse $(KRML_HOME)/krmllib $(KRML_HOME)/krmllib/obj) --max_fuel 0 --max_ifuel 2 --initial_ifuel 2 --z3cliopt 'smt.qi.eager_threshold=10' #--z3cliopt 'smt.arith.nl=false' --smtencoding.elim_box true --smtencoding.l_arith_repr native --smtencoding.nl_arith_repr wrapped FSTAR=$(FSTAR_HOME)/bin/fstar.exe $(FSTAR_OPTIONS) $(OTHERFLAGS) --cmi diff --git a/src/3d/tests/BoolSwitch.3d b/src/3d/tests/BoolSwitch.3d index 931329df2..136d992a9 100755 --- a/src/3d/tests/BoolSwitch.3d +++ b/src/3d/tests/BoolSwitch.3d @@ -8,4 +8,16 @@ casetype _T (Bool b) case false: UINT32 integer; } -} T; \ No newline at end of file +} T; + +entrypoint +casetype _S (Bool b) +{ + switch (b) + { + case true: + UINT32 f; + default: + UINT32 g; + } +} S; \ No newline at end of file diff --git a/src/ASN1/.gitignore b/src/ASN1/.gitignore new file mode 100644 index 000000000..289c08392 --- /dev/null +++ b/src/ASN1/.gitignore @@ -0,0 +1 @@ +*.checked diff --git a/src/ASN1/ASN1.Base.fst b/src/ASN1/ASN1.Base.fst new file mode 100755 index 000000000..13668bba3 --- /dev/null +++ b/src/ASN1/ASN1.Base.fst @@ -0,0 +1,514 @@ +module ASN1.Base +open LowParse.Tot.Base +open LowParse.Tot.Combinators + +open ASN1.Spec.Time +open ASN1.Spec.Content.INTEGER + +// ASN.1 Kinds + +module U32 = FStar.UInt32 +module I32 = FStar.Int32 +module U8 = FStar.UInt8 +module B = FStar.Bytes +module Seq = FStar.Seq +module List = FStar.List.Tot + +// ASN.1 Identifier + +type asn1_id_class_t = +| UNIVERSAL +| APPLICATION +| CONTEXT_SPECIFIC +| PRIVATE + +type asn1_id_flag_t = +| PRIMITIVE +| CONSTRUCTED + +type asn1_id_value_t = U32.t + +type asn1_id_t = +| MK_ASN1_ID : c : asn1_id_class_t -> f : asn1_id_flag_t -> v : asn1_id_value_t -> asn1_id_t + +//TODO: constant tables (Currently in X.509) + +//ASN.1 kinds and High-level types + +//Can we describe the correspondence between the kind and the type by defining a function that maps a kind to its type. In that way, we can get the parsers from the partial computation of the function on a template which is slick. + +//A hack for dependency on default + +type asn1_terminal_k : Type = +| ASN1_BOOLEAN +| ASN1_INTEGER : (bound : pos) -> asn1_terminal_k +// | ASN1_ENUM +| ASN1_BITSTRING +| ASN1_OCTETSTRING +| ASN1_PRINTABLESTRING +| ASN1_UTF8STRING +| ASN1_IA5STRING +| ASN1_NULL +| ASN1_OID +// | ASN1_ROID +| ASN1_UTCTIME +| ASN1_GENERALIZEDTIME +| ASN1_PREFIXED_TERMINAL : asn1_id_t -> asn1_terminal_k -> asn1_terminal_k + +type asn1_boolean_t = bool + +type asn1_integer_t (bound : pos) = integer_in_interval bound + +let rec pow2_mono (n m:nat) +: Lemma + (requires n < m) + (ensures pow2 n < pow2 m) += if n + 1 = m then () else pow2_mono n (m - 1) +let pow2_le (n:nat {n < 8}) : Lemma (pow2 n < 256) = pow2_mono n 8 + +//Bitstring is represented as an array of bytes and 0~7 unused bits +type asn1_bitstring_t = +| BYTES_WITH_UNUSEDBITS : + unused : U8.t {0 <= (U8.v unused) /\ (U8.v unused) <= 7} -> + b : B.bytes {let _ = pow2_le (U8.v unused) in + (U8.v unused = 0) \/ + ((U8.v unused > 0) /\ B.length b > 0 /\ + FStar.UInt.mod (U8.v (B.index b ((B.length b) - 1))) (pow2 (U8.v unused)) = 0)} -> asn1_bitstring_t +//TODO: use bit op + +type asn1_octetstring_t = B.bytes + +type utf8_cp_t = (x : U32.t {U32.v x < pow2 21}) + +type asn1_utf8string_t = list utf8_cp_t + +let is_printable_char (ch : U8.t) : bool = + let v = U8.v ch in + (65 <= v && v <= 90) || // A - Z + (97 <= v && v <= 122) || // a - z + (48 <= v && v <= 57) || // 0 - 9 + v = 32 || // (space) + (39 <= v && v <= 41) || // '() + (43 <= v && v <= 47) || // +,-./ + v = 58 || v = 61 || v = 63 // :=? + +type asn1_printablestring_t = list (b : byte {is_printable_char b}) + +let is_ia5_char (ch : U8.t) : bool = U8.v ch < 128 + +type asn1_ia5string_t = list (b : byte {is_ia5_char b}) + +type asn1_null_t = unit + +let asn1_OID_wf' (value1 value2 : U32.t) = + (U32.v value1 < 2 && U32.v value2 < 40) || (U32.v value1 = 2 && U32.v value2 < 256 - 80) + +let asn1_OID_wf (l : list U32.t) = + List.length l >= 2 && + (match l with + | value1 :: value2 :: tl -> asn1_OID_wf' value1 value2) + +type asn1_oid_t = + (l : list U32.t {asn1_OID_wf l}) + +// type asn1_roid_t = unit + +type asn1_utctime_t = (b : B.bytes {is_valid_ASN1UTCTIME b}) + +type asn1_generalizedtime_t = (b : B.bytes {is_valid_ASN1GENERALIZEDTIME b}) + +let rec asn1_terminal_t (k : asn1_terminal_k) : eqtype = + match k with + | ASN1_BOOLEAN -> asn1_boolean_t + | ASN1_INTEGER bound -> asn1_integer_t bound +// | ASN1_ENUM -> asn1_enum_t + | ASN1_BITSTRING -> asn1_bitstring_t + | ASN1_OCTETSTRING -> asn1_octetstring_t + | ASN1_UTF8STRING -> asn1_utf8string_t + | ASN1_PRINTABLESTRING -> asn1_printablestring_t + | ASN1_IA5STRING -> asn1_ia5string_t + | ASN1_NULL -> asn1_null_t + | ASN1_OID -> asn1_oid_t +// | ASN1_ROID -> asn1_roid_t + | ASN1_UTCTIME -> asn1_utctime_t + | ASN1_GENERALIZEDTIME -> asn1_generalizedtime_t + | ASN1_PREFIXED_TERMINAL _ k -> asn1_terminal_t k + +type asn1_decorator : Type = +| PLAIN +| OPTION +| DEFAULT + +let id_dec = Set.set asn1_id_t & asn1_decorator +let id_decs = list id_dec + +let rec asn1_sequence_k_wf' (li : id_decs) (s : Set.set asn1_id_t) : Type = + match li with + | [] -> True + | hd :: tl -> + let (s', d) = hd in + Set.disjoint s s' /\ + (match d with + | PLAIN -> asn1_sequence_k_wf tl + | _ -> asn1_sequence_k_wf' tl (Set.union s s')) + +and asn1_sequence_k_wf (li : id_decs) : Type = + match li with + | [] -> True + | hd :: tl -> + let (s', d) = hd in + match d with + | PLAIN -> asn1_sequence_k_wf tl + | OPTION | DEFAULT -> asn1_sequence_k_wf' tl s' + +let my_as_set l = Set.as_set l + +let proj2_of_3 (#a #b : Type) (#c : a -> b -> Type) (x : dtuple3 a (fun _ -> b) c) : a * b = + let (| xa, xb, _ |) = x in (xa, xb) + +let rec asn1_any_prefix_k_wf' (ks : Set.set asn1_id_t) (li : id_decs) (s : Set.set asn1_id_t) : Type = + match li with + | [] -> Set.disjoint s ks + | hd :: tl -> + let (s', d) = hd in + Set.disjoint s s' /\ + (match d with + | PLAIN -> asn1_any_prefix_k_wf ks tl + | OPTION | DEFAULT -> asn1_any_prefix_k_wf' ks tl (Set.union s s')) + +and asn1_any_prefix_k_wf (ks : Set.set asn1_id_t) (li : id_decs) : Type = + match li with + | [] -> True + | hd :: tl -> + let (s', d) = hd in + match d with + | PLAIN -> asn1_any_prefix_k_wf ks tl + | OPTION | DEFAULT -> asn1_any_prefix_k_wf' ks tl s' + +noeq //noextract +type asn1_content_k : Type = +| ASN1_RESTRICTED_TERMINAL : (k : asn1_terminal_k) -> (is_valid : (asn1_terminal_t k) -> bool) -> asn1_content_k +| ASN1_TERMINAL : asn1_terminal_k -> asn1_content_k +| ASN1_SEQUENCE : asn1_gen_items_lk -> asn1_content_k +| ASN1_SEQUENCE_OF : #s : _ -> asn1_k s -> asn1_content_k +//| ASN1_SET : #s : _ -> asn1_set_k s -> asn1_content_k +| ASN1_SET_OF : #s : _ -> asn1_k s -> asn1_content_k +| ASN1_PREFIXED : #s : _ -> asn1_k s -> asn1_content_k +| ASN1_ANY_DEFINED_BY : + id_decs_prefix : id_decs -> + prefix:asn1_gen_items_l id_decs_prefix -> + id : asn1_id_t -> + key_k : asn1_terminal_k -> + supported : list (asn1_terminal_t key_k * asn1_gen_items_lk) -> + fallback : option asn1_gen_items_lk -> + pf_wf : squash (asn1_any_prefix_k_wf (Set.singleton id) id_decs_prefix) -> + pf_sup : squash (List.noRepeats (List.map fst supported)) -> + asn1_content_k + +// The complete ASN.1 kind is indexed by the set of valid first identifiers +// Note that length does not matter here +and asn1_k : Set.set asn1_id_t -> Type = +| ASN1_ILC : id : asn1_id_t -> asn1_content_k -> asn1_k (Set.singleton id) +| ASN1_CHOICE_ILC : choices : list (asn1_id_t & asn1_content_k) -> + pf : squash (List.noRepeats (List.map fst choices)) -> + asn1_k (my_as_set (List.map fst choices)) +| ASN1_ANY_ILC : asn1_k (Set.complement (Set.empty)) + +and asn1_decorated_k : Set.set asn1_id_t -> asn1_decorator -> Type = +| ASN1_PLAIN_ILC : #s : _ -> k : asn1_k s -> asn1_decorated_k s PLAIN +| ASN1_OPTION_ILC : #s : _ -> k : asn1_k s -> asn1_decorated_k s OPTION +| ASN1_DEFAULT_TERMINAL : id : asn1_id_t -> #k : asn1_terminal_k -> defaultv : asn1_terminal_t k -> asn1_decorated_k (Set.singleton id) DEFAULT +| ASN1_DEFAULT_RESTRICTED_TERMINAL : id : asn1_id_t -> #k : asn1_terminal_k -> is_valid : ((asn1_terminal_t k) -> bool) -> + defaultv : asn1_terminal_t k {is_valid defaultv = true} -> asn1_decorated_k (Set.singleton id) DEFAULT + + +and asn1_gen_items_l : id_decs -> Type0 = + | ASN1_GEN_ITEMS_NIL : asn1_gen_items_l [] + | ASN1_GEN_ITEMS_CONS : s:Set.set asn1_id_t -> d:asn1_decorator -> asn1_decorated_k s d -> + tl:id_decs -> asn1_gen_items_l tl -> asn1_gen_items_l ((s, d)::tl) + +and asn1_gen_items_lk : Type = (id_decs:id_decs { asn1_sequence_k_wf id_decs } & asn1_gen_items_l id_decs) + + +let asn1_gen_item_k : Type = s : Set.set asn1_id_t & d : asn1_decorator & asn1_decorated_k s d + +let asn1_gen_items_k : Type = items : list (asn1_gen_item_k) & squash (asn1_sequence_k_wf (List.map proj2_of_3 items)) + + +let mk_ASN1_GEN_ITEM (#s) (#d) (k : asn1_decorated_k s d) : asn1_gen_item_k = + (| s, d, k |) + +(* Conversions to go between asn1_gen_items_lk and asn1_get_items_k *) +let rec l_as_list (#i:id_decs) (l:asn1_gen_items_l i) + : r:list asn1_gen_item_k { List.map proj2_of_3 r == i } + = match l with + | ASN1_GEN_ITEMS_NIL -> [] + | ASN1_GEN_ITEMS_CONS s d dd _ tl -> + (| s, d, dd |) :: l_as_list tl + +let lk_as_k (lk:asn1_gen_items_lk) + : asn1_gen_items_k + = let (| ids, l |) = lk in + (| l_as_list l, () |) + +let rec list_as_l (l:list asn1_gen_item_k) + : asn1_gen_items_l (List.map proj2_of_3 l) + = match l with + | [] -> ASN1_GEN_ITEMS_NIL + | hd::tl -> + let (| i, d, dd |) = hd in + ASN1_GEN_ITEMS_CONS i d dd _ (list_as_l tl) + +let k_as_lk (k:asn1_gen_items_k) + : asn1_gen_items_lk + = let (| l, _ |) = k in + (| _, list_as_l l |) + +type default_tv (#a : eqtype) (v : a) = +| Default : default_tv v +| Nondefault : v' : a{~(v' = v)} -> default_tv v + +let v_of_default (#a : eqtype) (#v : a) (v' : default_tv v) : a = + match v' with + | Default -> v + | Nondefault v'' -> v'' + +let rec assoc_slt (#xT: eqtype) (#yT : Type) (l : list (xT & yT)) (x : xT) : + Lemma (requires Some? (List.assoc x l)) + (ensures (let Some y = (List.assoc x l) in y << l)) + (decreases l) += match l with + | (a, b) :: t -> if x = a then () else (assoc_slt t x) + +let idlookup_t_postcond (#key : eqtype) (id : key) (lc : list (key & Type)) (t : Type) : GTot Type0 += (t << lc \/ t == False) + +let idlookup_t (#key : eqtype) (id : key) (lc : list (key & Type)) : + Pure Type + (requires True) + (ensures fun t -> idlookup_t_postcond id lc t) += let _ = List.assoc_mem id lc in + let res = List.assoc id lc in + match res with + | Some t -> + let _ = List.assoc_memP_some id t lc in + let _ = assoc_slt lc id in + t + | None -> + let _ = List.assoc_memP_none id lc in + False + +let idlookup_with_fallback_t_postcond (#key : eqtype) (id : key) (lc : list (key & Type)) (fb : Type) (t : Type) : GTot Type0 += (t << lc \/ t == fb) + +let idlookup_with_fallback_t (#key : eqtype) (id : key) (lc : list (key & Type)) (fb : Type) : + Pure Type + (requires True) + (ensures fun t -> idlookup_with_fallback_t_postcond id lc fb t) += let _ = List.assoc_mem id lc in + let res = List.assoc id lc in + match res with + | Some t -> + let _ = List.assoc_memP_some id t lc in + let _ = assoc_slt lc id in + t + | None -> + let _ = List.assoc_memP_none id lc in + fb + +let make_gen_choice_type (#key : eqtype) (lc : list (key & Type)) = id : key & idlookup_t id lc + +let make_gen_choice_type_with_fallback (#key : eqtype) (lc : list (key & Type)) (fb : Type) = id : key & idlookup_with_fallback_t id lc fb + +let isNonEmpty (#t : Type) (l : list t) += match l with + | [] -> false + | _ -> true + +let non_empty_list (t : Type) = l : (list t) {isNonEmpty l} + +let rec asn1_content_t (k : asn1_content_k) : Tot Type (decreases k) = + match k with + | ASN1_RESTRICTED_TERMINAL k' is_valid -> parse_filter_refine is_valid + | ASN1_TERMINAL k' -> asn1_terminal_t k' + | ASN1_SEQUENCE gitems -> asn1_sequence_t_core (dsnd gitems) + | ASN1_SEQUENCE_OF k' -> non_empty_list (asn1_t k') + | ASN1_SET_OF k' -> non_empty_list (asn1_t k') + | ASN1_PREFIXED k' -> asn1_t k' + | ASN1_ANY_DEFINED_BY id_decs_prefix prefix id key_k ls ofb pf_wf pf_sup -> + let suffix_t = + (match ofb with + | None -> make_gen_choice_type (asn1_any_t_core (asn1_terminal_t key_k) ls) + | Some fb -> make_gen_choice_type_with_fallback (asn1_any_t_core (asn1_terminal_t key_k) ls) (asn1_sequence_t_core (dsnd fb))) in + asn1_sequence_any_t_core prefix suffix_t + +and asn1_any_t_core (t : eqtype) (ls : list (t * asn1_gen_items_lk)) : Tot (list (t & Type)) (decreases ls) = + match ls with + | [] -> [] + | h :: tl -> + let (x, y) = h in + (x, asn1_sequence_t_core (dsnd y)) :: asn1_any_t_core t tl + +and asn1_lc_t (lc : list (asn1_id_t & asn1_content_k)) : Tot (list (asn1_id_t & Type)) (decreases lc) = + match lc with + | [] -> [] + | h :: t -> + let (x, y) = h in + (x, asn1_content_t y) :: (asn1_lc_t t) + +and asn1_t (#s : _) (k : asn1_k s) : Tot Type (decreases k) = + match k with + | ASN1_ILC id k' -> asn1_content_t k' + | ASN1_CHOICE_ILC lc pf -> make_gen_choice_type (asn1_lc_t lc) + | ASN1_ANY_ILC -> asn1_id_t & asn1_octetstring_t + +and asn1_decorated_t_core #s #d (dk:asn1_decorated_k s d) : Tot Type (decreases dk) = + match dk with + | ASN1_PLAIN_ILC k -> asn1_t k + | ASN1_OPTION_ILC k -> option (asn1_t k) + | ASN1_DEFAULT_TERMINAL id defv -> default_tv defv + | ASN1_DEFAULT_RESTRICTED_TERMINAL id #k is_valid defv -> default_tv #(parse_filter_refine is_valid) defv + +and asn1_sequence_t_core #id_decs (items:asn1_gen_items_l id_decs) : Tot Type (decreases items) = + match items with + | ASN1_GEN_ITEMS_NIL -> unit + | ASN1_GEN_ITEMS_CONS s d dd _ ASN1_GEN_ITEMS_NIL -> + asn1_decorated_t_core dd + | ASN1_GEN_ITEMS_CONS s d dd _ tl -> + asn1_decorated_t_core dd & asn1_sequence_t_core tl + +and asn1_sequence_any_t_core #id_decs (items : asn1_gen_items_l id_decs) (suffix_t : Type) : Tot Type (decreases items) = + match items with + | ASN1_GEN_ITEMS_NIL -> suffix_t + | ASN1_GEN_ITEMS_CONS s d dd _ ASN1_GEN_ITEMS_NIL -> + asn1_decorated_t_core dd & suffix_t + | ASN1_GEN_ITEMS_CONS s d dd _ tl -> + asn1_decorated_t_core dd & asn1_sequence_any_t_core tl suffix_t + +(* It may be convenient to use these alternative but equivalent formulations in the rest of the development *) +let asn1_decorated_t (i:asn1_gen_item_k) : Type = + let (| _, _, dk |) = i in + asn1_decorated_t_core dk + +let rec asn1_sequence_t (items:list asn1_gen_item_k) = + match items with + | [] -> unit + | [hd] -> + asn1_decorated_t hd + | hd::tl -> + asn1_decorated_t hd & asn1_sequence_t tl + +let rec asn1_sequence_t_core_equiv #id_decs (items:asn1_gen_items_l id_decs) + : Lemma (ensures asn1_sequence_t_core items == asn1_sequence_t (l_as_list items)) + (decreases items) + = match items with + | ASN1_GEN_ITEMS_NIL -> () + | ASN1_GEN_ITEMS_CONS _ _ _ _ tl -> + asn1_sequence_t_core_equiv tl + +let rec asn1_sequence_t_core_equiv' (items:list asn1_gen_item_k) + : Lemma (ensures asn1_sequence_t_core (list_as_l items) == asn1_sequence_t items) + (decreases items) + = match items with + | [] -> () + | hd::tl -> asn1_sequence_t_core_equiv' tl + + +let rec asn1_any_t (t : eqtype) (ls : list (t * asn1_gen_items_k)) : Tot (list (t & Type)) (decreases ls) = + match ls with + | [] -> [] + | h :: tl -> + let (x, y) = h in + (x, asn1_sequence_t (dfst y)) :: asn1_any_t _ tl + +let t_lk_as_t_k #t (ls:list (t & asn1_gen_items_lk)) + : list (t & asn1_gen_items_k) + = List.map (fun (t, lk) -> t, lk_as_k lk) ls + +let t_k_as_t_lk #t (ls:list (t & asn1_gen_items_k)) + : list (t & asn1_gen_items_lk) + = List.map (fun (t, k) -> t, k_as_lk k) ls + +let rec asn1_any_t_equiv (#t:eqtype) (ls:list (t & asn1_gen_items_lk)) + : Lemma (ensures asn1_any_t_core t ls == asn1_any_t t (t_lk_as_t_k ls)) + = match ls with + | [] -> () + | hd::tl -> + let x,y = hd in + asn1_any_t_equiv tl; asn1_sequence_t_core_equiv (dsnd y) + +let rec asn1_any_t_equiv' (#t:eqtype) (ls:list (t & asn1_gen_items_k)) + : Lemma (ensures asn1_any_t_core t (t_k_as_t_lk ls) == asn1_any_t t ls) + = match ls with + | [] -> () + | hd::tl -> + let x,y = hd in + asn1_any_t_equiv' tl; asn1_sequence_t_core_equiv' (dfst y) + + +let rec asn1_sequence_any_t (items : list asn1_gen_item_k) (suffix_t : Type) : Tot Type (decreases items) = + match items with + | [] -> suffix_t + | [hd] -> asn1_decorated_t hd & suffix_t + | hd :: tl -> + asn1_decorated_t hd & asn1_sequence_any_t tl suffix_t + +let rec asn1_sequence_any_t_equiv #id_decs (items : asn1_gen_items_l id_decs) (suffix_t : Type) + : Lemma (ensures asn1_sequence_any_t_core items suffix_t == + asn1_sequence_any_t (l_as_list items) suffix_t) + (decreases items) + = match items with + | ASN1_GEN_ITEMS_NIL -> () + | ASN1_GEN_ITEMS_CONS s d dd _ tl -> + asn1_sequence_any_t_equiv tl suffix_t + + +let rec asn1_sequence_any_t_equiv' (items : list asn1_gen_item_k) (suffix_t : Type) + : Lemma (ensures asn1_sequence_any_t_core (list_as_l items) suffix_t == + asn1_sequence_any_t items suffix_t) + (decreases items) + = match items with + | [] -> () + | hd::tl -> asn1_sequence_any_t_equiv' tl suffix_t + +type asn1_length_u32_t = U32.t + +let asn1_decorated_pure_t (item : asn1_gen_item_k) : Type = + match item with + | (| _, _, dk |) -> match dk with + | ASN1_PLAIN_ILC k -> asn1_t k + | ASN1_OPTION_ILC k -> asn1_t k + | ASN1_DEFAULT_TERMINAL _ #k _ -> asn1_terminal_t k + | ASN1_DEFAULT_RESTRICTED_TERMINAL _ #k is_valid _ -> parse_filter_refine is_valid + +type asn1_strong_parser_kind : parser_kind = { + parser_kind_metadata = None; + parser_kind_low = 0; + parser_kind_high = None; + parser_kind_subkind = Some ParserStrong; +} + +type asn1_weak_parser_kind : parser_kind = { + parser_kind_metadata = None; + parser_kind_low = 0; + parser_kind_high = None; + parser_kind_subkind = None; +} + +type asn1_strong_parser (t : Type) = parser asn1_strong_parser_kind t + +type asn1_weak_parser (t : Type) = parser asn1_weak_parser_kind t + +noeq +type gen_parser (k : parser_kind) = +| Mkgenparser : (t : Type) -> (p : parser k t) -> gen_parser k + +noeq +type parser_twin (k : parser_kind) (t : Type) = +| Mkparsertwin : (p : parser k t) -> (fp : (asn1_id_t -> parser k t) {and_then_cases_injective fp} ) -> parser_twin k t + +noeq +type gen_decorated_parser_twin = +| Mkgendcparser : (d : asn1_gen_item_k) -> (p : asn1_strong_parser (asn1_decorated_pure_t d)) +-> fp : (asn1_id_t -> asn1_strong_parser (asn1_decorated_pure_t d)) {and_then_cases_injective fp} -> +gen_decorated_parser_twin diff --git a/src/ASN1/ASN1.CRL.fst b/src/ASN1/ASN1.CRL.fst new file mode 100644 index 000000000..86017015a --- /dev/null +++ b/src/ASN1/ASN1.CRL.fst @@ -0,0 +1,69 @@ +module ASN1.CRL + +open ASN1.Base +open ASN1.Syntax + +module X509 = ASN1.X509 + +let extension_fallback += mk_gen_items [ + "critical" *^ (DEFAULT ^: X509.critical_field); + "extnValue" *^ (PLAIN ^: asn1_octetstring)] + (_ by (seq_tac ())) + +let crlExtension += asn1_any_oid_with_fallback + "extnId" + [] + extension_fallback + (_ by (seq_tac ())) + (_ by (choice_tac ())) + +let crlExtensions = asn1_sequence_of crlExtension + +let version = mk_restricted_field asn1_integer (fun x -> x = 1) + +let revokedCertificate += asn1_sequence [ + "userCertificate" *^ (PLAIN ^: X509.certificateSerialNumber); + "revocationDate" *^ (PLAIN ^: X509.time); + "crlEntryExtensions" *^ (OPTION ^: crlExtensions)] + (_ by (seq_tac ())) + +let revokedCertificates = asn1_sequence_of revokedCertificate + +let tBSCertList += asn1_sequence [ + "version" *^ (OPTION ^: version); + "signature" *^ (PLAIN ^: X509.algorithmIdentifier); + "issuer" *^ (PLAIN ^: X509.name); + "thisUpdate" *^ (PLAIN ^: X509.time); + "nextUpdate" *^ (OPTION ^: X509.time); + "revokedCertificates" *^ (OPTION ^: revokedCertificates); + "crlExtensions" *^ (OPTION ^: mk_prefixed (mk_custom_id CONTEXT_SPECIFIC CONSTRUCTED 0) crlExtensions)] + (_ by (seq_tac ())) + +let cRLCertificateList += asn1_sequence [ + "tbsCertList" *^ (PLAIN ^: tBSCertList); + "signatureAlgorithm" *^ (PLAIN ^: X509.algorithmIdentifier); + "signatureValue" *^ (PLAIN ^: asn1_bitstring)] + (_ by (seq_tac ())) + +open ASN1.Spec.Interpreter + +let crl_parser = asn1_as_parser cRLCertificateList + + +[@@normalize_for_extraction [delta; + zeta; + iota; + primops]] +let parse_crl (b:bytes) = crl_parser b + +[@@normalize_for_extraction [delta; + zeta; + iota; + primops]] +let dparse_crl (b:bytes) = dasn1_as_parser cRLCertificateList b + diff --git a/src/ASN1/ASN1.Debug.fsti b/src/ASN1/ASN1.Debug.fsti new file mode 100644 index 000000000..f2673d62c --- /dev/null +++ b/src/ASN1/ASN1.Debug.fsti @@ -0,0 +1,28 @@ +module ASN1.Debug +open LowParse.Tot.Base + +val parse_debug + (#t: Type) + (#k: parser_kind) + (msg: string) + (p: parser k t) +: Pure (parser k t) + (requires True) + (ensures (fun f -> forall input . parse f input == parse p input)) + +val print_debug + (#t: Type) + (msg: string) + (v: t) +: Pure t + (requires True) + (ensures (fun v' -> v' == v)) + +val parse_debugf + (#t #t': Type) + (#k: parser_kind) + (msg: string) + (fp: t' -> parser k t) +: Pure (t' -> parser k t) + (requires True) + (ensures (fun f -> forall x input . parse (f x) input == parse (fp x) input)) diff --git a/src/ASN1/ASN1.Low.Content.BOOLEAN.fst b/src/ASN1/ASN1.Low.Content.BOOLEAN.fst new file mode 100755 index 000000000..8b07263ad --- /dev/null +++ b/src/ASN1/ASN1.Low.Content.BOOLEAN.fst @@ -0,0 +1,55 @@ +module ASN1.Low.Content.BOOLEAN + +open ASN1.Base + +open ASN1.Spec.Content.BOOLEAN + +open LowParse.Low.Base +open LowParse.Low.Combinators +open LowParse.Low.Int + +module U8 = FStar.UInt8 +module U32 = FStar.UInt32 +module HS = FStar.HyperStack +module HST = FStar.HyperStack.ST +module B = LowStar.Buffer +module Cast = FStar.Int.Cast +module U64 = FStar.UInt64 + +let valid_asn1_boolean + (h : HS.mem) + (#rrel #rel: _) + (input : slice rrel rel) + (pos : U32.t) +: Lemma ((valid parse_asn1_boolean h input pos) + <==> + (valid parse_u8 h input pos /\ + (let v = contents parse_u8 h input pos in + v == asn1_boolean_TRUE_DER \/ v == asn1_boolean_FALSE_DER))) += valid_facts parse_asn1_boolean h input pos; + valid_facts parse_u8 h input pos; + lemma_parse_asn1_boolean_unfold (bytes_of_slice_from h input pos) + +inline_for_extraction +let validate_asn1_boolean () : Tot (validator parse_asn1_boolean) += fun #rrel #rel (input : slice rrel rel) pos -> + let h = HST.get() in + let _ = + valid_asn1_boolean h input (uint64_to_uint32 pos); + valid_equiv parse_u8 h input (uint64_to_uint32 pos) + in + if U32.lt (input.len `U32.sub` (uint64_to_uint32) pos) 1ul + then + validator_error_not_enough_data + else + let _ = + valid_total_constant_size h parse_u8 1 input (uint64_to_uint32 pos); + assert (valid parse_u8 h input (uint64_to_uint32 pos)) + in + let v' = read_u8 input (uint64_to_uint32 pos) in + if (U8.eq v' asn1_boolean_TRUE_DER) || (U8.eq v' asn1_boolean_FALSE_DER) then + pos `U64.add` 1uL + else + validator_error_generic + +//TODO: Use validator combinators diff --git a/src/ASN1/ASN1.Low.ILC.fst b/src/ASN1/ASN1.Low.ILC.fst new file mode 100644 index 000000000..117e3ead2 --- /dev/null +++ b/src/ASN1/ASN1.Low.ILC.fst @@ -0,0 +1,55 @@ +module ASN1.Low.ILC + +open ASN1.Base + +open ASN1.Spec.ILC +open ASN1.Low.LengthU32 +open ASN1.Low.IdentifierU32 + +open LowParse.Low.Combinators +open LowParse.Low.VLGen + +module U32 = FStar.UInt32 + +inline_for_extraction +let validate_asn1_LC + (#k : asn1_content_k) + (#p : asn1_weak_parser (asn1_content_t k)) + (v : validator p) +: Tot (validator (parse_asn1_LC p)) += validate_weaken _ + (validate_vlgen_weak 0 (U32.uint_to_t 0) 4294967295 (U32.uint_to_t 4294967295) (validate_asn1_lengthu32 ()) (read_asn1_lengthu32 ()) v) () + +(* +let parse_asn1_ILC + (id : asn1_id_t) + (#ack : asn1_content_k) + (p : asn1_weak_parser (asn1_content_t ack)) +: asn1_strong_parser (asn1_content_t ack) += let p' = + parse_asn1_identifier_u21 + `parse_filter` + (fun id' -> id' = id) + `nondep_then` + parse_asn1_LC p + `parse_synth` + (snd) in + weaken asn1_strong_parser_kind p' +*) + +inline_for_extraction +let validate_asn1_ILC + (id : asn1_id_t) + (#k : asn1_content_k) + (#p : asn1_weak_parser (asn1_content_t k)) + (v : validator p) +: Tot (validator (parse_asn1_ILC id p)) += let v' = + validate_synth + (validate_filter (validate_asn1_identifieru21 ()) (read_asn1_identifieru21 ()) (fun id' -> id' = id) (fun id' -> id' = id) + `validate_nondep_then` + validate_asn1_LC v) + (snd) + _ + in + validate_weaken _ v' () diff --git a/src/ASN1/ASN1.Low.IdentifierU32.fst b/src/ASN1/ASN1.Low.IdentifierU32.fst new file mode 100644 index 000000000..30323a66d --- /dev/null +++ b/src/ASN1/ASN1.Low.IdentifierU32.fst @@ -0,0 +1,36 @@ +module ASN1.Low.IdentifierU32 + +open ASN1.Base + +open ASN1.Spec.IdentifierU32 + +open LowParse.Low.Combinators + +module U8 = FStar.UInt8 +module HST = FStar.HyperStack.ST + +inline_for_extraction +let validate_asn1_identifier_tail (state : asn1_partial_id_t) (buf : byte) +: Tot (validator (parse_asn1_identifier_tail state buf)) += fun #rrel #rel (input: slice rrel rel) pos -> + let h = HST.get() in + let _ = + valid_equiv (parse_asn1_identifier_tail state buf) h input (uint64_to_uint32 pos) + in + if U8.lt (buf) (U8.uint_to_t 128) then + pos + else + validator_error_generic + + + +inline_for_extraction +let validate_asn1_identifieru21 () +: Tot (validator parse_asn1_identifier_u21) += admit () + +inline_for_extraction +let read_asn1_identifieru21 () +: Tot (leaf_reader parse_asn1_identifier_u21) += admit () + diff --git a/src/ASN1/ASN1.Low.LengthU32.fst b/src/ASN1/ASN1.Low.LengthU32.fst new file mode 100755 index 000000000..8cd2f2b69 --- /dev/null +++ b/src/ASN1/ASN1.Low.LengthU32.fst @@ -0,0 +1,15 @@ +module ASN1.Low.LengthU32 + +open ASN1.Spec.LengthU32 +open LowParse.Low.Base + +module LPDER = LowParse.Low.DER +module U32 = FStar.UInt32 + +inline_for_extraction +let validate_asn1_lengthu32 () : Tot (validator parse_asn1_length_u32_t) += LPDER.validate_bounded_der_length32 0 (U32.uint_to_t 0) 4294967295 (U32.uint_to_t 4294967295) + +inline_for_extraction +let read_asn1_lengthu32 () : Tot (leaf_reader (parse_asn1_length_u32_t)) += LPDER.read_bounded_der_length32 0 4294967295 diff --git a/src/ASN1/ASN1.Spec.Any.fst b/src/ASN1/ASN1.Spec.Any.fst new file mode 100644 index 000000000..06e44d9ca --- /dev/null +++ b/src/ASN1/ASN1.Spec.Any.fst @@ -0,0 +1,299 @@ +module ASN1.Spec.Any + +open ASN1.Base + +open LowParse.Tot.Combinators + +open ASN1.Spec.Choice +open ASN1.Spec.Sequence + +open ASN1.Spec.IdentifierU32 + +module List = FStar.List.Tot +module Set = FStar.Set + +let rec make_gen_choice_weak_payload_parser + (#t : eqtype) + (lc : list (t & (gen_parser asn1_weak_parser_kind))) +// (pf : List.noRepeats (List.map fst lc)) + : Tot (id : t -> asn1_weak_parser + (refine_with_tag (project_tags lc) id)) += fun id -> + match lc with + | [] -> fail_parser _ _ + | hd :: tl -> + let (id', gp) = hd in + if (id = id') then + let p = (Mkgenparser?.p gp) in + parse_synth p (attach_tag lc id) + else + parse_synth (make_gen_choice_weak_payload_parser tl id) (fun x -> lemma_choice_cast (fst hd, Mkgenparser?.t (snd hd)) (extract_types tl) id x) + +let make_gen_choice_weak_parser + (#t : eqtype) + (#k : parser_kind) + (p : parser k t) + (lc : list (t & (gen_parser asn1_weak_parser_kind))) + //(pf : List.noRepeats (List.map fst lc)) + : parser (and_then_kind k asn1_weak_parser_kind) (make_gen_choice_type (extract_types lc)) += parse_tagged_union p (tag_of_gen_choice_type (extract_types lc)) (make_gen_choice_weak_payload_parser lc) + +let make_gen_choice_weak_parser_twin + (#t : eqtype) + (#k : parser_kind) + (fp : asn1_id_t -> parser k t {and_then_cases_injective fp}) + (lc : list (t & (gen_parser asn1_weak_parser_kind))) + //(pf : List.noRepeats (List.map fst lc)) +: asn1_id_t -> parser (and_then_kind k asn1_weak_parser_kind) (make_gen_choice_type (extract_types lc)) += fun id -> parse_tagged_union (fp id) (tag_of_gen_choice_type (extract_types lc)) (make_gen_choice_weak_payload_parser lc) + +let make_gen_choice_weak_parser_twin_and_then_cases_injective + (#t : eqtype) + (#k : parser_kind) + (fp : asn1_id_t -> parser k t {and_then_cases_injective fp}) + (lc : list (t & (gen_parser asn1_weak_parser_kind))) +: Lemma (ensures (and_then_cases_injective (make_gen_choice_weak_parser_twin fp lc))) += let p = make_gen_choice_weak_parser_twin fp lc in + and_then_cases_injective_intro p (fun x1 x2 b1 b2 -> + parse_tagged_union_eq (fp x1) (tag_of_gen_choice_type (extract_types lc)) (make_gen_choice_weak_payload_parser lc) b1; + parse_tagged_union_eq (fp x2) (tag_of_gen_choice_type (extract_types lc)) (make_gen_choice_weak_payload_parser lc) b2; + and_then_cases_injective_elim fp x1 x2 b1 b2 + ) + +let tag_of_gen_choice_type_with_fallback (#key : eqtype) (lc : list (key & Type)) (fb : Type) : make_gen_choice_type_with_fallback lc fb -> Tot key = dfst + +let project_tags_with_fallback (#t : eqtype) (#k : parser_kind) (lc : list (t & (gen_parser k))) (fb : gen_parser k) = +tag_of_gen_choice_type_with_fallback (extract_types lc) (Mkgenparser?.t fb) + +let attach_tag_with_fallback (#t : eqtype) (#k : parser_kind) (lc : list (t & (gen_parser k))) (fb : gen_parser k) (id : t) (x : idlookup_with_fallback_t id (extract_types lc) (Mkgenparser?.t fb)) : + (refine_with_tag (project_tags_with_fallback lc fb) id) += Mkdtuple2 id x + +let lemma_choice_with_fallback_cast + (#key : eqtype) + (hd : key & Type) + (tl : list (key & Type)) + (fb : Type) + (id : key) + (ret : refine_with_tag (tag_of_gen_choice_type_with_fallback tl fb) id) +: Pure (refine_with_tag (tag_of_gen_choice_type_with_fallback (hd :: tl) fb) id) + (requires id <> (fst hd)) + (ensures (fun _ -> True)) += let (|id', v|) = ret in + (|id', v|) + +let rec make_gen_choice_with_fallback_weak_payload_parser + (#t : eqtype) + (lc : list (t & (gen_parser asn1_weak_parser_kind))) + (fb : gen_parser asn1_weak_parser_kind) +// (pf : List.noRepeats (List.map fst lc)) + : Tot (id : t -> asn1_weak_parser + (refine_with_tag (project_tags_with_fallback lc fb) id)) += fun id -> + match lc with + | [] -> + assert (lc == []); + let _ = List.map_lemma (fun x -> (fst x, Mkgenparser?.t (snd x))) lc in + assert (extract_types lc == []); + parse_synth (Mkgenparser?.p fb) (attach_tag_with_fallback lc fb id) + | hd :: tl -> + let (id', gp) = hd in + if (id = id') then + let p = (Mkgenparser?.p gp) in + parse_synth p (attach_tag_with_fallback lc fb id) + else + parse_synth (make_gen_choice_with_fallback_weak_payload_parser tl fb id) (fun x -> lemma_choice_with_fallback_cast (fst hd, Mkgenparser?.t (snd hd)) (extract_types tl) (Mkgenparser?.t fb) id x) + +let make_gen_choice_with_fallback_weak_parser + (#t : eqtype) + (#k : parser_kind) + (p : parser k t) + (lc : list (t & (gen_parser asn1_weak_parser_kind))) + (fb : gen_parser asn1_weak_parser_kind) + //(pf : List.noRepeats (List.map fst lc)) + : parser (and_then_kind k asn1_weak_parser_kind) (make_gen_choice_type_with_fallback (extract_types lc) (Mkgenparser?.t fb)) += parse_tagged_union p (tag_of_gen_choice_type_with_fallback (extract_types lc) (Mkgenparser?.t fb)) (make_gen_choice_with_fallback_weak_payload_parser lc fb) + +let make_gen_choice_with_fallback_weak_parser_twin + (#t : eqtype) + (#k : parser_kind) + (fp : asn1_id_t -> parser k t {and_then_cases_injective fp}) + (lc : list (t & (gen_parser asn1_weak_parser_kind))) + (fb : gen_parser asn1_weak_parser_kind) + //(pf : List.noRepeats (List.map fst lc)) + : asn1_id_t -> parser (and_then_kind k asn1_weak_parser_kind) (make_gen_choice_type_with_fallback (extract_types lc) (Mkgenparser?.t fb)) += fun id -> parse_tagged_union (fp id) (tag_of_gen_choice_type_with_fallback (extract_types lc) (Mkgenparser?.t fb)) (make_gen_choice_with_fallback_weak_payload_parser lc fb) + +let make_gen_choice_with_fallback_weak_parser_twin_and_then_cases_injective + (#t : eqtype) + (#k : parser_kind) + (fp : asn1_id_t -> parser k t {and_then_cases_injective fp}) + (lc : list (t & (gen_parser asn1_weak_parser_kind))) + (fb : gen_parser asn1_weak_parser_kind) +: Lemma (ensures (and_then_cases_injective (make_gen_choice_with_fallback_weak_parser_twin fp lc fb))) += let p = make_gen_choice_with_fallback_weak_parser_twin fp lc fb in + and_then_cases_injective_intro p (fun x1 x2 b1 b2 -> + parse_tagged_union_eq (fp x1) (tag_of_gen_choice_type_with_fallback (extract_types lc) (Mkgenparser?.t fb)) (make_gen_choice_with_fallback_weak_payload_parser lc fb) b1; + parse_tagged_union_eq (fp x2) (tag_of_gen_choice_type_with_fallback (extract_types lc) (Mkgenparser?.t fb)) (make_gen_choice_with_fallback_weak_payload_parser lc fb) b2; + and_then_cases_injective_elim fp x1 x2 b1 b2 + ) + +let asn1_sequence_any_parser_type (l : list (gen_decorated_parser_twin)) (t : Type) += asn1_weak_parser (asn1_sequence_any_t (List.map (Mkgendcparser?.d) l) t) + +let asn1_sequence_any_parser_guard_type (itemtwins : list (gen_decorated_parser_twin)) (suffix_t : Type) += option asn1_id_t -> + asn1_sequence_any_parser_type itemtwins suffix_t + +let asn1_sequence_any_parser_body_type (itemtwins : list (gen_decorated_parser_twin)) (suffix_t : Type) += asn1_id_t -> + asn1_sequence_any_parser_type itemtwins suffix_t + +let make_asn1_sequence_any_parser_body + (itemtwins : list (gen_decorated_parser_twin) {Cons? itemtwins}) + (#suffix_t : Type) + (ploop : asn1_sequence_any_parser_guard_type (List.tl itemtwins) suffix_t) +: Pure (asn1_id_t -> asn1_sequence_any_parser_type itemtwins suffix_t) + (requires (and_then_cases_injective_some ploop)) + (ensures (fun _ -> True)) += fun id -> + match itemtwins with + | hd :: tl -> match hd with + | Mkgendcparser d p fp -> + let (| s, de, dk |) = d in + let (p, ns) = + if (Set.mem id s) then + (parse_asn1_sequence_item_twin hd id, None) + else + (match de with + | PLAIN -> (fail_parser _ _, None) + | _ -> let defv' = generate_defaultable_item hd in + match defv' with + | Some defv -> (weaken asn1_strong_parser_kind (parse_ret defv), Some id)) + in + weaken asn1_weak_parser_kind (p `nondep_then` (ploop ns)) + +let make_asn1_sequence_any_parser_body_and_then_cases_injective + (itemtwins : list (gen_decorated_parser_twin) {Cons? itemtwins}) + (#suffix_t : Type) + (ploop : asn1_sequence_any_parser_guard_type (List.tl itemtwins) suffix_t) +: Lemma (requires (and_then_cases_injective_some ploop)) + (ensures (and_then_cases_injective (make_asn1_sequence_any_parser_body itemtwins ploop))) += let p = make_asn1_sequence_any_parser_body itemtwins ploop in + and_then_cases_injective_intro p (fun id1 id2 b1 b2 -> + match itemtwins with | hd :: tl -> + match hd with | Mkgendcparser d p fp -> + let (| s, de, dk |) = d in + match (Set.mem id1 s), (Set.mem id2 s) with + | true, true -> + let p1 = parse_asn1_sequence_item_twin hd id1 in + let p2 = parse_asn1_sequence_item_twin hd id2 in + let p = ploop None in + parse_asn1_sequence_item_twin_cases_injective hd; + nondep_then_eq p1 p b1; + nondep_then_eq p2 p b2 + | true, false -> + (match de with + | PLAIN -> + let p = fail_parser asn1_strong_parser_kind (asn1_decorated_t (Mkgendcparser?.d hd)) in + nondep_then_eq p (ploop None) b2 + | _ -> match (generate_defaultable_item hd) with | Some defv -> + let p = parse_asn1_sequence_item_twin hd id1 in + nondep_then_eq p (ploop None) b1; + let p' = weaken asn1_strong_parser_kind (parse_ret defv) in + nondep_then_eq p' (ploop (Some id2)) b2; + parse_asn1_sequence_item_twin_nondefault hd id1 b1) + | false, true -> + (match de with + | PLAIN -> + let p = fail_parser asn1_strong_parser_kind (asn1_decorated_t (Mkgendcparser?.d hd)) in + nondep_then_eq p (ploop None) b1 + | _ -> match (generate_defaultable_item hd) with | Some defv -> + let p = parse_asn1_sequence_item_twin hd id2 in + nondep_then_eq p (ploop None) b2; + let p' = weaken asn1_strong_parser_kind (parse_ret defv) in + nondep_then_eq p' (ploop (Some id1)) b1; + parse_asn1_sequence_item_twin_nondefault hd id2 b2) + | false, false -> + match de with + | PLAIN -> + let p = fail_parser asn1_strong_parser_kind (asn1_decorated_t (Mkgendcparser?.d hd)) in + nondep_then_eq p (ploop None) b1 + | _ -> match (generate_defaultable_item hd) with | Some defv -> + let p = weaken asn1_strong_parser_kind (parse_ret defv) in + nondep_then_eq p (ploop (Some id1)) b1; + nondep_then_eq p (ploop (Some id2)) b2; + and_then_cases_injective_some_elim ploop id1 id2 b1 b2 + ) + +let make_asn1_sequence_any_parser_guard + (itemtwins : list (gen_decorated_parser_twin) {Cons? itemtwins}) + (#suffix_t : Type) + (pbody : asn1_sequence_any_parser_body_type itemtwins suffix_t) + (s : option (asn1_id_t)) +: Pure (asn1_sequence_any_parser_type itemtwins suffix_t) + (requires (and_then_cases_injective pbody)) + (ensures (fun _ -> True)) += let p = + match s with + | Some id -> weaken asn1_strong_parser_kind (parse_ret id) + | None -> weaken asn1_strong_parser_kind (parse_asn1_identifier_U32) + in + p `and_then` pbody + +let make_asn1_sequence_any_parser_guard_and_then_cases_injective + (itemtwins : list (gen_decorated_parser_twin) {Cons? itemtwins}) + (#suffix_t : Type) + (pbody : asn1_sequence_any_parser_body_type itemtwins suffix_t) +: Lemma + (requires and_then_cases_injective pbody) + (ensures (and_then_cases_injective_some (make_asn1_sequence_any_parser_guard itemtwins pbody))) += let p = make_asn1_sequence_any_parser_guard itemtwins pbody in + and_then_cases_injective_some_intro p (fun s1 s2 b1 b2 -> + match s1 with | Some x1 -> + match s2 with | Some x2 -> + let p1 = weaken asn1_strong_parser_kind (parse_ret x1) in + and_then_eq p1 pbody b1; + let p2 = weaken asn1_strong_parser_kind (parse_ret x2) in + and_then_eq p2 pbody b2; + and_then_cases_injective_elim pbody x1 x2 b1 b2 + ) + +let rec make_asn1_sequence_any_parser'' + (itemtwins : list (gen_decorated_parser_twin) {Cons?itemtwins}) + (#suffix_t : Type) + (suffix_p_twin : parser_twin asn1_weak_parser_kind suffix_t) +: Pure (asn1_sequence_any_parser_body_type itemtwins suffix_t) + (requires True) + (ensures (fun fp -> and_then_cases_injective fp)) + (decreases %[itemtwins;0]) += let p = make_asn1_sequence_any_parser' (List.tl itemtwins) suffix_p_twin in + let _ = make_asn1_sequence_any_parser_body_and_then_cases_injective itemtwins p in + make_asn1_sequence_any_parser_body itemtwins p + +and make_asn1_sequence_any_parser' + (itemtwins : list (gen_decorated_parser_twin)) + (#suffix_t : Type) + (suffix_p_twin : parser_twin asn1_weak_parser_kind suffix_t) +: Pure (asn1_sequence_any_parser_guard_type itemtwins suffix_t) + (requires True) + (ensures fun fp -> and_then_cases_injective_some fp) + (decreases %[itemtwins;1]) += match itemtwins with + | [] -> + (fun s -> match s with + | Some id -> Mkparsertwin?.fp suffix_p_twin id + | None -> Mkparsertwin?.p suffix_p_twin) + | _ -> + let p = make_asn1_sequence_any_parser'' itemtwins suffix_p_twin in + let _ = make_asn1_sequence_any_parser_guard_and_then_cases_injective itemtwins p in + make_asn1_sequence_any_parser_guard itemtwins p + +let make_asn1_sequence_any_parser + (itemtwins : list (gen_decorated_parser_twin)) + (#suffix_t : Type) + (suffix_p_twin : parser_twin asn1_weak_parser_kind suffix_t) +// (pf : (asn1_sequence_k_wf (List.map project_set_decorator itemtwins))) +: Tot (asn1_sequence_any_parser_type itemtwins suffix_t) += make_asn1_sequence_any_parser' itemtwins suffix_p_twin None + diff --git a/src/ASN1/ASN1.Spec.Automata.fst b/src/ASN1/ASN1.Spec.Automata.fst new file mode 100644 index 000000000..5ef768cbd --- /dev/null +++ b/src/ASN1/ASN1.Spec.Automata.fst @@ -0,0 +1,473 @@ +module ASN1.Spec.Automata + +open LowParse.Tot.Base +open LowParse.Tot.Combinators + +open ASN1.Base + +noeq +type automata_control_param = { + control_t : eqtype; + ch_t : eqtype; + fail_check : control_t -> ch_t -> bool; + termination_check : (s : control_t) -> + (ch : ch_t {fail_check s ch = false}) -> bool; + next_state : (s : control_t) -> + (ch : ch_t {fail_check s ch = false /\ termination_check s ch = false}) -> control_t; +} + +(* + +type extend_control_t (t : Type) = +| CFail +| CTerm +| CCont : (s : t) -> extend_control_t t + +let automata_cp_small_step + (cp : automata_control_param) + (s : cp.control_t) + (ch : cp.ch_t) +: extend_control_t (cp.control_t) += if (cp.fail_check s ch) then + CFail + else + if (cp.termination_check s ch) then + CTerm + else + CCont (cp.next_state s ch) + +// Cannot define big step due to non-termination unless finite input + +let rec automata_cp_big_step_list + (cp : automata_control_param) + (s : cp.control_t) + (lch : list cp.ch_t) +: Tot (extend_control_t cp.control_t) + (decreases lch) += let cur = automata_cp_small_step cp s in + match lch with + | [] -> CFail + | ch :: tl -> + match (cur ch) with + | CCont s' -> automata_cp_big_step_list cp s' tl + | other -> other + +*) + +noeq +type automata_data_param (cp : automata_control_param) = { + ret_t : eqtype; + partial_t : eqtype; + pre_t : cp.control_t -> partial_t -> Type0; + post_t : (s : cp.control_t) -> + (data : partial_t {pre_t s data}) -> + ret_t -> Type0; + update_term : (s : cp.control_t) -> + (data : partial_t {pre_t s data}) -> + (ch : cp.ch_t {cp.fail_check s ch = false /\ cp.termination_check s ch = true}) -> + (ret : ret_t {post_t s data ret}); + update_next : (s : cp.control_t) -> + (data : partial_t {pre_t s data}) -> + (ch : cp.ch_t {cp.fail_check s ch = false /\ cp.termination_check s ch = false}) -> + (data' : partial_t {pre_t (cp.next_state s ch) data'}); + lemma_cast_ret : (state : cp.control_t) -> + (data : partial_t {pre_t state data}) -> + (ch : cp.ch_t {cp.fail_check state ch = false /\ cp.termination_check state ch = false}) -> + (ret : ret_t) -> + Lemma (requires (post_t (cp.next_state state ch) (update_next state data ch) ret)) + (ensures (post_t state data ret)) +} + +(* +type extend_control_data_t + (cp : automata_control_param) + (dp : automata_data_param cp) += | DFail + | DTerm : dp.ret_t -> extend_control_data_t cp dp + | DCont : (s : cp.control_t) -> (data : dp.partial_t {dp.pre_t s data}) -> extend_control_data_t cp dp + +let automata_cp_dp_small_step + (cp : automata_control_param) + (dp : automata_data_param cp) + (s : cp.control_t) + (data : dp.partial_t {dp.pre_t s data}) + (ch : cp.ch_t) +: extend_control_data_t cp dp += if cp.fail_check s ch then + DFail + else + if cp.termination_check s ch then + DTerm (dp.update_term s data ch) + else + DCont (cp.next_state s ch) (dp.update_next s data ch) +*) + +noeq +type automata_bare_parser_param (cp : automata_control_param) = { + ch_t_bare_parser : bare_parser cp.ch_t; + ch_t_bare_parser_valid : unit -> Lemma (parses_at_least 1 ch_t_bare_parser) +} + +let id_cast + (t : eqtype) + (p1 : t -> Type0) + (p2 : t -> Type0) + (lem : (x : t -> (Lemma (requires p1 x) (ensures p2 x)))) + (x : t {p1 x}) +: (x' : t {p2 x'}) += let _ = lem x in + (x <: (x : t {p2 x})) + +let rec automata_bare_parser' + (cp : automata_control_param) + (dp : automata_data_param cp) + (bp : automata_bare_parser_param cp) + (s : cp.control_t) + (data : dp.partial_t {dp.pre_t s data}) + (b : bytes) +: Tot (option ((ret : dp.ret_t {dp.post_t s data ret}) * (consumed_length b))) + (decreases (Seq.length b)) += match (bp.ch_t_bare_parser b) with + | None -> None + | Some (ch, l) -> + if cp.fail_check s ch then + None + else + if cp.termination_check s ch then + Some (dp.update_term s data ch, l) + else + ( + let _ = assert (parse bp.ch_t_bare_parser b == Some (ch, l)) in // SMT pattern on `parse` + let s' = cp.next_state s ch in + let data' = dp.update_next s data ch in + let _ = bp.ch_t_bare_parser_valid () in + let (b' : bytes{Seq.length b' < Seq.length b}) = Seq.slice b l (Seq.length b) in + match (automata_bare_parser' cp dp bp s' data' b') with + | None -> None + | Some (ret, l') -> + Some (id_cast dp.ret_t (dp.post_t s' data') (dp.post_t s data) (dp.lemma_cast_ret s data ch) ret, l + l') + ) + +type automata_default_parser_kind : parser_kind = { + parser_kind_metadata = None; + parser_kind_low = 0; + parser_kind_high = None; + parser_kind_subkind = Some ParserStrong; +} + +noeq +type automata_parser_param (cp : automata_control_param) (dp : automata_data_param cp) (bp : automata_bare_parser_param cp) = { + ch_t_parser_valid : unit -> Lemma (parser_kind_prop automata_default_parser_kind bp.ch_t_bare_parser); + + lemma_update_term_inj2 : ((state : cp.control_t) -> + (data1 : dp.partial_t {dp.pre_t state data1}) -> + (data2 : dp.partial_t {dp.pre_t state data2}) -> + (ch1 : cp.ch_t {cp.fail_check state ch1 = false /\ cp.termination_check state ch1 = true}) -> + (ch2 : cp.ch_t {cp.fail_check state ch2 = false /\ cp.termination_check state ch2 = true}) -> + Lemma (requires (dp.update_term state data1 ch1 = dp.update_term state data2 ch2)) + (ensures (data1 = data2 /\ ch1 = ch2))); + + lemma_update_term_next_non_intersect : ((state : cp.control_t) -> + (data1 : dp.partial_t {dp.pre_t state data1}) -> + (data2 : dp.partial_t {dp.pre_t state data2}) -> + (ch1 : cp.ch_t {cp.fail_check state ch1 = false /\ cp.termination_check state ch1 = true}) -> + (ch2 : cp.ch_t {cp.fail_check state ch2 = false /\ cp.termination_check state ch2 = false}) -> + (ret1 : dp.ret_t {dp.post_t state data1 ret1}) -> + (ret2 : dp.ret_t {dp.post_t (cp.next_state state ch2) (dp.update_next state data2 ch2) ret2}) -> + Lemma (requires (ret1 = dp.update_term state data1 ch1 /\ ret1 = ret2)) + (ensures False)); + + lemma_update_next_non_intersect : ((state : cp.control_t) -> + (data1 : dp.partial_t {dp.pre_t state data1}) -> + (data2 : dp.partial_t {dp.pre_t state data2}) -> + (ch1 : cp.ch_t {cp.fail_check state ch1 = false /\ cp.termination_check state ch1 = false}) -> + (ch2 : cp.ch_t {cp.fail_check state ch2 = false /\ cp.termination_check state ch2 = false}) -> + (ret1 : dp.ret_t {dp.post_t (cp.next_state state ch1) (dp.update_next state data1 ch1) ret1}) -> + (ret2 : dp.ret_t {dp.post_t (cp.next_state state ch2) (dp.update_next state data2 ch2) ret2}) -> + Lemma (requires (cp.next_state state ch1 <> cp.next_state state ch2 /\ ret1 = ret2)) + (ensures False)); + + lemma_update_next_inj2 : ((state : cp.control_t) -> + (data1 : dp.partial_t {dp.pre_t state data1}) -> + (data2 : dp.partial_t {dp.pre_t state data2}) -> + (ch1 : cp.ch_t {cp.fail_check state ch1 = false /\ cp.termination_check state ch1 = false}) -> + (ch2 : cp.ch_t {cp.fail_check state ch2 = false /\ cp.termination_check state ch2 = false}) -> + Lemma (requires cp.next_state state ch1 = cp.next_state state ch2 /\ dp.update_next state data1 ch1 = dp.update_next state data2 ch2) + (ensures data1 = data2 /\ ch1 = ch2)) +} + +let and_then_cases_injective_dep_precond + (#t:Type) + (#t': Type) + (gt : t -> t' -> Type0) + (p': ((x : t) -> Tot (bare_parser (y : t' {gt x y})))) + (x1 x2: t) + (b1 b2: bytes) +: GTot Type0 += Some? (parse (p' x1) b1) /\ + Some? (parse (p' x2) b2) /\ ( + let (Some (v1, _)) = parse (p' x1) b1 in + let (Some (v2, _)) = parse (p' x2) b2 in + v1 == v2 + ) + +let and_then_cases_injective_dep + (#t : Type) + (#t' : Type) + (gt : t -> t' -> Type0) + (p': ((m : t) -> Tot (bare_parser (x : t' {gt m x})))) +: GTot Type0 += forall (x1 x2: t) (b1 b2: bytes) . {:pattern (parse (p' x1) b1); (parse (p' x2) b2)} + and_then_cases_injective_dep_precond gt p' x1 x2 b1 b2 ==> + x1 == x2 + +let and_then_cases_injective_dep_intro + (#t : Type) + (#t': Type) + (gt : t -> t' -> Type0) + (p': ((x : t) -> Tot (bare_parser (y : t' {gt x y})))) + (lem: ( + (x1: t) -> + (x2: t) -> + (b1: bytes) -> + (b2: bytes) -> + Lemma + (requires (and_then_cases_injective_dep_precond gt p' x1 x2 b1 b2)) + (ensures (x1 == x2)) + )) +: Lemma + (and_then_cases_injective_dep gt p') += Classical.forall_intro_4 (fun x1 x2 b1 b2 -> (Classical.move_requires (lem x1 x2 b1) b2)) + +let rec automata_bare_parser'_pf1_aux + (cp : automata_control_param) + (dp : automata_data_param cp) + (bp : automata_bare_parser_param cp) + (pp : automata_parser_param cp dp bp) + (s : cp.control_t) + (x1 : dp.partial_t {dp.pre_t s x1}) + (x2 : dp.partial_t {dp.pre_t s x2}) + (b1 : bytes) + (b2 : bytes) +: Lemma + (requires (and_then_cases_injective_dep_precond (dp.post_t s) (automata_bare_parser' cp dp bp s) x1 x2 b1 b2)) + (ensures (x1 == x2)) + (decreases (Seq.length b1)) += let p = automata_bare_parser' cp dp bp in + match (parse (p s x1) b1) with | Some (ret1, l1) -> + match (parse (p s x2) b2) with | Some (ret2, l2) -> + match (parse (bp.ch_t_bare_parser) b1) with | Some (ch1, l01) -> + match (parse (bp.ch_t_bare_parser) b2) with | Some (ch2, l02) -> + if cp.fail_check s ch1 then + _ + else if cp.fail_check s ch2 then + _ + else if cp.termination_check s ch1 then + ( + if cp.termination_check s ch2 then + pp.lemma_update_term_inj2 s x1 x2 ch1 ch2 + else + pp.lemma_update_term_next_non_intersect s x1 x2 ch1 ch2 ret1 ret2 + ) + else + ( + if cp.termination_check s ch2 then + pp.lemma_update_term_next_non_intersect s x2 x1 ch2 ch1 ret2 ret1 + else + ( + let s'1 = cp.next_state s ch1 in + let s'2 = cp.next_state s ch2 in + let x'1 = dp.update_next s x1 ch1 in + let _ = bp.ch_t_bare_parser_valid () in + let (b'1 : bytes{Seq.length b'1 < Seq.length b1}) = Seq.slice b1 l01 (Seq.length b1) in + let x'2 = dp.update_next s x2 ch2 in + let _ = bp.ch_t_bare_parser_valid () in + let (b'2 : bytes{Seq.length b'2 < Seq.length b2}) = Seq.slice b2 l02 (Seq.length b2) in + if s'1 = s'2 then + ( + let _ = automata_bare_parser'_pf1_aux cp dp bp pp s'1 x'1 x'2 b'1 b'2 in + pp.lemma_update_next_inj2 s x1 x2 ch1 ch2 + ) + else + ( + pp.lemma_update_next_non_intersect s x1 x2 ch1 ch2 ret1 ret2 + ) + ) + ) + +let automata_bare_parser'_pf1 + (cp : automata_control_param) + (dp : automata_data_param cp) + (bp : automata_bare_parser_param cp) + (pp : automata_parser_param cp dp bp) + (s : cp.control_t) +: Lemma (and_then_cases_injective_dep (dp.post_t s) (automata_bare_parser' cp dp bp s)) += let p = automata_bare_parser' cp dp bp s in + and_then_cases_injective_dep_intro (dp.post_t s) p (automata_bare_parser'_pf1_aux cp dp bp pp s) + +let rec automata_bare_parser'_pf2_aux + (cp : automata_control_param) + (dp : automata_data_param cp) + (bp : automata_bare_parser_param cp) + (pp : automata_parser_param cp dp bp) + (s : cp.control_t) + (x : dp.partial_t {dp.pre_t s x}) + (b1 : bytes) + (b2 : bytes) +: Lemma (requires no_lookahead_on_precond (automata_bare_parser' cp dp bp s x) b1 b2) + (ensures no_lookahead_on_postcond (automata_bare_parser' cp dp bp s x) b1 b2) + (decreases (Seq.length b1)) += let p = automata_bare_parser' cp dp bp s x in + let _ = pp.ch_t_parser_valid () in + let _ = parser_kind_prop_equiv automata_default_parser_kind bp.ch_t_bare_parser in + match (parse p b1) with | Some (ret1, l1) -> + match (parse (bp.ch_t_bare_parser) b1) with | Some (ch1, l01) -> + let _ = + Seq.lemma_split (Seq.slice b2 0 l1) l01; + Seq.lemma_split (Seq.slice b1 0 l1) l01 + in + assert (Seq.slice b2 0 l01 == Seq.slice b1 0 l01); + match (parse (bp.ch_t_bare_parser) b2) with + | None -> + assert (no_lookahead_on bp.ch_t_bare_parser b1 b2) + | Some (ch2, l02) -> + assert (ch1 = ch2); + assert (injective_precond bp.ch_t_bare_parser b1 b2); + assert ((l01 <: nat) = (l02 <: nat)); + if cp.fail_check s ch1 then + _ + else if cp.termination_check s ch1 then + _ + else + ( + let s' = cp.next_state s ch1 in + let x' = dp.update_next s x ch1 in + let _ = bp.ch_t_bare_parser_valid () in + let (b'1 : bytes{Seq.length b'1 < Seq.length b1}) = Seq.slice b1 l01 (Seq.length b1) in + let _ = bp.ch_t_bare_parser_valid () in + let (b'2 : bytes{Seq.length b'2 < Seq.length b2}) = Seq.slice b2 l01 (Seq.length b2) in + automata_bare_parser'_pf2_aux cp dp bp pp s' x' b'1 b'2 + ) + +let automata_bare_parser'_pf2 + (cp : automata_control_param) + (dp : automata_data_param cp) + (bp : automata_bare_parser_param cp) + (pp : automata_parser_param cp dp bp) + (s : cp.control_t) + (x : dp.partial_t {dp.pre_t s x}) +: Lemma (no_lookahead (automata_bare_parser' cp dp bp s x)) += Classical.forall_intro_2 (fun b1 b2 -> Classical.move_requires (automata_bare_parser'_pf2_aux cp dp bp pp s x b1) b2) + +let rec automata_bare_parser'_pf3_aux + (cp : automata_control_param) + (dp : automata_data_param cp) + (bp : automata_bare_parser_param cp) + (pp : automata_parser_param cp dp bp) + (s : cp.control_t) + (x : dp.partial_t {dp.pre_t s x}) + (b1 : bytes) + (b2 : bytes) +: Lemma (requires injective_precond (automata_bare_parser' cp dp bp s x) b1 b2) + (ensures injective_postcond (automata_bare_parser' cp dp bp s x) b1 b2) + (decreases (Seq.length b1)) += let p = automata_bare_parser' cp dp bp s x in + let _ = pp.ch_t_parser_valid () in + let _ = parser_kind_prop_equiv automata_default_parser_kind bp.ch_t_bare_parser in + match (parse p b1) with | Some (ret1, l1) -> + match (parse p b2) with | Some (ret2, l2) -> + match (parse (bp.ch_t_bare_parser) b1) with | Some (ch1, l01) -> + match (parse (bp.ch_t_bare_parser) b2) with | Some (ch2, l02) -> + let _ = + Seq.lemma_split (Seq.slice b1 0 l1) l01; + Seq.lemma_split (Seq.slice b2 0 l2) l02 + in + if cp.fail_check s ch1 then + _ + else if cp.fail_check s ch2 then + _ + else if cp.termination_check s ch1 then + ( + if cp.termination_check s ch2 then + ( + pp.lemma_update_term_inj2 s x x ch1 ch2; + assert (injective_precond bp.ch_t_bare_parser b1 b2); + assert ((l01 <: nat) = (l02 <: nat)) + ) + else + pp.lemma_update_term_next_non_intersect s x x ch1 ch2 ret1 ret2 + ) + else + ( + if cp.termination_check s ch2 then + pp.lemma_update_term_next_non_intersect s x x ch2 ch1 ret2 ret1 + else + ( + let s'1 = cp.next_state s ch1 in + let s'2 = cp.next_state s ch2 in + let x'1 = dp.update_next s x ch1 in + let x'2 = dp.update_next s x ch2 in + let _ = bp.ch_t_bare_parser_valid () in + let (b'1 : bytes{Seq.length b'1 < Seq.length b1}) = Seq.slice b1 l01 (Seq.length b1) in + let _ = bp.ch_t_bare_parser_valid () in + let (b'2 : bytes{Seq.length b'2 < Seq.length b2}) = Seq.slice b2 l02 (Seq.length b2) in + if s'1 = s'2 then + ( + let _ = automata_bare_parser'_pf1_aux cp dp bp pp s'1 x'1 x'2 b'1 b'2 in + pp.lemma_update_next_inj2 s x x ch1 ch2; + assert (injective_precond bp.ch_t_bare_parser b1 b2); + assert ((l01 <: nat) = (l02 <: nat)); + let _ = automata_bare_parser'_pf3_aux cp dp bp pp s'1 x'1 b'1 b'2 in + _ + ) + else + ( + pp.lemma_update_next_non_intersect s x x ch1 ch2 ret1 ret2 + ) + ) + ) + +let automata_bare_parser'_pf3 + (cp : automata_control_param) + (dp : automata_data_param cp) + (bp : automata_bare_parser_param cp) + (pp : automata_parser_param cp dp bp) + (s : cp.control_t) + (x : dp.partial_t {dp.pre_t s x}) +: Lemma (injective (automata_bare_parser' cp dp bp s x)) += Classical.forall_intro_2 (fun b1 b2 -> Classical.move_requires (automata_bare_parser'_pf3_aux cp dp bp pp s x b1) b2) + +let automata_bare_parser'_pf23 + (cp : automata_control_param) + (dp : automata_data_param cp) + (bp : automata_bare_parser_param cp) + (pp : automata_parser_param cp dp bp) + (s : cp.control_t) + (x : dp.partial_t {dp.pre_t s x}) +: Lemma (parser_kind_prop automata_default_parser_kind (automata_bare_parser' cp dp bp s x)) += parser_kind_prop_equiv automata_default_parser_kind (automata_bare_parser' cp dp bp s x); + automata_bare_parser'_pf2 cp dp bp pp s x; + automata_bare_parser'_pf3 cp dp bp pp s x + +let automata_bare_parser'_pf + (cp : automata_control_param) + (dp : automata_data_param cp) + (bp : automata_bare_parser_param cp) + (pp : automata_parser_param cp dp bp) +: Lemma ( + (forall s. and_then_cases_injective_dep (dp.post_t s) (automata_bare_parser' cp dp bp s)) /\ + (forall s data. parser_kind_prop automata_default_parser_kind (automata_bare_parser' cp dp bp s data))) += Classical.forall_intro (fun s -> automata_bare_parser'_pf1 cp dp bp pp s); + Classical.forall_intro_2 (fun s data -> automata_bare_parser'_pf23 cp dp bp pp s data) + +let automata_parser + (cp : automata_control_param) + (dp : automata_data_param cp) + (bp : automata_bare_parser_param cp) + (pp : automata_parser_param cp dp bp) + (s : cp.control_t) + (data : dp.partial_t {dp.pre_t s data}) +: (parser automata_default_parser_kind (ret : dp.ret_t {dp.post_t s data ret})) += let _ = automata_bare_parser'_pf cp dp bp pp in + automata_bare_parser' cp dp bp s data + diff --git a/src/ASN1/ASN1.Spec.Choice.fst b/src/ASN1/ASN1.Spec.Choice.fst new file mode 100755 index 000000000..ea6f41c21 --- /dev/null +++ b/src/ASN1/ASN1.Spec.Choice.fst @@ -0,0 +1,128 @@ +module ASN1.Spec.Choice + +open ASN1.Base + +open LowParse.Tot.Base +open LowParse.Tot.Combinators + +open ASN1.Spec.IdentifierU32 + +module Math = FStar.Math.Lib +module List = FStar.List.Tot +module Set = FStar.Set + +(* + +let sanitify_parser_kind (k : parser_kind) +: Pure parser_kind + (requires True) + (ensures fun k -> fail_parser_kind_precond k) += { + parser_kind_low = k.parser_kind_low; + parser_kind_high = k.parser_kind_high; + parser_kind_subkind = k.parser_kind_subkind; + parser_kind_metadata = (match k.parser_kind_metadata with Some ParserKindMetadataFail -> Some ParserKindMetadataFail | _ -> None); + } + +let rec make_gen_choice_kind + (lc : list gen_parser {Cons? lc}) +: Pure parser_kind + (requires True) + (ensures (fun k -> + fail_parser_kind_precond k /\ + (let lk = List.map Mkgenparser?.k lc in + forall k'. {:pattern (List.memP k' lk)} (List.memP k' lk) ==> is_weaker_than k k'))) += match lc with + | [h] -> + let k = Mkgenparser?.k h in + sanitify_parser_kind k + | h :: t -> glb (Mkgenparser?.k h) (make_gen_choice_kind t) + +*) + +let tag_of_gen_choice_type (#key : eqtype) (lc : list (key & Type)) : make_gen_choice_type lc -> Tot key = dfst + +let extract_types (#t : eqtype) (#k : parser_kind) (lc : list (t & (gen_parser k))) = + List.map (fun x -> (fst x, Mkgenparser?.t (snd x))) lc + +let project_tags (#t : eqtype) (#k : parser_kind) (lc : list (t & (gen_parser k))) = +tag_of_gen_choice_type (extract_types lc) + +let attach_tag (#t : eqtype) (#k : parser_kind) (lc : list (t & (gen_parser k))) (id : t) (x : idlookup_t id (extract_types lc)) : + (refine_with_tag (project_tags lc) id) += Mkdtuple2 id x + +let lemma_choice_cast + (#key : eqtype) + (hd : key & Type) + (tl : list (key & Type)) + (id : key) + (ret : refine_with_tag (tag_of_gen_choice_type tl) id) +: Pure (refine_with_tag (tag_of_gen_choice_type (hd :: tl)) id) + (requires id <> (fst hd)) + (ensures (fun _ -> True)) += let (|id', v|) = ret in + (|id', v|) + +let rec make_gen_choice_strong_payload_parser + (#t : eqtype) + (lc : list (t & (gen_parser asn1_strong_parser_kind))) +// (pf : List.noRepeats (List.map fst lc)) + : Tot (id : t -> asn1_strong_parser + (refine_with_tag (project_tags lc) id)) += fun id -> + match lc with + | [] -> fail_parser _ _ + | hd :: tl -> + let (id', gp) = hd in + if (id = id') then + let p = (Mkgenparser?.p gp) in + parse_synth p (attach_tag lc id) + else + parse_synth (make_gen_choice_strong_payload_parser tl id) (fun x -> lemma_choice_cast (fst hd, Mkgenparser?.t (snd hd)) (extract_types tl) id x) + +let make_gen_choice_strong_parser + (#t : eqtype) + (#k : parser_kind) + (p : parser k t) + (lc : list (t & (gen_parser asn1_strong_parser_kind))) + //(pf : List.noRepeats (List.map fst lc)) + : parser (and_then_kind k asn1_strong_parser_kind) (make_gen_choice_type (extract_types lc)) += parse_tagged_union p (tag_of_gen_choice_type (extract_types lc)) (make_gen_choice_strong_payload_parser lc) + +let make_asn1_choice_parser + (lc : list (asn1_id_t * asn1_content_k)) + (pf : squash (List.noRepeats (List.map fst lc))) + (#s : _) + (k : asn1_k s) + (lp : list (asn1_id_t & (gen_parser asn1_strong_parser_kind))) + : + Pure (asn1_strong_parser (asn1_t k)) + (requires (s == Set.as_set (List.map fst lc)) /\ (k == ASN1_CHOICE_ILC lc pf) /\ (asn1_lc_t lc == extract_types lp)) + (ensures fun _ -> True) += weaken asn1_strong_parser_kind (make_gen_choice_strong_parser parse_asn1_identifier_U32 lp) + +let make_asn1_choice_parser_twin + (lc : list (asn1_id_t * asn1_content_k)) + (pf : squash (List.noRepeats (List.map fst lc))) + (#s : _) + (k : asn1_k s) + (lp : list (asn1_id_t & (gen_parser asn1_strong_parser_kind))) + (id' : asn1_id_t) + : + Pure (asn1_strong_parser (asn1_t k)) + (requires (s == Set.as_set (List.map fst lc)) /\ (k == ASN1_CHOICE_ILC lc pf) /\ (asn1_lc_t lc == extract_types lp)) + (ensures fun _ -> True) += parse_tagged_union_payload (project_tags lp) (make_gen_choice_strong_payload_parser lp) id' + +let make_asn1_choice_parser_twin_cases_injective + (lc : list (asn1_id_t * asn1_content_k)) + (pf : squash (List.noRepeats (List.map fst lc))) + (#s : _) + (k : asn1_k s) + (lp : list (asn1_id_t & (gen_parser asn1_strong_parser_kind))) + : + Lemma + (requires (s == Set.as_set (List.map fst lc)) /\ (k == ASN1_CHOICE_ILC lc pf) /\ (asn1_lc_t lc == extract_types lp)) + (ensures and_then_cases_injective (make_asn1_choice_parser_twin lc pf k lp)) += parse_tagged_union_payload_and_then_cases_injective (project_tags lp) (make_gen_choice_strong_payload_parser lp) diff --git a/src/ASN1/ASN1.Spec.Content.BITSTRING.fst b/src/ASN1/ASN1.Spec.Content.BITSTRING.fst new file mode 100644 index 000000000..9c589c21f --- /dev/null +++ b/src/ASN1/ASN1.Spec.Content.BITSTRING.fst @@ -0,0 +1,57 @@ +module ASN1.Spec.Content.BITSTRING + +open ASN1.Base + +open LowParse.Tot.Combinators +open LowParse.Tot.Bytes + +module U8 = FStar.UInt8 +module B = FStar.Bytes + +let filter_asn1_bitstring_unused (u : U8.t) += 0 <= U8.v u && U8.v u <= 7 + +let filter_asn1_bitstring_payload (u : U8.t {0 <= U8.v u /\ U8.v u <= 7}) (b : B.bytes) += if (U8.v u = 0) then + true + else if (B.length b > 0) then + let lastb = B.index b (B.length b - 1) in + let mask = pow2 (U8.v u) in + (FStar.UInt.mod (U8.v lastb) mask = 0) + else + false + +(* TODO: use the a named thing *) + +let synth_asn1_bitstring (u : U8.t {0 <= U8.v u /\ U8.v u <= 7}) + (b : B.bytes {(U8.v u = 0) \/ + ((U8.v u > 0) /\ B.length b > 0 /\ + FStar.UInt.mod (U8.v (B.index b ((B.length b) - 1))) (pow2 (U8.v u)) = 0)}) += BYTES_WITH_UNUSEDBITS u b + +let parse_asn1_bitstring_payload (u : U8.t {0 <= U8.v u /\ U8.v u <= 7}) : asn1_weak_parser asn1_bitstring_t += weaken _ + (parse_all_bytes + `parse_filter` + (filter_asn1_bitstring_payload u) + `parse_synth` + (synth_asn1_bitstring u)) + +let parse_asn1_bitstring_payload_cases_injective () : + Lemma (ensures and_then_cases_injective parse_asn1_bitstring_payload) += let p = parse_asn1_bitstring_payload in + and_then_cases_injective_intro p ( + fun u1 u2 b1 b2 -> + parse_all_bytes_injective (); + parse_synth_eq (parse_all_bytes `parse_filter` (filter_asn1_bitstring_payload u1)) (synth_asn1_bitstring u1) b1; + parse_synth_eq (parse_all_bytes `parse_filter` (filter_asn1_bitstring_payload u2)) (synth_asn1_bitstring u2) b2 + ) + +let parse_asn1_bitstring : asn1_weak_parser asn1_bitstring_t += let _ = parse_asn1_bitstring_payload_cases_injective () in + weaken _ + (parse_u8 + `parse_filter` + filter_asn1_bitstring_unused + `and_then` + parse_asn1_bitstring_payload) diff --git a/src/ASN1/ASN1.Spec.Content.BOOLEAN.fst b/src/ASN1/ASN1.Spec.Content.BOOLEAN.fst new file mode 100755 index 000000000..9b79c1ce4 --- /dev/null +++ b/src/ASN1/ASN1.Spec.Content.BOOLEAN.fst @@ -0,0 +1,72 @@ +module ASN1.Spec.Content.BOOLEAN + +open ASN1.Base + +open LowParse.Tot.Base +open LowParse.Tot.Combinators +open LowParse.Tot.Int + +module U8 = FStar.UInt8 + +let byte = U8.t + +(* Ref: X.690 8.2 and 11.1 *) + +inline_for_extraction +let asn1_boolean_TRUE_DER : byte = 0xFFuy + +inline_for_extraction +let asn1_boolean_FALSE_DER : byte = 0x00uy + +let is_valid_asn1_boolean (b : byte) += b = asn1_boolean_TRUE_DER || b = asn1_boolean_FALSE_DER + +let decode_asn1_boolean (b : parse_filter_refine is_valid_asn1_boolean) : asn1_boolean_t += b = asn1_boolean_TRUE_DER + +let encode_asn1_boolean (b : asn1_boolean_t) : parse_filter_refine is_valid_asn1_boolean += match b with + | true -> asn1_boolean_TRUE_DER + | false -> asn1_boolean_FALSE_DER + +let parse_asn1_boolean_t_kind = strong_parser_kind 1 1 None + +let parse_asn1_boolean : parser parse_asn1_boolean_t_kind asn1_boolean_t += parse_u8 + `parse_filter` + is_valid_asn1_boolean + `parse_synth` + decode_asn1_boolean + +let lemma_parse_asn1_boolean_unfold input : + Lemma (parse parse_asn1_boolean input == + (match parse parse_u8 input with + | Some (x, consumed) -> if is_valid_asn1_boolean x then + Some (decode_asn1_boolean x, consumed) + else + None + | None -> None)) += parser_kind_prop_equiv asn1_weak_parser_kind parse_asn1_boolean; + parser_kind_prop_equiv parse_u8_kind parse_u8; + if Seq.length input > 0 then + (parse_u8_spec input; + parse_u8_spec' input); + parse_filter_eq + (parse_u8) + (is_valid_asn1_boolean) + (input); + parse_synth_eq + (parse_u8 + `parse_filter` + is_valid_asn1_boolean) + (decode_asn1_boolean) + (input) + +let serialize_asn1_boolean : serializer parse_asn1_boolean += serialize_synth + (parse_u8 `parse_filter` is_valid_asn1_boolean) + (decode_asn1_boolean) + (serialize_u8 `serialize_filter` is_valid_asn1_boolean) + (encode_asn1_boolean) + () + diff --git a/src/ASN1/ASN1.Spec.Content.IA5STRING.fst b/src/ASN1/ASN1.Spec.Content.IA5STRING.fst new file mode 100644 index 000000000..eb0285e99 --- /dev/null +++ b/src/ASN1/ASN1.Spec.Content.IA5STRING.fst @@ -0,0 +1,16 @@ +module ASN1.Spec.Content.IA5STRING + +open ASN1.Base + +open LowParse.Tot.Combinators +open LowParse.Tot.Int +open LowParse.Tot.List + +module U8 = FStar.UInt8 + +let parse_ia5_char = + parse_u8 `parse_filter` is_ia5_char + +let parse_asn1_ia5string : asn1_weak_parser asn1_ia5string_t = + weaken _ (parse_list parse_ia5_char) + diff --git a/src/ASN1/ASN1.Spec.Content.INTEGER.fst b/src/ASN1/ASN1.Spec.Content.INTEGER.fst new file mode 100644 index 000000000..7213e6916 --- /dev/null +++ b/src/ASN1/ASN1.Spec.Content.INTEGER.fst @@ -0,0 +1,1143 @@ +module ASN1.Spec.Content.INTEGER + +module BF = LowParse.BitFields +module E = LowParse.Endianness.BitFields +module U = FStar.UInt +module U8 = FStar.UInt8 + +open FStar.Mul + +// Make all integer proofs (pow2, etc.) explicit +#push-options "--z3cliopt smt.arith.nl=false --fuel 0" + +let rec be_to_n_leading_zeros + (s: Seq.seq U8.t) +: Lemma + (requires (Seq.length s > 0)) + (ensures ( + E.be_to_n s < pow2 (8 * (Seq.length s - 1)) <==> U8.v (Seq.index s 0) == 0 + )) + (decreases (Seq.length s)) += E.reveal_be_to_n s; + let s' = Seq.slice s 0 (Seq.length s - 1) in + E.reveal_be_to_n s'; + assert_norm (pow2 0 == 1); + assert_norm (pow2 8 == 256); + if Seq.length s = 1 + then () + else begin + be_to_n_leading_zeros s'; + FStar.Math.Lemmas.pow2_plus 8 (8 * (Seq.length s' - 1)) + end + +let rec be_to_n_chop_leading_zeros + (s: Seq.seq U8.t) +: Lemma + (requires ( + Seq.length s > 0 /\ + U8.v (Seq.index s 0) == 0 + )) + (ensures ( + E.be_to_n s == E.be_to_n (Seq.slice s 1 (Seq.length s)) + )) + (decreases (Seq.length s)) += + E.reveal_be_to_n s; + let s' = Seq.slice s 0 (Seq.length s - 1) in + E.reveal_be_to_n s'; + let sm = Seq.slice s 1 (Seq.length s) in + E.reveal_be_to_n sm; + assert_norm (pow2 8 == 256); + if Seq.length s = 1 + then () + else begin + be_to_n_chop_leading_zeros s'; + FStar.Math.Lemmas.pow2_plus 8 (8 * (Seq.length s' - 1)) + end + +let rec be_to_n_chop_leading_zeros_seq + (s: Seq.seq U8.t) + (d: nat) +: Lemma + (requires ( + True + )) + (ensures ( + E.be_to_n (Seq.create d 0uy `Seq.append` s) == E.be_to_n s + )) + (decreases d) += let s' = Seq.create d 0uy `Seq.append` s in + if d = 0 + then assert (s' `Seq.equal` s) + else begin + let s1 = Seq.cons 0uy s in + be_to_n_chop_leading_zeros s1; + assert (Seq.slice s1 1 (Seq.length s1) `Seq.equal` s); + be_to_n_chop_leading_zeros_seq s1 (d - 1); + assert (s' `Seq.equal` (Seq.create (d - 1) 0uy `Seq.append` s1)) + end + +let be_to_n_chop_leading_zeros_recip + (s1 s2: Seq.seq U8.t) +: Lemma + (requires ( + Seq.length s1 > 0 /\ + Seq.length s1 <= Seq.length s2 /\ + E.be_to_n s1 == E.be_to_n s2 + )) + (ensures ( + s2 `Seq.equal` (Seq.create (Seq.length s2 - Seq.length s1) 0uy `Seq.append` s1) + )) += let d = Seq.length s2 - Seq.length s1 in + let s2' = Seq.create d 0uy `Seq.append` s1 in + be_to_n_chop_leading_zeros_seq s1 d; + E.be_to_n_inj s2' s2 + +let rec be_to_n_msb + (s: Seq.seq U8.t) +: Lemma + (requires (Seq.length s > 0)) + (ensures ( + E.be_to_n s >= pow2 (8 * Seq.length s - 1) <==> U8.v (Seq.index s 0) >= 128 + )) + (decreases (Seq.length s)) += E.reveal_be_to_n s; + let s' = Seq.slice s 0 (Seq.length s - 1) in + E.reveal_be_to_n s'; + assert_norm (pow2 7 == 128); + assert_norm (pow2 8 == 256); + if Seq.length s = 1 + then () + else begin + be_to_n_msb s'; + FStar.Math.Lemmas.pow2_plus 8 (8 * Seq.length s' - 1) + end + +let outer_positive_interval (n: nat) (x: int) : Tot prop = + n > 0 /\ + x >= pow2 (8 * (n - 1)) /\ + x < pow2 (8 * n - 1) + +let outer_positive_interval_intro (x: Seq.seq U8.t) : Lemma + (requires ( + Seq.length x > 0 /\ + begin let c = U8.v (Seq.index x 0) in + c > 0 /\ c < 128 + end + )) + (ensures ( + outer_positive_interval (Seq.length x) (E.be_to_n x) + )) += be_to_n_leading_zeros x; + be_to_n_msb x + +let outer_positive_interval_elim (n: nat) (x: int) : Lemma + (requires ( + outer_positive_interval n x + )) + (ensures ( + x > 0 /\ x < pow2 (8 * n) /\ + begin let c = U8.v (Seq.index (E.n_to_be n x) 0) in + c > 0 /\ c < 128 + end + )) += FStar.Math.Lemmas.pow2_le_compat (8 * n) (8 * n - 1); + let s = E.n_to_be n x in + be_to_n_leading_zeros s; + be_to_n_msb s + +let inner_positive_interval (n: nat) (x: int) : Tot prop = + n > 1 /\ + x >= pow2 (8 * (n - 1) - 1) /\ + x < pow2 (8 * (n - 1)) + +let inner_positive_interval_intro (x: Seq.seq U8.t) : Lemma + (requires ( + Seq.length x > 1 /\ + U8.v (Seq.index x 0) == 0 /\ + U8.v (Seq.index x 1) >= 128 + )) + (ensures ( + inner_positive_interval (Seq.length x) (E.be_to_n x) + )) += be_to_n_leading_zeros x; + be_to_n_chop_leading_zeros x; + be_to_n_msb (Seq.slice x 1 (Seq.length x)) + +let inner_positive_interval_elim (n: nat) (x: int) : Lemma + (requires (inner_positive_interval n x)) + (ensures ( + x > 0 /\ x < pow2 (8 * n) /\ + begin let s = E.n_to_be n x in + Seq.length s > 1 /\ + U8.v (Seq.index s 0) == 0 /\ + U8.v (Seq.index s 1) >= 128 + end + )) += FStar.Math.Lemmas.pow2_lt_compat (8 * n) (8 * (n - 1)); + let s = E.n_to_be n x in + be_to_n_leading_zeros s; + be_to_n_chop_leading_zeros s; + be_to_n_msb (Seq.slice s 1 (Seq.length s)) + +let rec be_to_n_leading_ones + (s: Seq.seq U8.t) +: Lemma + (requires (Seq.length s > 0)) + (ensures ( + E.be_to_n s >= pow2 (8 * (Seq.length s - 1)) * 255 <==> U8.v (Seq.index s 0) == 255 + )) + (decreases (Seq.length s)) += E.reveal_be_to_n s; + let s' = Seq.slice s 0 (Seq.length s - 1) in + E.reveal_be_to_n s'; + assert_norm (pow2 7 == 128); + assert_norm (pow2 8 == 256); + if Seq.length s = 1 + then () + else begin + be_to_n_leading_ones s'; + FStar.Math.Lemmas.pow2_plus 8 (8 * (Seq.length s' - 1)) + end + +let pow2_8_n_255 (n: nat) : Lemma + (pow2 (8 * n) * 255 == pow2 (8 * (n + 1)) - pow2 (8 * n)) += assert_norm (pow2 8 == 256); + FStar.Math.Lemmas.pow2_plus (8 * n) 8 + +// 2's complement representation of negative integers can have their leading ones chopped + +let rec be_to_n_chop_leading_ones + (s: Seq.seq U8.t) +: Lemma + (requires ( + Seq.length s > 0 /\ + U8.v (Seq.index s 0) == 255 + )) + (ensures ( + E.be_to_n s - pow2 (8 * Seq.length s) == E.be_to_n (Seq.slice s 1 (Seq.length s)) - pow2 (8 * (Seq.length s - 1)) + )) + (decreases (Seq.length s)) += E.reveal_be_to_n s; + let s' = Seq.slice s 0 (Seq.length s - 1) in + E.reveal_be_to_n s'; + let sm = Seq.slice s 1 (Seq.length s) in + E.reveal_be_to_n sm; + assert_norm (pow2 7 == 128); + assert_norm (pow2 8 == 256); + if Seq.length s = 1 + then () + else begin + be_to_n_chop_leading_ones s'; + FStar.Math.Lemmas.pow2_plus 8 (8 * (Seq.length s - 1)); + FStar.Math.Lemmas.pow2_plus 8 (8 * (Seq.length s' - 1)) + end + +let rec be_to_n_chop_leading_ones_seq + (s: Seq.seq U8.t) + (d: nat) +: Lemma + (requires ( + True + )) + (ensures ( + let s' = Seq.create d 255uy `Seq.append` s in + E.be_to_n s' - pow2 (8 * Seq.length s') == E.be_to_n s - pow2 (8 * Seq.length s) + )) + (decreases d) += let s' = Seq.create d 255uy `Seq.append` s in + if d = 0 + then assert (s' `Seq.equal` s) + else begin + let s1 = Seq.cons 255uy s in + be_to_n_chop_leading_ones s1; + assert (Seq.slice s1 1 (Seq.length s1) `Seq.equal` s); + be_to_n_chop_leading_ones_seq s1 (d - 1); + assert (s' `Seq.equal` (Seq.create (d - 1) 255uy `Seq.append` s1)) + end + +let be_to_n_chop_leading_ones_recip + (s1 s2: Seq.seq U8.t) +: Lemma + (requires ( + Seq.length s1 <= Seq.length s2 /\ + E.be_to_n s1 - pow2 (8 * Seq.length s1) == E.be_to_n s2 - pow2 (8 * Seq.length s2) + )) + (ensures ( + s2 `Seq.equal` (Seq.create (Seq.length s2 - Seq.length s1) 255uy `Seq.append` s1) + )) += let d = Seq.length s2 - Seq.length s1 in + let s2' = Seq.create d 255uy `Seq.append` s1 in + be_to_n_chop_leading_ones_seq s1 d; + E.be_to_n_inj s2' s2 + +let outer_negative_interval (n: nat) (x: int) : Tot prop = + n > 0 /\ + begin let u = x + pow2 (8 * n) in + u < pow2 (8 * (n - 1)) * 255 /\ + u >= pow2 (8 * n - 1) + end + +let outer_negative_interval_intro (x: Seq.seq U8.t) : Lemma + (requires ( + Seq.length x > 0 /\ + begin let c = U8.v (Seq.index x 0) in + c >= 128 /\ c < 255 + end + )) + (ensures ( + outer_negative_interval (Seq.length x) (E.be_to_n x - pow2 (8 * Seq.length x)) + )) += be_to_n_msb x; + be_to_n_leading_ones x + +let outer_negative_interval_elim (n: nat) (x: int) : Lemma + (requires ( + outer_negative_interval n x + )) + (ensures ( + n > 0 /\ + begin let u = x + pow2 (8 * n) in + 0 <= u /\ + u < pow2 (8 * n) /\ + x < -1 /\ + begin let c = U8.v (Seq.index (E.n_to_be n u) 0) in + c >= 128 /\ c < 255 + end end + )) += let u = x + pow2 (8 * n) in + assert_norm (256 == pow2 8); + FStar.Math.Lemmas.pow2_plus (8 * (n - 1)) 8; + let s = E.n_to_be n u in + be_to_n_msb s; + be_to_n_leading_ones s + +let inner_negative_interval (n: nat) (x: int) : Tot prop = + n > 1 /\ + x + pow2 (8 * n) >= pow2 (8 * (n - 1)) * 255 /\ + x + pow2 (8 * (n - 1)) < pow2 (8 * (n - 1) - 1) + +let inner_negative_interval_upper_bound (n: nat) (x: int) : Lemma + (requires (inner_negative_interval n x)) + (ensures ( + 0 <= x + pow2 (8 * n) /\ + x + pow2 (8 * n) < pow2 (8 * n) + )) += FStar.Math.Lemmas.pow2_plus (8 * (n - 1) - 1) 1 + +let inner_negative_interval_intro (x: Seq.seq U8.t) : Lemma + (requires ( + Seq.length x > 1 /\ + U8.v (Seq.index x 0) == 255 /\ + begin let c = U8.v (Seq.index x 1) in + c < 128 + end + )) + (ensures ( + inner_negative_interval (Seq.length x) (E.be_to_n x - pow2 (8 * Seq.length x)) + )) += be_to_n_leading_ones x; + be_to_n_chop_leading_ones x; + be_to_n_msb (Seq.slice x 1 (Seq.length x)) + +let inner_negative_interval_elim (n: nat) (x: int) : Lemma + (requires ( + inner_negative_interval n x + )) + (ensures ( + n > 1 /\ + x < -1 /\ + begin let u = x + pow2 (8 * n) in + let u' = x + pow2 (8 * (n - 1)) in + 0 <= u /\ u < pow2 (8 * n) /\ + 0 <= u' /\ u' < pow2 (8 * (n - 1)) /\ + begin let s = E.n_to_be n u in + let s' = E.n_to_be (n - 1) u' in + U8.v (Seq.index s 0) == 255 /\ + s' `Seq.equal` Seq.slice s 1 (Seq.length s) /\ + begin let c = U8.v (Seq.index s 1) in + c < 128 + end end end + )) += let u' = x + pow2 (8 * (n - 1)) in + pow2_8_n_255 (n - 1); + FStar.Math.Lemmas.pow2_le_compat (8 * (n - 1)) (8 * (n - 1) - 1); + let s' = E.n_to_be (n - 1) u' in + let s = Seq.cons 255uy s' in + assert (Seq.slice s 1 (Seq.length s) `Seq.equal` s'); + be_to_n_leading_ones s; + be_to_n_chop_leading_ones s; + be_to_n_msb s'; + FStar.Math.Lemmas.pow2_lt_compat (8 * (n - 1)) (8 * (n - 1) - 1) + +(* Sanity-check: the whole signed interval is covered *) + +let domain (n: nat) (x: int) : Tot prop = + (n == 1 /\ (x == 0 \/ x == -1)) \/ inner_positive_interval n x \/ outer_positive_interval n x \/ inner_negative_interval n x \/ outer_negative_interval n x + +let interval (n: nat) (x: int) : Tot prop = + n > 0 /\ + begin let abs_val = pow2 (8 * n - 1) in + 0 - abs_val <= x /\ x < abs_val + end + +let interval_weaken (n1 n2: nat) (x: int) : Lemma + (requires (interval n1 x /\ n1 <= n2)) + (ensures (interval n2 x)) += FStar.Math.Lemmas.pow2_le_compat (8 * n2 - 1) (8 * n1 - 1) + +let interval_intro_inner_positive (n: nat) (x: int) : Lemma + (requires (inner_positive_interval n x)) + (ensures (interval n x)) += FStar.Math.Lemmas.pow2_le_compat (8 * n - 1) (8 * (n - 1)) + +let interval_intro_outer_positive (n: nat) (x: int) : Lemma + (requires (outer_positive_interval n x)) + (ensures (interval n x)) += () + +let interval_intro_inner_negative (n: nat) (x: int) : Lemma + (requires (inner_negative_interval n x)) + (ensures (interval n x)) += inner_negative_interval_elim n x; + FStar.Math.Lemmas.pow2_le_compat (8 * n - 1) (8 * (n - 1)) + +let interval_intro_outer_negative (n: nat) (x: int) : Lemma + (requires (outer_negative_interval n x)) + (ensures (interval n x)) += assert_norm (pow2 1 == 2); + FStar.Math.Lemmas.pow2_plus (8 * n - 1) 1; + outer_negative_interval_elim n x + +let interval_intro (n: nat) (x: int) : Lemma + (requires (domain n x)) + (ensures (interval n x)) += Classical.move_requires (interval_intro_inner_negative n) x; + Classical.move_requires (interval_intro_inner_positive n) x; + Classical.move_requires (interval_intro_outer_negative n) x; + Classical.move_requires (interval_intro_outer_positive n) x + +let rec interval_elim (n: nat) (x: int) : Pure nat + (requires ( + interval n x + )) + (ensures (fun n' -> + n' <= n /\ domain n' x + )) + (decreases n) += if x = 0 || x = -1 + then 1 + else if x > 0 + then begin + FStar.Math.Lemmas.pow2_le_compat (8 * n) (8 * n - 1); + let s = E.n_to_be n x in + let c0 = U8.v (Seq.index s 0) in + be_to_n_msb s; + if c0 > 0 + then begin + outer_positive_interval_intro s; + n + end else begin + be_to_n_chop_leading_zeros s; + let s' = Seq.slice s 1 (Seq.length s) in + if n = 1 + then begin + E.reveal_be_to_n s'; + assert False; + n + end else begin + assert (n > 1); + be_to_n_msb s'; + if U8.v (Seq.index s 1) >= 128 + then begin + inner_positive_interval_intro s; + n + end else begin + interval_elim (n - 1) x + end + end + end + end else begin + let u = x + pow2 (8 * n) in + FStar.Math.Lemmas.pow2_plus (8 * n - 1) 1; + assert_norm (pow2 1 == 2); + let s = E.n_to_be n u in + be_to_n_msb s; + be_to_n_leading_ones s; + if U8.v (Seq.index s 0) < 255 + then begin + outer_negative_interval_intro s; + n + end else begin + be_to_n_chop_leading_ones s; + let s' = Seq.slice s 1 (Seq.length s) in + if n = 1 + then begin + E.reveal_be_to_n s'; + assert False; + n + end else begin + assert (n > 1); + let u' = x + pow2 (8 * (n - 1)) in + assert (s' == E.n_to_be (n - 1) u'); + let c1 = U8.v (Seq.index s' 0) in + be_to_n_msb s'; + if c1 < 128 + then begin + inner_negative_interval_intro s; + n + end else begin + FStar.Math.Lemmas.pow2_plus (8 * (n - 1) - 1) 1; + interval_elim (n - 1) x + end + end + end + end + +let interval_equiv (n: nat) (x: int) : Lemma + (interval n x <==> (exists (n': nat) . n' <= n /\ domain n' x)) += + let f () : Lemma + (requires (interval n x)) + (ensures (exists (n': nat) . n' <= n /\ domain n' x)) + = let _ = interval_elim n x in + () + in + let g + (n': nat) + : Lemma + (requires (n' <= n /\ domain n' x)) + (ensures (interval n x)) + = interval_intro n' x; + interval_weaken n' n x + in + Classical.move_requires f (); + Classical.forall_intro (Classical.move_requires g) + +let rec some_log256 + (x: nat) +: Pure nat + (requires True) + (ensures (fun n -> n > 0 /\ x < pow2 (8 * n))) + (decreases x) += assert_norm (pow2 8 == 256); + if x < 256 + then 1 + else begin + FStar.Math.Lemmas.euclidean_division_definition x 256; + let n' = some_log256 (x / 256) in + FStar.Math.Lemmas.pow2_plus (8 * n') 8; + n' + 1 + end + +let interval_intro_gen + (x: int) +: Pure nat + (requires True) + (ensures (fun n -> n > 0 /\ interval n x)) += if x >= 0 + then begin + let n = some_log256 x in + FStar.Math.Lemmas.pow2_le_compat (8 * (n + 1) - 1) (8 * n); + n + 1 + end + else begin + let n = some_log256 (- x) in + FStar.Math.Lemmas.pow2_le_compat (8 * (n + 1) - 1) (8 * n); + n + 1 + end + +let domain_intro_gen + (x: int) +: Pure nat + (requires True) + (ensures (fun n -> domain n x)) += let n' = interval_intro_gen x in + interval_elim n' x + +(* Correctness: the representation is minimal *) + +let positive_interval_minimal + (n: nat) + (x: int) + (n': nat) +: Lemma + (requires ( + domain n x /\ + x >= 0 /\ + n' > 0 /\ + x < pow2 (8 * n') /\ + U8.v (Seq.index (E.n_to_be n' x) 0) < 128 + )) + (ensures (n <= n')) += Classical.move_requires (inner_positive_interval_elim n) x; + Classical.move_requires (outer_positive_interval_elim n) x; + Classical.move_requires (inner_negative_interval_elim n) x; + Classical.move_requires (outer_negative_interval_elim n) x; + if x = 0 + then assert (n == 1) + else if n <= n' + then () + else begin + interval_intro n x; + FStar.Math.Lemmas.pow2_le_compat (8 * n) (8 * n - 1); + be_to_n_chop_leading_zeros_recip (E.n_to_be n' x) (E.n_to_be n x) + end + +let positive_interval_minimal' + (n: nat) + (x: int) + (n': nat) +: Lemma + (requires ( + domain n x /\ + x >= 0 /\ + n' > 0 /\ + x < pow2 (8 * n' - 1) + )) + (ensures ( + x < pow2 (8 * n') /\ + U8.v (Seq.index (E.n_to_be n' x) 0) < 128 /\ + n <= n' + )) += FStar.Math.Lemmas.pow2_le_compat (8 * n') (8 * n' - 1); + be_to_n_msb (E.n_to_be n' x); + positive_interval_minimal n x n' + +let negative_interval_minimal + (n: nat) + (x: int) + (n': nat) +: Lemma + (requires ( + domain n x /\ + x < 0 /\ + n' > 0 /\ + x + pow2 (8 * n') >= 0 /\ + U8.v (Seq.index (E.n_to_be n' (x + pow2 (8 * n'))) 0) >= 128 + )) + (ensures ( + n <= n' + )) += Classical.move_requires (inner_positive_interval_elim n) x; + Classical.move_requires (outer_positive_interval_elim n) x; + Classical.move_requires (inner_negative_interval_elim n) x; + Classical.move_requires (outer_negative_interval_elim n) x; + if x = -1 + then assert (n == 1) + else if n <= n' + then () + else begin + interval_intro n x; + FStar.Math.Lemmas.pow2_le_compat (8 * n) (8 * n - 1); + be_to_n_chop_leading_ones_recip (E.n_to_be n' (x + pow2 (8 * n'))) (E.n_to_be n (x + pow2 (8 * n))) + end + +let negative_interval_minimal' + (n: nat) + (x: int) + (n': nat) +: Lemma + (requires ( + domain n x /\ + x < 0 /\ + n' > 0 /\ + x + pow2 (8 * n' - 1) >= 0 + )) + (ensures ( + x + pow2 (8 * n') >= 0 /\ + U8.v (Seq.index (E.n_to_be n' (x + pow2 (8 * n'))) 0) >= 128 /\ + n <= n' + )) += FStar.Math.Lemmas.pow2_le_compat (8 * n') (8 * n' - 1); + FStar.Math.Lemmas.pow2_plus (8 * n' - 1) 1; + assert_norm (pow2 1 == 2); + be_to_n_msb (E.n_to_be n' (x + pow2 (8 * n'))); + negative_interval_minimal n x n' + +let domain_unique' + (n1 n2: nat) + (x: int) +: Lemma + (requires (n1 <= n2 /\ domain n1 x /\ domain n2 x)) + (ensures (n1 == n2)) += interval_intro n1 x; + if x < 0 + then negative_interval_minimal' n2 x n1 + else positive_interval_minimal' n2 x n1 + +let domain_unique + (n1 n2: nat) + (x: int) +: Lemma + (requires (domain n1 x /\ domain n2 x)) + (ensures (n1 == n2)) += if n1 <= n2 + then domain_unique' n1 n2 x + else domain_unique' n2 n1 x + +(* Parser *) + +module LP = LowParse.Tot.Combinators + +let valid_unsigned_repr + (b: LP.bytes) +: Tot bool += let n = Seq.length b in + n > 0 && + begin if n = 1 + then true + else + let c0 = Seq.index b 0 in + let c1 = Seq.index b 1 in + not ((c0 = 0uy && c1 `U8.lt` 128uy) || (c0 = 255uy && 127uy `U8.lt` c1)) + end + +let integer_in_domain (n: nat) : Tot Type0 = (i: int { domain n i }) + +let rec be_to_n_zero + (n: nat) +: Lemma + (E.be_to_n (Seq.create n 0uy) == 0) += let s = Seq.create n 0uy in + E.reveal_be_to_n s; + assert (Seq.length s == n); + if n = 0 + then () + else begin + assert (Seq.slice s 0 (Seq.length s - 1) `Seq.equal` Seq.create (n - 1) 0uy); + be_to_n_zero (n - 1) + end + +let be_to_n_singleton + (s: LP.bytes) +: Lemma + (requires ( + Seq.length s == 1 + )) + (ensures ( + Seq.length s == 1 /\ + E.be_to_n s == U8.v (Seq.index s 0) + )) += E.reveal_be_to_n s; + E.reveal_be_to_n (Seq.slice s 0 (Seq.length s - 1)) + +let mk_integer_aux + (b: LP.bytes) +: Tot (option (integer_in_domain (Seq.length b))) += if valid_unsigned_repr b + then Some begin + let u = E.be_to_n b in + let c0 = Seq.index b 0 in + if c0 = 0uy + then + if Seq.length b = 1 + then begin + be_to_n_singleton b; + u + end + else begin + inner_positive_interval_intro b; + u + end + else if c0 `U8.lt` 128uy + then begin + outer_positive_interval_intro b; + u + end + else + let s = u - pow2 (8 * Seq.length b) in // cast from unsigned to signed for negative numbers + if c0 `U8.lt` 255uy + then begin + outer_negative_interval_intro b; + s + end + else if Seq.length b = 1 + then begin + be_to_n_singleton b; + assert_norm (pow2 8 == 256); + assert (s == -1); + s + end + else begin + inner_negative_interval_intro b; + s + end + end else None + +let mk_integer'_eq' + (b: LP.bytes) +: Lemma + (requires (valid_unsigned_repr b)) + (ensures ( + let u = E.be_to_n b in + let s = Some?.v (mk_integer_aux b) in + (s >= 0 <==> s == u) /\ + (s < 0 <==> s == u - pow2 (8 * Seq.length b)) + )) += let n = Seq.length b in + let u = E.be_to_n b in + let s = Some?.v (mk_integer_aux b) in + FStar.Math.Lemmas.pow2_plus (8 * n - 1) 1; + Classical.move_requires inner_positive_interval_intro b; + Classical.move_requires outer_positive_interval_intro b; + Classical.move_requires inner_negative_interval_intro b; + Classical.move_requires outer_negative_interval_intro b; + Classical.move_requires (inner_positive_interval_elim n) s; + Classical.move_requires (outer_positive_interval_elim n) s; + Classical.move_requires (inner_negative_interval_elim n) s; + Classical.move_requires (outer_negative_interval_elim n) s; + Classical.move_requires be_to_n_singleton b + +let mk_integer'_eq + (b: LP.bytes) +: Lemma + (requires (valid_unsigned_repr b)) + (ensures ( + let u = E.be_to_n b in + let s = Some?.v (mk_integer_aux b) in + (s >= 0 <==> u < pow2 (8 * Seq.length b - 1)) /\ + (s >= 0 <==> s == u) /\ + (s < 0 <==> s == u - pow2 (8 * Seq.length b)) + )) += let n = Seq.length b in + let u = E.be_to_n b in + let s = Some?.v (mk_integer_aux b) in + mk_integer'_eq' b; + interval_intro n s; + FStar.Math.Lemmas.pow2_plus (8 * n - 1) 1 + +let mk_integer' + (b: LP.bytes) +: Pure int + (requires (valid_unsigned_repr b)) + (ensures (fun _ -> True)) += let u = E.be_to_n b in + if u < pow2 (8 * Seq.length b - 1) + then u + else u - pow2 (8 * Seq.length b) + +let mk_integer + (sz: nat) + (b: LP.bytes { Seq.length b == sz }) +: Tot (option (integer_in_domain sz)) += if valid_unsigned_repr b + then Some ( + mk_integer'_eq b; + mk_integer' b + ) + else None + +let mk_integer_inj_1 + (sz: nat) + (b1 b2: Seq.lseq LP.byte sz) +: Lemma + (requires (LP.make_constant_size_parser_precond_precond sz (integer_in_domain sz) (mk_integer sz) b1 b2)) + (ensures (Seq.equal b1 b2)) += mk_integer'_eq b1; + mk_integer'_eq b2; + E.be_to_n_inj b1 b2 + +let mk_integer_inj_2 + (sz: nat) + (b1 b2: Seq.lseq LP.byte sz) +: Lemma + (LP.make_constant_size_parser_precond_precond sz (integer_in_domain sz) (mk_integer sz) b1 b2 ==> Seq.equal b1 b2) += Classical.move_requires (mk_integer_inj_1 sz b1) b2 + +let mk_integer_inj + (sz: nat) +: Lemma + (LP.make_constant_size_parser_precond sz (integer_in_domain sz) (mk_integer sz)) += Classical.forall_intro_2 (mk_integer_inj_2 sz) + +let parse_integer_of_size + (sz: nat) +: Tot (LP.parser (LP.constant_size_parser_kind sz) (integer_in_domain sz)) += mk_integer_inj sz; + LP.make_constant_size_parser sz (integer_in_domain sz) (mk_integer sz) + +let bounded_integer_tag + (bound: nat) +: Tot Type0 += (sz: nat { sz <= bound }) + +let integer_in_interval + (bound: nat) +: Tot Type0 += (x: int { interval bound x }) + +let tag_of_bounded_integer_payload + (bound: nat) + (x: integer_in_interval bound) +: Tot (bounded_integer_tag bound) += interval_elim bound x + +let synth_bounded_integer_payload + (bound: nat) + (tag: bounded_integer_tag bound) + (x: integer_in_domain tag) +: Tot (LP.refine_with_tag (tag_of_bounded_integer_payload bound) tag) += interval_intro tag x; + interval_weaken tag bound x; + domain_unique tag (tag_of_bounded_integer_payload bound x) x; + x + +let parse_bounded_integer_payload + (bound: nat) + (tag: bounded_integer_tag bound) +: Tot (LP.parser (LP.strong_parser_kind 0 bound None) (LP.refine_with_tag (tag_of_bounded_integer_payload bound) tag)) += LP.weaken (LP.strong_parser_kind 0 bound None) (parse_integer_of_size tag) + `LP.parse_synth` synth_bounded_integer_payload bound tag + +let parse_bounded_integer + (bound: nat) + (#kt: LP.parser_kind) + (p: LP.parser kt (bounded_integer_tag bound) { + kt.LP.parser_kind_subkind == Some LP.ParserStrong + }) +: Tot (LP.parser (kt `LP.and_then_kind` LP.strong_parser_kind 0 bound None) (integer_in_interval bound)) += LP.parse_tagged_union + p + (tag_of_bounded_integer_payload bound) + (parse_bounded_integer_payload bound) + +let mk_integer'_inj' + (b1 b2: LP.bytes) +: Lemma + (requires (valid_unsigned_repr b1 /\ valid_unsigned_repr b2 /\ mk_integer' b1 == mk_integer' b2)) + (ensures (b1 == b2)) += mk_integer'_eq' b1; + mk_integer'_eq' b2; + let n = mk_integer' b1 in + domain_unique (Seq.length b1) (Seq.length b2) n; + mk_integer_inj_1 (Seq.length b1) b1 b2 + +let mk_integer'_inj + (b1 b2: LP.bytes) +: Lemma + ((valid_unsigned_repr b1 /\ valid_unsigned_repr b2 /\ mk_integer' b1 == mk_integer' b2) ==> (b1 == b2)) += Classical.move_requires (mk_integer'_inj' b1) b2 + +let parse_untagged_bounded_integer_kind (bound: pos) : LP.parser_kind = { + LP.parser_kind_low = 1; + LP.parser_kind_high = Some bound; + LP.parser_kind_subkind = None; + LP.parser_kind_metadata = None; +} + +let parse_untagged_bounded_integer' + (bound: pos) + (x: LP.bytes) +: Tot (option (integer_in_interval bound & LP.consumed_length x)) += let l = Seq.length x in + if l > bound + then None + else if valid_unsigned_repr x + then begin + mk_integer'_eq' x; + let l = Seq.length x in + let r = mk_integer' x in + interval_intro l r; + interval_weaken l bound r; + Some (r, l) + end else + None + +let parse_untagged_bounded_integer + (bound: pos) +: Tot (LP.parser (parse_untagged_bounded_integer_kind bound) (integer_in_interval bound)) += Classical.forall_intro_2 mk_integer'_inj; + LP.parser_kind_prop_equiv (parse_untagged_bounded_integer_kind bound) (parse_untagged_bounded_integer' bound); + parse_untagged_bounded_integer' bound + +let tag_of_integer_payload + (x: int) +: Tot nat += domain_intro_gen x + +let synth_integer_payload + (tag: nat) + (x: integer_in_domain tag) +: Tot (LP.refine_with_tag tag_of_integer_payload tag) += domain_unique tag (tag_of_integer_payload x) x; + x + +inline_for_extraction +//noextract +let parse_integer_payload_kind : LP.parser_kind = + let open LP in + { + parser_kind_low = 0; + parser_kind_high = None; + parser_kind_subkind = Some ParserStrong; + parser_kind_metadata = None; + } + +let parse_integer_payload + (tag: nat) +: Tot (LP.parser parse_integer_payload_kind (LP.refine_with_tag (tag_of_integer_payload) tag)) += LP.weaken parse_integer_payload_kind (parse_integer_of_size tag) + `LP.parse_synth` synth_integer_payload tag + +let parse_integer + (#kt: LP.parser_kind) + (p: LP.parser kt nat { + kt.LP.parser_kind_subkind == Some LP.ParserStrong + }) +: Tot (LP.parser (kt `LP.and_then_kind` parse_integer_payload_kind) int) += LP.parse_tagged_union + p + (tag_of_integer_payload) + (parse_integer_payload) + +(* Serializer *) + +let mk_bytes + (n: nat) + (x: int) +: Pure (Seq.lseq LP.byte n) + (requires (domain n x)) + (ensures (fun s -> + valid_unsigned_repr s /\ + mk_integer' s == x + )) += assert_norm (pow2 8 == 256); + assert_norm (pow2 7 == 128); + if (n = 1 && (x = 0 || x = -1)) + then + if x = 0 + then begin + let s = Seq.create 1 0uy in + be_to_n_singleton s; + s + end else begin + let s = Seq.create 1 255uy in + be_to_n_singleton s; + s + end + else begin + Classical.move_requires (inner_positive_interval_elim n) x; + Classical.move_requires (inner_negative_interval_elim n) x; + Classical.move_requires (outer_positive_interval_elim n) x; + Classical.move_requires (outer_negative_interval_elim n) x; + if x >= 0 + then begin + let s = E.n_to_be n x in + mk_integer'_eq s; + s + end else begin + let s = E.n_to_be n (x + pow2 (8 * n)) in + mk_integer'_eq s; + s + end + end + +let serialize_integer_of_size + (sz: nat) +: Tot (LP.serializer (parse_integer_of_size sz)) += fun (x: integer_in_domain sz) -> mk_bytes sz x + +let synth_bounded_integer_payload_recip + (bound: nat) + (tag: bounded_integer_tag bound) + (x: LP.refine_with_tag (tag_of_bounded_integer_payload bound) tag) +: Tot (integer_in_domain tag) += x + +let serialize_bounded_integer_payload + (bound: nat) + (tag: bounded_integer_tag bound) +: Tot (LP.serializer (parse_bounded_integer_payload bound tag)) += LP.serialize_synth + _ + (synth_bounded_integer_payload bound tag) + (LP.serialize_weaken _ (serialize_integer_of_size tag)) + (synth_bounded_integer_payload_recip bound tag) + () + +let serialize_bounded_integer + (bound: nat) + (#kt: LP.parser_kind) + (#p: LP.parser kt (bounded_integer_tag bound)) + (s: LP.serializer p { + kt.LP.parser_kind_subkind == Some LP.ParserStrong + }) +: Tot (LP.serializer (parse_bounded_integer bound p)) += LP.serialize_tagged_union + s + (tag_of_bounded_integer_payload bound) + (serialize_bounded_integer_payload bound) + +let serialize_untagged_bounded_integer + (bound: pos) +: Tot (LP.serializer (parse_untagged_bounded_integer bound)) += (fun (x: integer_in_interval bound) -> + let d = interval_elim bound x in + mk_bytes d x <: LP.bytes + ) + +let synth_integer_payload_recip + (tag: nat) + (x: LP.refine_with_tag (tag_of_integer_payload) tag) +: Tot (integer_in_domain tag) += x + +let serialize_integer_payload + (tag: nat) +: Tot (LP.serializer (parse_integer_payload tag)) += LP.serialize_synth + _ + (synth_integer_payload tag) + (LP.serialize_weaken _ (serialize_integer_of_size tag)) + (synth_integer_payload_recip tag) + () + +let serialize_integer + (#kt: LP.parser_kind) + (#p: LP.parser kt nat) + (s: LP.serializer p { + kt.LP.parser_kind_subkind == Some LP.ParserStrong + }) +: Tot (LP.serializer (parse_integer p)) += LP.serialize_tagged_union + s + (tag_of_integer_payload) + (serialize_integer_payload) + +#pop-options + +(* Implementations with machine integer types *) + +inline_for_extraction +// noextract +noeq +type int_t (nbytes: pos) (i_t: Type0) = { + v: (i_t -> Tot (integer_in_interval nbytes)); + int_to_t: (integer_in_interval nbytes -> Tot i_t); + v_int_to_t: ((x: integer_in_interval nbytes) -> Lemma (v (int_to_t x) == x)); + int_to_t_v: ((x: i_t) -> Lemma (int_to_t (v x) == x)); +} + +let parse_untagged_signed_integer + (#nbytes: pos) + (#i_t: Type0) + (i: int_t nbytes i_t) +: Tot (LP.parser (parse_untagged_bounded_integer_kind nbytes) i_t) += Classical.forall_intro i.v_int_to_t; + parse_untagged_bounded_integer nbytes `LP.parse_synth` (fun x -> i.int_to_t x) + +module I32 = FStar.Int32 + +inline_for_extraction +// noextract +let int32: int_t 4 I32.t = { + v = (fun x -> I32.v x); + int_to_t = (fun x -> I32.int_to_t x); + v_int_to_t = (fun _ -> ()); + int_to_t_v = (fun _ -> ()); +} + +let parse_untagged_int32 +: LP.parser (parse_untagged_bounded_integer_kind 4) I32.t += parse_untagged_signed_integer int32 diff --git a/src/ASN1/ASN1.Spec.Content.NULL.fst b/src/ASN1/ASN1.Spec.Content.NULL.fst new file mode 100644 index 000000000..b241188a6 --- /dev/null +++ b/src/ASN1/ASN1.Spec.Content.NULL.fst @@ -0,0 +1,8 @@ +module ASN1.Spec.Content.NULL + +open ASN1.Base + +open LowParse.Tot.Combinators + +let parse_asn1_null : asn1_weak_parser asn1_null_t += weaken _ parse_empty diff --git a/src/ASN1/ASN1.Spec.Content.OCTETSTRING.fst b/src/ASN1/ASN1.Spec.Content.OCTETSTRING.fst new file mode 100644 index 000000000..845596957 --- /dev/null +++ b/src/ASN1/ASN1.Spec.Content.OCTETSTRING.fst @@ -0,0 +1,8 @@ +module ASN1.Spec.Content.OCTETSTRING + +open ASN1.Base + +include LowParse.Tot.Bytes + +let parse_asn1_octetstring : asn1_weak_parser asn1_octetstring_t += weaken _ parse_all_bytes diff --git a/src/ASN1/ASN1.Spec.Content.OIDU32.fst b/src/ASN1/ASN1.Spec.Content.OIDU32.fst new file mode 100644 index 000000000..d5a3522e4 --- /dev/null +++ b/src/ASN1/ASN1.Spec.Content.OIDU32.fst @@ -0,0 +1,76 @@ +module ASN1.Spec.Content.OIDU32 + +open ASN1.Base +open ASN1.Spec.IdentifierU32 + +open LowParse.Tot.Base +open LowParse.Tot.Combinators +open LowParse.Tot.Int +open LowParse.Tot.List + +open FStar.Mul + +module U8 = FStar.UInt8 +module U32 = FStar.UInt32 +module Cast = FStar.Int.Cast + +let byte = U8.t + +let parse_asn1_OIDU32_tail = parse_list parse_asn1_identifier_U32_alt + +let decode_OIDU32_head (buf : byte) +: (p : (U32.t & U32.t) {asn1_OID_wf' (fst p) (snd p)}) += if (U8.v buf < 40) then + (0ul, Cast.uint8_to_uint32 buf) + else + if (U8.v buf < 80) then + (1ul, Cast.uint8_to_uint32 (U8.sub buf 40uy)) + else + (2ul, Cast.uint8_to_uint32 (U8.sub buf 80uy)) + +let decode_OIDU32_head_inj (buf1 buf2 : byte) : +Lemma (requires (decode_OIDU32_head buf1 = decode_OIDU32_head buf2)) + (ensures (buf1 = buf2)) += if (U8.v buf1 < 40) then + if (U8.v buf2 < 40) then + _ + else if (U8.v buf2 < 80) then + _ + else + _ + else if (U8.v buf1 < 80) then + if (U8.v buf2 < 40) then + _ + else if (U8.v buf2 < 80) then + _ + else + _ + else + if (U8.v buf2 < 40) then + _ + else if (U8.v buf2 < 80) then + _ + else + _ + +let synth_OIDU32 (btl : byte & list U32.t) +: asn1_oid_t += let (buf, tl) = btl in + let p = decode_OIDU32_head buf in + (fst p) :: (snd p) :: tl + +let lemma_synth_OIDU32_inj () : + Lemma (ensures (synth_injective synth_OIDU32)) += synth_injective_intro' (synth_OIDU32) (fun x1 x2 -> + let (b1, tl1) = x1 in + let (b2, tl2) = x2 in + assert (tl1 = tl2); + let p1 = decode_OIDU32_head b1 in + let p2 = decode_OIDU32_head b2 in + assert (p1 = p2); + let _ = decode_OIDU32_head_inj b1 b2 in + _) + +let parse_asn1_OIDU32 : asn1_weak_parser asn1_oid_t = + let _ = lemma_synth_OIDU32_inj () in + weaken asn1_weak_parser_kind ((parse_u8 `nondep_then` parse_asn1_OIDU32_tail) `parse_synth` synth_OIDU32) diff --git a/src/ASN1/ASN1.Spec.Content.PRINTABLESTRING.fst b/src/ASN1/ASN1.Spec.Content.PRINTABLESTRING.fst new file mode 100644 index 000000000..a9733b86a --- /dev/null +++ b/src/ASN1/ASN1.Spec.Content.PRINTABLESTRING.fst @@ -0,0 +1,15 @@ +module ASN1.Spec.Content.PRINTABLESTRING + +open ASN1.Base + +open LowParse.Tot.Combinators +open LowParse.Tot.Int +open LowParse.Tot.List + +module U8 = FStar.UInt8 + +let parse_printable_char = + parse_u8 `parse_filter` is_printable_char + +let parse_asn1_printablestring : asn1_weak_parser asn1_printablestring_t = + weaken _ (parse_list parse_printable_char) diff --git a/src/ASN1/ASN1.Spec.Content.TIME.fst b/src/ASN1/ASN1.Spec.Content.TIME.fst new file mode 100644 index 000000000..72a53d1c6 --- /dev/null +++ b/src/ASN1/ASN1.Spec.Content.TIME.fst @@ -0,0 +1,16 @@ +module ASN1.Spec.Content.TIME + +open LowParse.Tot.Combinators +open LowParse.Tot.Bytes + +open ASN1.Base +open ASN1.Spec.Time + +(* Warning: Temporary Solutions *) + +let parse_asn1_UTCTIME : asn1_weak_parser asn1_utctime_t += weaken _ (parse_all_bytes `parse_filter` is_valid_ASN1UTCTIME) + +let parse_asn1_GENERALIZEDTIME : asn1_weak_parser asn1_generalizedtime_t += weaken _ (parse_all_bytes `parse_filter` is_valid_ASN1GENERALIZEDTIME) + diff --git a/src/ASN1/ASN1.Spec.Content.UTF8STRING.fst b/src/ASN1/ASN1.Spec.Content.UTF8STRING.fst new file mode 100644 index 000000000..7cd8ff680 --- /dev/null +++ b/src/ASN1/ASN1.Spec.Content.UTF8STRING.fst @@ -0,0 +1,283 @@ +module ASN1.Spec.Content.UTF8STRING +open ASN1.Base +open ASN1.Spec.Automata + +open LowParse.Tot.Combinators +open LowParse.Tot.Int +open LowParse.Tot.List + +module U8 = FStar.UInt8 +module U32 = FStar.UInt32 + +module Cast = FStar.Int.Cast + +open FStar.Mul + +let _ = assert_norm (pow2 16 == 65536) +let _ = assert_norm (pow2 9 == 512) +let _ = assert_norm (pow2 3 == 8) +let _ = assert_norm (pow2 6 == 64) +let _ = assert_norm (pow2 12 == 4096) +let _ = assert_norm (pow2 18 == 262144) +let _ = assert_norm (pow2 11 == 2048) +let _ = assert_norm (pow2 21 == 2097152) +let _ = assert_norm (pow2 15 == 32768) +let _ = assert_norm (pow2 32 == 4294967296) +let _ = assert_norm (pow2 7 == 128) +let _ = assert_norm (pow2 20 == 1048576) + +type utf8_cp_s = +| Init | S3 | S3' | S2 | S2' | S1 + +let ch_t = U8.t + +let out_range_common (ch : ch_t) = + U8.v ch < 128 || 128 + 64 <= U8.v ch + +let out_range_s2' (ch : ch_t) = + U8.v ch < 128 + 32 || 128 + 64 <= U8.v ch + +let out_range_s3' (ch : ch_t) = + U8.v ch < 128 + 16 || 128 + 64 <= U8.v ch + +let out_range_init (ch : ch_t) = + (128 <= U8.v ch && U8.v ch <= 128 + 64 + 1) || 248 <= U8.v ch + +let fail_check (s : utf8_cp_s) : ch_t -> bool = + match s with + | S1 | S2 | S3 -> out_range_common + | S2' -> out_range_s2' + | S3' -> out_range_s3' + | Init -> out_range_init + +let termination_check (s : utf8_cp_s) = + match s with + | S1 -> fun _ -> true + | S2 | S3 | S2' | S3' -> fun _ -> false + | Init -> (fun (ch : ch_t {fail_check s ch = false}) -> U8.v ch < 128) + +let next_state (s : utf8_cp_s) (ch : ch_t {fail_check s ch = false /\ termination_check s ch = false}) : utf8_cp_s = + match s with + | S2 | S2' -> S1 + | S3 | S3' -> S2 + | Init -> if (U8.v ch < 128 + 64 + 32) then + S1 + else if (U8.v ch = 128 + 64 + 32) then + S2' + else if (U8.v ch < 128 + 64 + 32 + 16) then + S2 + else if (U8.v ch = 128 + 64 + 32 + 16) then + S3' + else + S3 + +let utf8_cp_cp : automata_control_param = { + control_t = utf8_cp_s; + ch_t = ch_t; + fail_check = fail_check; + termination_check = termination_check; + next_state = next_state +} + +let ret_t = utf8_cp_t + +let partial_t = U32.t + +let pre_t (s : utf8_cp_s) : partial_t -> Type0 = + match s with + | S1 -> (fun data -> 1 < U32.v data /\ U32.v data < pow2 15) + | S2 -> (fun data -> 0 < U32.v data /\ U32.v data < pow2 9) + | S3 -> (fun data -> 0 < U32.v data /\ U32.v data < pow2 3) + | S2' | S3' | Init -> (fun data -> U32.v data = 0) + +let post_t (s : utf8_cp_s) (data : partial_t {pre_t s data}) : ret_t -> Type0 = + match s with + | S1 -> (fun ret -> U32.v data * (pow2 6) <= U32.v ret /\ U32.v ret < (U32.v data + 1) * (pow2 6)) + | S2 -> (fun ret -> U32.v data * (pow2 12) <= U32.v ret /\ U32.v ret < (U32.v data + 1) * (pow2 12)) + | S3 -> (fun ret -> U32.v data * (pow2 18) <= U32.v ret /\ U32.v ret < (U32.v data + 1) * (pow2 18)) + | S2' -> (fun ret -> pow2 11 <= U32.v ret /\ U32.v ret < pow2 12) + | S3' -> (fun ret -> pow2 16 <= U32.v ret /\ U32.v ret < pow2 18) + | Init -> (fun ret -> U32.v ret < pow2 21) + +let update_term (state : utf8_cp_s) : (data : partial_t {pre_t state data}) -> + (ch : ch_t {fail_check state ch = false /\ termination_check state ch = true}) -> + (ret : ret_t {post_t state data ret}) = + match state with + | S2 | S3 | S2' | S3' -> (fun _ _ -> false_elim ()) + | S1 -> (fun data ch -> + assert (U32.v data < pow2 15); + let _ = Math.Lemmas.pow2_plus 15 6 in + let _ = Math.Lemmas.pow2_lt_compat 32 21 in + let b = U8.sub ch 128uy in (U32.add (U32.mul data 64ul) (Cast.uint8_to_uint32 b))) + | Init -> (fun _ ch -> + let _ = Math.Lemmas.pow2_lt_compat 21 7 in + Cast.uint8_to_uint32 ch) + +let update_next (state : utf8_cp_s) : (data : partial_t {pre_t state data}) -> + (ch : ch_t {fail_check state ch = false /\ termination_check state ch = false}) -> + (data' : partial_t {pre_t (next_state state ch) data'}) = + match state with + | S1 -> (fun _ _ -> false_elim ()) + | S2 -> (fun data ch -> + let _ = Math.Lemmas.pow2_plus 9 6 in + let b = U8.sub ch 128uy in (U32.add (U32.mul data 64ul) (Cast.uint8_to_uint32 b))) + | S3 -> (fun data ch -> + let _ = Math.Lemmas.pow2_plus 3 6 in + let b = U8.sub ch 128uy in (U32.add (U32.mul data 64ul) (Cast.uint8_to_uint32 b))) + | S2' -> (fun _ ch -> + let b = U8.sub ch 128uy in + assert (32 <= U8.v b /\ U8.v b < 64); + let _ = Math.Lemmas.pow2_lt_compat 15 6 in + Cast.uint8_to_uint32 b) + | S3' -> (fun _ ch -> + let b = U8.sub ch 128uy in Cast.uint8_to_uint32 b) + | Init -> (fun _ ch -> + if (U8.v ch < 128 + 64 + 32) then + let _ = Math.Lemmas.pow2_lt_compat 15 5 in + Cast.uint8_to_uint32 (U8.sub ch 192uy) + else if (U8.v ch = 128 + 64 + 32) then + 0ul + else if (U8.v ch < 128 + 64 + 32 + 16) then + Cast.uint8_to_uint32 (U8.sub ch 224uy) + else if (U8.v ch = 128 + 64 + 32 + 16) then + 0ul + else + Cast.uint8_to_uint32 (U8.sub ch 240uy)) + +let lemma_mul_le_le_le (a b c d : nat) +: Lemma (requires (a <= b /\ b * c <= d)) + (ensures (a * c <= d)) += _ + +let lemma_mul_lt_lt_lt (a b c d : nat) +: Lemma (requires (a < b /\ c < (a + 1) * d)) + (ensures (c < b * d)) += _ + +#push-options "--z3rlimit 128 --fuel 8 --ifuel 0" + +let lemma_cast_ret + (state : utf8_cp_s) + (data : partial_t {pre_t state data}) + (ch : ch_t {fail_check state ch = false /\ termination_check state ch = false}) + (ret : ret_t) +: Lemma (requires (post_t (next_state state ch) (update_next state data ch) ret)) + (ensures (post_t state data ret)) += _ + +#pop-options + +let utf8_cp_dp : automata_data_param utf8_cp_cp = { + ret_t = ret_t; + partial_t = partial_t; + pre_t = pre_t; + post_t = post_t; + update_term = update_term; + update_next = update_next; + lemma_cast_ret = lemma_cast_ret +} + +let utf8_cp_bp : automata_bare_parser_param utf8_cp_cp = { + ch_t_bare_parser = parse_u8; + ch_t_bare_parser_valid = + fun _ -> parser_kind_prop_equiv parse_u8_kind parse_u8 +} + +#push-options "--z3rlimit 64 --fuel 4 --ifuel 1" // --split_queries" + +let lemma_update_term_inj2 + (state : utf8_cp_s) + (data1 : partial_t {pre_t state data1}) + (data2 : partial_t {pre_t state data2}) + (ch1 : ch_t {fail_check state ch1 = false /\ termination_check state ch1 = true}) + (ch2 : ch_t {fail_check state ch2 = false /\ termination_check state ch2 = true}) +: Lemma (requires (update_term state data1 ch1 = update_term state data2 ch2)) + (ensures (data1 = data2 /\ ch1 = ch2)) += _ + +let lemma_update_term_next_non_intersect + (state : utf8_cp_s) + (data1 : partial_t {pre_t state data1}) + (data2 : partial_t {pre_t state data2}) + (ch1 : ch_t {fail_check state ch1 = false /\ termination_check state ch1 = true}) + (ch2 : ch_t {fail_check state ch2 = false /\ termination_check state ch2 = false}) + (ret1 : ret_t {post_t state data1 ret1}) + (ret2 : ret_t {post_t (next_state state ch2) (update_next state data2 ch2) ret2}) +: Lemma (requires (ret1 = update_term state data1 ch1 /\ ret1 = ret2)) + (ensures False) += _ + +let control_s_sym_ord + (state : utf8_cp_s) += match state with + | S1 -> 0 + | S2 -> 1 + | S2' -> 2 + | S3 -> 3 + | S3' -> 4 + | Init -> 5 + +#restart-solver +let lemma_update_next_non_intersect_init_sym + (data1 : partial_t {pre_t Init data1}) + (data2 : partial_t {pre_t Init data2}) + (ch1 : ch_t {fail_check Init ch1 = false /\ termination_check Init ch1 = false}) + (ch2 : ch_t {fail_check Init ch2 = false /\ termination_check Init ch2 = false}) + (ret1 : ret_t {post_t (next_state Init ch1) (update_next Init data1 ch1) ret1}) + (ret2 : ret_t {post_t (next_state Init ch2) (update_next Init data2 ch2) ret2}) +: Lemma (requires (control_s_sym_ord (next_state Init ch1) < control_s_sym_ord (next_state Init ch2) /\ ret1 = ret2)) + (ensures False) += let state'1 = next_state Init ch1 in + let state'2 = next_state Init ch2 in + match state'1 with + | S1 | S2 | S2' -> _ + | S3 -> match state'2 with + | S3' -> assert (pow2 18 <= U32.v ret1); + assert (U32.v ret2 < pow2 18) + +let lemma_update_next_non_intersect + (state : utf8_cp_s) + (data1 : partial_t {pre_t state data1}) + (data2 : partial_t {pre_t state data2}) + (ch1 : ch_t {fail_check state ch1 = false /\ termination_check state ch1 = false}) + (ch2 : ch_t {fail_check state ch2 = false /\ termination_check state ch2 = false}) + (ret1 : ret_t {post_t (next_state state ch1) (update_next state data1 ch1) ret1}) + (ret2 : ret_t {post_t (next_state state ch2) (update_next state data2 ch2) ret2}) +: Lemma (requires (next_state state ch1 <> next_state state ch2 /\ ret1 = ret2)) + (ensures False) += match state with + | S2 | S2' | S3 | S3' | S1 -> false_elim () + | Init -> let state'1 = next_state state ch1 in + let state'2 = next_state state ch2 in + if (control_s_sym_ord state'1 < control_s_sym_ord state'2) then + lemma_update_next_non_intersect_init_sym data1 data2 ch1 ch2 ret1 ret2 + else + lemma_update_next_non_intersect_init_sym data2 data1 ch2 ch1 ret2 ret1 + +let lemma_update_next_inj2 + (state : utf8_cp_s) + (data1 : partial_t {pre_t state data1}) + (data2 : partial_t {pre_t state data2}) + (ch1 : ch_t {fail_check state ch1 = false /\ termination_check state ch1 = false}) + (ch2 : ch_t {fail_check state ch2 = false /\ termination_check state ch2 = false}) +: Lemma (requires next_state state ch1 = next_state state ch2 /\ update_next state data1 ch1 = update_next state data2 ch2) + (ensures data1 = data2 /\ ch1 = ch2) += _ + +#pop-options + +let utf8_cp_pp : automata_parser_param utf8_cp_cp utf8_cp_dp utf8_cp_bp = { + ch_t_parser_valid = (fun _ -> + let _ = parser_kind_prop_equiv parse_u8_kind parse_u8 in + let _ = parser_kind_prop_equiv automata_default_parser_kind parse_u8 in + _); + lemma_update_term_inj2 = lemma_update_term_inj2; + lemma_update_term_next_non_intersect = lemma_update_term_next_non_intersect; + lemma_update_next_non_intersect = lemma_update_next_non_intersect; + lemma_update_next_inj2 = lemma_update_next_inj2 +} + +let utf8_cp_parser = automata_parser utf8_cp_cp utf8_cp_dp utf8_cp_bp utf8_cp_pp Init 0ul + +let parse_asn1_utf8string : asn1_weak_parser asn1_utf8string_t += weaken _ (parse_list utf8_cp_parser) diff --git a/src/ASN1/ASN1.Spec.ILC.fst b/src/ASN1/ASN1.Spec.ILC.fst new file mode 100755 index 000000000..97687d1b3 --- /dev/null +++ b/src/ASN1/ASN1.Spec.ILC.fst @@ -0,0 +1,129 @@ +module ASN1.Spec.ILC + +open ASN1.Base + +open ASN1.Spec.IdentifierU32 +open ASN1.Spec.LengthU32 + +open ASN1.Spec.Content.OCTETSTRING + +open LowParse.Tot.Base +open LowParse.Tot.Combinators +open LowParse.Tot.VLGen + +module U32 = FStar.UInt32 +module Seq = FStar.Seq + +let parse_asn1_LC + (#ack : asn1_content_k) + (p : asn1_weak_parser (asn1_content_t ack)) +: asn1_strong_parser (asn1_content_t ack) += weaken _ (parse_vlgen_weak 0 4294967295 parse_asn1_length_u32_t p) + +let parse_asn1_ILC + (id : asn1_id_t) + (#ack : asn1_content_k) + (p : asn1_weak_parser (asn1_content_t ack)) +: asn1_strong_parser (asn1_content_t ack) += let p' = + parse_asn1_identifier_U32 + `parse_filter` + (fun id' -> id' = id) + `nondep_then` + parse_asn1_LC p + `parse_synth` + (snd) in + weaken asn1_strong_parser_kind p' + +let parse_asn1_ILC_twin + (id : asn1_id_t) + (#ack : asn1_content_k) + (p : asn1_weak_parser (asn1_content_t ack)) + (id' : asn1_id_t) +: asn1_strong_parser (asn1_content_t ack) += if (id = id') then + parse_asn1_LC p + else + fail_parser _ _ + +let parser_asn1_ILC_twin_case_injective + (id : asn1_id_t) + (#ack : asn1_content_k) + (p : asn1_weak_parser (asn1_content_t ack)) +: Lemma + (and_then_cases_injective (parse_asn1_ILC_twin id p)) += and_then_cases_injective_intro (parse_asn1_ILC_twin id p) (fun id1 id2 _ _ -> assert (id1 = id) ; assert (id2 = id)) + +let parse_asn1_anyILC +: asn1_strong_parser (asn1_t (ASN1_ANY_ILC)) += parse_asn1_identifier_U32 `nondep_then` (parse_asn1_LC #(ASN1_TERMINAL ASN1_OCTETSTRING) parse_asn1_octetstring) + +let parse_asn1_anyILC_twin +: asn1_id_t -> asn1_strong_parser (asn1_t (ASN1_ANY_ILC)) += fun id -> (parse_ret id) `nondep_then` (parse_asn1_LC #(ASN1_TERMINAL ASN1_OCTETSTRING) parse_asn1_octetstring) + +let parse_asn1_anyILC_twin_and_then_cases_injective () +: Lemma (and_then_cases_injective parse_asn1_anyILC_twin) += let p = parse_asn1_anyILC_twin in + and_then_cases_injective_intro p (fun x1 x2 b1 b2 -> + let p1 = parse_ret x1 in + let p2 = parse_ret x2 in + let p' = (parse_asn1_LC #(ASN1_TERMINAL ASN1_OCTETSTRING) parse_asn1_octetstring) in + nondep_then_eq p1 p' b1; + nondep_then_eq p2 p' b2 + ) + +(* + +let parse_asn1_LC_kind (k: parser_kind) = parse_bounded_vlgen_kind (parse_asn1_length_u32_t_kind) 0 4294967295 k + +let parse_asn1_LC + (#ack : asn1_content_k) + (#k : parser_kind) + (#p : parser k (asn1_content_t ack)) + (s : serializer p { parse_vlgen_precond 0 4294967295 k }) +: parser (parse_asn1_LC_kind k) (asn1_content_t ack) += parse_bounded_vlgen 0 4294967295 parse_asn1_length_u32_t s + `parse_synth` + (synth_vlgen 0 4294967295 s) + +let parse_asn1_ILC_kind (k : parser_kind) = and_then_kind parse_asn1_identifier_u21_kind (parse_asn1_LC_kind k) + +let parse_asn1_ILC + (id : asn1_id_t) + (#ack : asn1_content_k) + (#k : parser_kind) + (#p : parser k (asn1_content_t ack)) + (s : serializer p { parse_vlgen_precond 0 4294967295 k }) +: parser (parse_asn1_ILC_kind k) (asn1_content_t ack) += parse_asn1_identifier_u21 + `parse_filter` + (fun id' -> id' = id) + `nondep_then` + parse_asn1_LC s + `parse_synth` + (snd) + +let parse_asn1_ILC_twin + (id : asn1_id_t) + (#ack : asn1_content_k) + (#k : parser_kind) + (#p : parser k (asn1_content_t ack)) + (s : serializer p { parse_vlgen_precond 0 4294967295 k }) + (id' : asn1_id_t) +: parser (parse_asn1_LC_kind k) (asn1_content_t ack) += if (id = id') then + parse_asn1_LC s + else + fail_parser _ _ + +let parser_asn1_ILC_twin_case_injective + (id : asn1_id_t) + (#ack : asn1_content_k) + (#k : parser_kind) + (#p : parser k (asn1_content_t ack)) + (s : serializer p { parse_vlgen_precond 0 4294967295 k }) +: Lemma + (and_then_cases_injective (parse_asn1_ILC_twin id s)) += and_then_cases_injective_intro (parse_asn1_ILC_twin id s) (fun id1 id2 _ _ -> assert (id1 = id) ; assert (id2 = id)) +*) diff --git a/src/ASN1/ASN1.Spec.IdentifierU32.fst b/src/ASN1/ASN1.Spec.IdentifierU32.fst new file mode 100755 index 000000000..25d49f36e --- /dev/null +++ b/src/ASN1/ASN1.Spec.IdentifierU32.fst @@ -0,0 +1,898 @@ +module ASN1.Spec.IdentifierU32 + +open ASN1.Base + +open LowParse.Tot.Base +open LowParse.Tot.Combinators +open LowParse.Tot.Int + +open FStar.Mul + +module U8 = FStar.UInt8 +module U32 = FStar.UInt32 +module Cast = FStar.Int.Cast + +let byte = U8.t + +(* Ref X690 8.1.2 *) + +let partial_state_lowerbound (n : nat) = + pow2 (n * 7) + +let lemma_partial_state_lowerbound_mono (n : nat) (m : nat) : Lemma + (requires m <= n) + (ensures partial_state_lowerbound m <= partial_state_lowerbound n) = + let _ = Math.Lemmas.pow2_le_compat (n * 7) (m * 7) in _ + +let partial_state_upperbound (n : nat) = + pow2 ((n + 1) * 7) + +let lemma_partial_state_upperbound_eq (n : nat) : Lemma (partial_state_upperbound n = partial_state_lowerbound (n + 1)) = _ + +let partial_state_in_bound (n : nat) (v : nat) = + partial_state_lowerbound n <= v /\ v < partial_state_upperbound n + +let partial_state_bound_separation (n : nat) (v : U32.t) (v' : U32.t) : + Lemma (requires (U32.v v < partial_state_upperbound n /\ partial_state_lowerbound (n + 1) <= U32.v v')) + (ensures (U32.v v < U32.v v')) += _ + +let partial_state_bound_f32 (ui : U32.t) : Pure nat + (requires (0 < U32.v ui)) + (ensures (fun n -> n <= 4 /\ partial_state_in_bound n (U32.v ui))) += let v = U32.v ui in + if v < partial_state_upperbound 0 then + (0) + else + (if v < partial_state_upperbound 1 then + (1) + else + (if v < partial_state_upperbound 2 then + (2) + else + (if v < partial_state_upperbound 3 then + (3) + else + (4)))) + +#push-options "--z3rlimit 128 --fuel 0 --ifuel 0" + +let lemma_partial_state_bound_f32_intro (ui : U32.t) (n : nat) : + Lemma (requires (partial_state_in_bound n (U32.v ui))) + (ensures (0 < U32.v ui /\ partial_state_bound_f32 ui = n)) += let v = U32.v ui in + let _ = + Math.Lemmas.pow2_lt_compat 7 0; + Math.Lemmas.pow2_lt_compat 14 7; + Math.Lemmas.pow2_lt_compat 21 14; + Math.Lemmas.pow2_lt_compat 28 21; + Math.Lemmas.pow2_lt_compat 35 28 + in + match n with + | 0 -> _ + | 1 -> _ + | 2 -> _ + | 3 -> _ + | 4 -> _ + | _ -> let _ = Math.Lemmas.pow2_le_compat (n * 7) 35 in + assert (pow2 35 <= v) + +type asn1_partial_id_t = (x : U32.t {0 < U32.v x}) + +let partial_state_prefixf (state : asn1_partial_id_t) (m : nat) : Pure (asn1_partial_id_t) + (requires (m <= partial_state_bound_f32 state)) + (ensures (fun state' -> partial_state_bound_f32 state' = m)) += let n = partial_state_bound_f32 state in + let delta = U32.mul (U32.uint_to_t (n - m)) 7ul in + assert (U32.v delta < 32); + let _ = UInt.shift_right_value_lemma (U32.v state) (U32.v delta) in + let ret = U32.shift_right state delta in + assert (U32.v state < partial_state_upperbound n); + let _ = Math.Lemmas.lemma_div_lt (U32.v state) ((n + 1) * 7) (U32.v delta) in + assert (U32.v ret < partial_state_upperbound m); + assert (partial_state_lowerbound n <= U32.v state); + let _ = Math.Lemmas.pow2_minus (n * 7) (U32.v delta) in + let _ = Math.Lemmas.lemma_div_le (partial_state_lowerbound n) (U32.v state) (pow2 (U32.v delta)) in + assert (partial_state_lowerbound m = partial_state_lowerbound n / (pow2 (U32.v delta))); + assert (U32.v ret = U32.v state / (pow2 (U32.v delta))); + assert (partial_state_lowerbound m <= U32.v ret); + let _ = lemma_partial_state_bound_f32_intro ret m in + ret + +let lemma_partial_state_prefixf_val (state : asn1_partial_id_t) (m : nat) +: Lemma (requires (m <= partial_state_bound_f32 state)) + (ensures (U32.v (partial_state_prefixf state m) = U32.v state / (pow2 (((partial_state_bound_f32 state) - m) * 7)))) += let delta = U32.mul (U32.uint_to_t ((partial_state_bound_f32 state) - m)) 7ul in + let _ = UInt.shift_right_value_lemma (U32.v state) (U32.v delta) in _ + +let partial_state_prefixr (state : asn1_partial_id_t) (state' : asn1_partial_id_t) = + (let n = partial_state_bound_f32 state in + let m = partial_state_bound_f32 state' in + n < m /\ + state = partial_state_prefixf state' n) + +let partial_state_prefixr_intro (a : asn1_partial_id_t) (b : nat) (state : asn1_partial_id_t) +: Lemma (requires ((let n = partial_state_bound_f32 a in + partial_state_bound_f32 state = n + 1 /\ + b < pow2 7 /\ (U32.v state) = (U32.v a) * (pow2 7) + b))) + (ensures (partial_state_prefixr a state)) += let n = partial_state_bound_f32 a in + let _ = + lemma_partial_state_prefixf_val state n; + Math.Lemmas.lemma_div_plus b (U32.v a) (pow2 7); + Math.Lemmas.small_div b (pow2 7) + in + _ + +let partial_state_prefixf_trans (state : asn1_partial_id_t) (n' : nat) (n'' : nat) : +Lemma (requires (n'' <= n' /\ n' <= partial_state_bound_f32 state)) + (ensures (partial_state_prefixf (partial_state_prefixf state n') n'' = partial_state_prefixf state n'')) += let n = partial_state_bound_f32 state in + let _ = + lemma_partial_state_prefixf_val state n'; + lemma_partial_state_prefixf_val state n''; + lemma_partial_state_prefixf_val (partial_state_prefixf state n') n'' + in + assert (U32.v (partial_state_prefixf state n'') = U32.v state / (pow2 ((n - n'') * 7))); + assert (U32.v (partial_state_prefixf (partial_state_prefixf state n') n'') = U32.v state / (pow2 ((n - n') * 7)) / (pow2((n' - n'') * 7))); + let _ = Math.Lemmas.division_multiplication_lemma (U32.v state) (pow2 ((n - n') * 7)) (pow2 ((n' - n'') * 7)) in + assert (U32.v (partial_state_prefixf (partial_state_prefixf state n') n'') = U32.v state / ((pow2 ((n - n') * 7)) * (pow2((n' - n'') * 7)))); + let _ = + Math.Lemmas.pow2_plus ((n - n') * 7) ((n' - n'') * 7); + Math.Lemmas.distributivity_add_left (n - n') (n' - n'') 7 + in + assert ((n - n') * 7 + (n' - n'') * 7 = (n - n' + (n' - n'')) * 7); + assert (U32.v (partial_state_prefixf (partial_state_prefixf state n') n'') = U32.v state / (pow2 ((n - n'') * 7))) + +let partial_state_prefixr_trans (state state' state'' : asn1_partial_id_t) +: Lemma (requires (partial_state_prefixr state state' /\ partial_state_prefixr state' state'')) + (ensures (partial_state_prefixr state state'')) += let n = partial_state_bound_f32 state in + let n' = partial_state_bound_f32 state' in + let n'' = partial_state_bound_f32 state'' in + let _ = partial_state_prefixf_trans state'' n' n in _ + +let partial_state_prefixr_weaken + (state :asn1_partial_id_t) + (state' : asn1_partial_id_t {partial_state_prefixr state state'}) + (state'' : asn1_partial_id_t {partial_state_prefixr state' state''}) : + (s : asn1_partial_id_t {s = state'' /\ partial_state_prefixr state s}) += let _ = partial_state_prefixr_trans state state' state'' in + state'' + +#push-options "--fuel 1 --ifuel 0" + +let update_state (state :asn1_partial_id_t) (b : byte) : Pure (asn1_partial_id_t) + (requires (U8.v b < 128 /\ (U32.v state < pow2 25))) + (ensures (fun state' -> partial_state_bound_f32 state' = (partial_state_bound_f32 state) + 1 /\ partial_state_prefixr state state')) += let n = partial_state_bound_f32 state in + let _ = Math.Lemmas.lemma_mult_lt_right (pow2 7) (U32.v state) (pow2 25) in + let _ = Math.Lemmas.pow2_plus 25 7 in + assert (U32.v state * pow2 7 < pow2 32); + let _ = Math.Lemmas.pow2_lt_compat 28 25 in + assert (partial_state_bound_f32 state < 4); + let _ = UInt.shift_left_value_lemma (U32.v state) 7 in + let _ = Math.Lemmas.pow2_plus ((n + 1) * 7) 7 in + let a = U32.shift_left state 7ul in + assert (U32.v a < partial_state_upperbound (n + 1)); + let _ = Math.Lemmas.pow2_plus (n * 7) 7 in + assert (partial_state_lowerbound (n + 1) <= U32.v a); + let b = Cast.uint8_to_uint32 b in + assert (U32.v a % (pow2 7) = 0); + assert (U32.v b < pow2 7); + let _ = Math.Lemmas.cancel_mul_mod (U32.v state) (pow2 7) in + let _ = UInt.logor_disjoint (U32.v a) (U32.v b) 7 in + let _ = UInt.logor_ge (U32.v a) (U32.v b) in + let ret = (U32.logor a b) in + assert (partial_state_in_bound (n + 1) (U32.v ret)); + assert (partial_state_bound_f32 ret = n + 1); + let _ = partial_state_prefixr_intro state (U32.v b) ret in + ret + +let update_state_inj2 (s1 s2 : asn1_partial_id_t) (b1 b2 : byte) +: Lemma + (requires ((U8.v b1 < 128 /\ U8.v b2 < 128) /\ + (U32.v s1 < pow2 25) /\ + (U32.v s2 < pow2 25) /\ + update_state s1 b1 = update_state s2 b2)) + (ensures (s1 = s2 /\ b1 = b2)) += assert (s1 = s2); + let _ = UInt.shift_left_value_lemma (U32.v s1) 7 in + let _ = Math.Lemmas.cancel_mul_mod (U32.v s1) (pow2 7) in + let a = UInt.shift_left (U32.v s1) 7 in + UInt.logor_disjoint a (U8.v b1) 7; + UInt.logor_disjoint a (U8.v b2) 7; + assert (U32.v (update_state s1 b1) = UInt.logor a (U8.v b1)); + assert (U32.v (update_state s2 b2) = UInt.logor a (U8.v b2)) + +let in_bound_32 (i : nat) (v : U32.t) : Lemma + (requires i <= 2 /\ (U32.v v < partial_state_upperbound i)) + (ensures U32.v v < pow2 25) += assert (U32.v v < pow2 ((i + 1) * 7)); + let _ = Math.Lemmas.pow2_le_compat 21 ((i + 1) * 7) in + assert (U32.v v < pow2 21); + let _ = Math.Lemmas.pow2_lt_compat 25 21 in + _ + +let decode_asn1_identifier_class (b : byte { U8.v b <= 3 }) : asn1_id_class_t += match b with + | 0uy -> UNIVERSAL + | 1uy -> APPLICATION + | 2uy -> CONTEXT_SPECIFIC + | 3uy -> PRIVATE + +let decode_asn1_identifier_class' (buf : byte) : asn1_id_class_t += let b = U8.shift_right buf 6ul in + decode_asn1_identifier_class b + +let decode_asn1_identifier_flag (b : byte { U8.v b <= 1 }) : asn1_id_flag_t += match b with + | 0uy -> PRIMITIVE + | 1uy -> CONSTRUCTED + +let decode_asn1_identifier_flag' (buf : byte) : asn1_id_flag_t += let b = U8.rem (U8.shift_right buf 5ul) 2uy in + decode_asn1_identifier_flag b + +let parse_asn1_identifier_tail_kind = strong_parser_kind 0 0 None + +let parse_asn1_identifier_tail (state : asn1_partial_id_t {partial_state_bound_f32 state = 3}) (buf : byte) : + parser parse_asn1_identifier_tail_kind (state' : asn1_partial_id_t {partial_state_prefixr state state'}) += if U8.lt buf 128uy then + if (U32.lt state (U32.uint_to_t (UInt.pow2_n #32 (32 - 7)))) then + let ret = update_state state buf in + weaken (parse_asn1_identifier_tail_kind) (parse_ret ret) + else + fail_parser (parse_asn1_identifier_tail_kind) _ + else + fail_parser (parse_asn1_identifier_tail_kind) _ + +let parse_asn1_identifier_tail_injective (state : asn1_partial_id_t {partial_state_bound_f32 state = 3}) : +Lemma (and_then_cases_injective (parse_asn1_identifier_tail state)) += and_then_cases_injective_intro (parse_asn1_identifier_tail state) (fun x1 x2 b1 b2 -> + assert (U8.v x1 < 128 /\ U8.v x2 < 128); + assert (U32.v state < pow2 (32 - 7)); + let _ = Math.Lemmas.pow2_plus (32 - 7) 7 in + let _ = Math.Lemmas.lemma_mult_lt_right (pow2 7) (U32.v state) (pow2 (32 - 7)) in + assert (U32.v state * pow2 7 < pow2 32); + let _ = update_state_inj2 state state x1 x2 in _) + +let parse_asn1_identifier_loop_kind (i : nat {i <= 3}) = strong_parser_kind 0 (3 - i) None + +let parse_asn1_identifier_loop_immediate_terminate + (i : nat {i <= 2}) + (state : asn1_partial_id_t {partial_state_bound_f32 state = i}) + (buf : byte {U8.v buf < 128}) +: (parser _ (state' : asn1_partial_id_t {partial_state_prefixr state state'})) += let _ = in_bound_32 i state in + parse_ret (update_state state buf) + +let parse_cast + (t : eqtype) + (p1 : t -> Type0) + (p2 : t -> Type0) + (lem : (x : t -> (Lemma (p1 x ==> p2 x)))) + (#k : parser_kind) + (p : parser k (x: t {p1 x})) +: (parser k (x : t {p2 x})) += let id_cast = + fun (x : t {p1 x}) -> + let _ = lem x in + (x <: (x : t {p2 x})) + in + parse_synth #k #(x : t{p1 x}) #(x : t{p2 x}) p id_cast + +let parse_cast_inverse + (t : eqtype) + (p1 : t -> Type0) + (p2 : t -> Type0) + (lem : (x : t -> (Lemma (p1 x ==> p2 x)))) + (#k : parser_kind) + (p : parser k (x : t {p1 x})) + (b : bytes) + (x : t {p2 x}) + (l : consumed_length b) +: Pure (x : t {p1 x}) + (requires (parse (parse_cast t p1 p2 lem p) b = Some (x, l))) + (ensures (fun y -> y = x)) += let id_cast = + fun (x : t {p1 x}) -> + let _ = lem x in + (x <: (x : t {p2 x})) + in + let _ = parse_synth_eq #k #(x : t{p1 x}) #(x : t{p2 x}) p id_cast b in + match p b with + | Some (x, l) -> x + + +let parse_asn1_identifier_loop_continue_weaken + (state : asn1_partial_id_t) + (state' : asn1_partial_id_t {partial_state_prefixr state state'}) + (#k : parser_kind) + (p : parser k (state'' : asn1_partial_id_t {partial_state_prefixr state' state''})) +: parser k (state'' : asn1_partial_id_t {partial_state_prefixr state state''}) += let lem = Classical.move_requires (partial_state_prefixr_trans state state') in + parse_cast + asn1_partial_id_t + (fun state'' -> partial_state_prefixr state' state'') + (fun state'' -> partial_state_prefixr state state'') + lem p + +let lemma_parse_asn1_identifier_loop_continue_weaken_inverse + (state : asn1_partial_id_t) + (state' : asn1_partial_id_t {partial_state_prefixr state state'}) + (#k : parser_kind) + (p : parser k (state'' : asn1_partial_id_t {partial_state_prefixr state' state''})) + (b : bytes) + (state'' : asn1_partial_id_t {partial_state_prefixr state state''}) + (l : _) +: Lemma + (requires (parse (parse_asn1_identifier_loop_continue_weaken state state' p) b = Some (state'', l))) + (ensures (partial_state_prefixr state' state'')) += let lem = Classical.move_requires (partial_state_prefixr_trans state state') in + let state''' = parse_cast_inverse + asn1_partial_id_t + (fun state'' -> partial_state_prefixr state' state'') + (fun state'' -> partial_state_prefixr state state'') + lem + p b state'' l in + assert (partial_state_prefixr state' state'''); + _ + +let loop_continue_calc_state' + (state : asn1_partial_id_t) + (buf : byte) +: Pure (asn1_partial_id_t) + (requires (partial_state_bound_f32 state <= 2 /\ U8.v buf >= 128)) + (ensures (fun state' -> partial_state_bound_f32 state' = (partial_state_bound_f32 state) + 1 /\ partial_state_prefixr state state')) += let b = U8.sub buf 128uy in + let i = partial_state_bound_f32 state in + let _ = in_bound_32 i state in + update_state state b + +let lemma_loop_continue_calc_state'_inj + (state : asn1_partial_id_t) + (buf1 buf2: byte) + (state1 : asn1_partial_id_t) + (state2 : asn1_partial_id_t) +: Lemma + (requires (partial_state_bound_f32 state <= 2 /\ U8.v buf1 >= 128 /\ U8.v buf2 >= 128 + /\ loop_continue_calc_state' state buf1 = state1 /\ loop_continue_calc_state' state buf2 = state2 /\ state1 = state2)) + (ensures (buf1 = buf2)) += let b1 = U8.sub buf1 128uy in + let b2 = U8.sub buf2 128uy in + let i = partial_state_bound_f32 state in + let _ = in_bound_32 i state in + update_state_inj2 state state b1 b2 + +let parse_loop_continuation_type (i : nat {i <= 3}) = + (state : asn1_partial_id_t {partial_state_bound_f32 state = i}) -> byte -> + (parser (parse_asn1_identifier_loop_kind (i)) (state' : asn1_partial_id_t {partial_state_prefixr state state'})) + +let parse_loop_continuation_spec (i : nat {i <= 3}) (c : parse_loop_continuation_type i) = + forall (state' : asn1_partial_id_t). (partial_state_bound_f32 state' = i) ==> and_then_cases_injective (c state') + +let parse_asn1_identifier_loop_continue + (i : nat {i <= 2}) + (c : parse_loop_continuation_type (i + 1)) + (state' : asn1_partial_id_t {partial_state_bound_f32 state' = i + 1}) +: Pure (parser (parse_asn1_identifier_loop_kind i) (state'' : asn1_partial_id_t {partial_state_prefixr state' state''})) + (requires (parse_loop_continuation_spec (i + 1) c)) + (ensures (fun _ -> True)) += let p' = c state' in + weaken (parse_asn1_identifier_loop_kind i) (parse_u8 `and_then` p') + +let parse_asn1_identifier_loop' + (i : nat {i <= 2}) + (c : parse_loop_continuation_type (i + 1)) + (state : asn1_partial_id_t {partial_state_bound_f32 state = i }) + (buf : byte) + : Pure (parser (parse_asn1_identifier_loop_kind i) (state' : asn1_partial_id_t {partial_state_prefixr state state'})) + (requires (parse_loop_continuation_spec (i + 1) c)) + (ensures (fun _ -> True)) += if (U8.v buf) < 128 then + weaken _ (parse_asn1_identifier_loop_immediate_terminate i state buf) + else + let state' = loop_continue_calc_state' state buf in + let p = parse_asn1_identifier_loop_continue i c state' in + parse_asn1_identifier_loop_continue_weaken state state' p + +let lemma_parse_asn1_identifier_loop'_cases_injective' + (i : nat {i <= 2}) + (c : parse_loop_continuation_type (i + 1)) + (state : asn1_partial_id_t {partial_state_bound_f32 state = i }) +: Lemma + (requires (parse_loop_continuation_spec (i + 1) c)) + (ensures (and_then_cases_injective (parse_asn1_identifier_loop' i c state))) += let p = parse_asn1_identifier_loop' i c state in + and_then_cases_injective_intro p (fun x1 x2 b1 b2 -> + match (parse (p x1) b1) with + | Some (id1, l1) -> match (parse (p x2) b2) with + | Some (id2, l2) -> + (if (U8.v x1 < 128) then + (let _ = in_bound_32 i state in + if (U8.v x2 < 128) then + let _ = update_state_inj2 state state x1 x2 in _ + else + (let state' = loop_continue_calc_state' state x2 in + let p' = parse_asn1_identifier_loop_continue i c state' in + let _ = lemma_parse_asn1_identifier_loop_continue_weaken_inverse state state' p' b2 id2 l2 in + _)) + else + (let _ = in_bound_32 i state in + let state1' = loop_continue_calc_state' state x1 in + let p1' = parse_asn1_identifier_loop_continue i c state1' in + let _ = lemma_parse_asn1_identifier_loop_continue_weaken_inverse state state1' p1' b1 id1 l1 in + if (U8.v x2 < 128) then + _ + else + (let state2' = loop_continue_calc_state' state x2 in + let p2' = parse_asn1_identifier_loop_continue i c state2' in + let _ = lemma_parse_asn1_identifier_loop_continue_weaken_inverse state state2' p2' b2 id2 l2 in + let _ = lemma_loop_continue_calc_state'_inj state x1 x2 state1' state2' in _ + )))) + +let lemma_parse_asn1_identifier_loop'_cases_injective + (i : nat {i <= 2}) + (c : parse_loop_continuation_type (i + 1)) +: Lemma + (requires (parse_loop_continuation_spec (i + 1) c)) + (ensures (parse_loop_continuation_spec i (parse_asn1_identifier_loop' i c))) += Classical.forall_intro (Classical.move_requires (lemma_parse_asn1_identifier_loop'_cases_injective' i c)) + +let rec parse_asn1_identifier_loop + (i : nat {i <= 3}) +: Pure (parse_loop_continuation_type i) + (requires True) + (ensures (fun c -> parse_loop_continuation_spec i c)) + (decreases %[3 - i]) += if i = 3 then + let _ = Classical.forall_intro parse_asn1_identifier_tail_injective in + parse_asn1_identifier_tail + else + let c = parse_asn1_identifier_loop (i + 1) in + let _ = (lemma_parse_asn1_identifier_loop'_cases_injective i c) in + (parse_asn1_identifier_loop' i c) + +let initialize_partial_state + (b : byte) +: Pure asn1_partial_id_t + (requires (0 < U8.v b /\ U8.v b < 128)) + (ensures (fun state -> partial_state_bound_f32 state = 0)) += let ret = Cast.uint8_to_uint32 b in + let _ = lemma_partial_state_bound_f32_intro ret 0 in + ret + +let initialize_partial_state_inj + (b1 : byte) + (b2 : byte) +: Lemma (requires (0 < U8.v b1 /\ U8.v b1 < 128 /\ + 0 < U8.v b2 /\ U8.v b2 < 128 /\ + initialize_partial_state b1 = initialize_partial_state b2)) + (ensures b1 = b2) += _ + +let parse_asn1_identifier_head_kind = strong_parser_kind 0 4 None + +let parse_asn1_identifier_head' + (state : asn1_partial_id_t) +: Pure (parser parse_asn1_identifier_head_kind (state' : asn1_partial_id_t {partial_state_prefixr state state'})) + (requires (partial_state_bound_f32 state = 0)) + (ensures (fun _ -> True)) += let c = parse_asn1_identifier_loop 0 in + let _ = Squash.give_proof (Classical.Sugar.forall_elim state (Squash.get_proof (parse_loop_continuation_spec 0 c))) in + weaken (parse_asn1_identifier_head_kind) + (parse_u8 + `and_then` + (c state)) + +let parse_asn1_identifier_head + (buf : byte) +: parser parse_asn1_identifier_head_kind (state : asn1_partial_id_t {31 <= U32.v state}) += if (U8.v buf < 128) then + (if (U8.v buf < 31) then + fail_parser _ _ + else + weaken (parse_asn1_identifier_head_kind) (parse_ret (initialize_partial_state buf))) + else + (let b = U8.sub buf 128uy in + if (b = 0uy) then + fail_parser _ _ + else + (let state = (initialize_partial_state b) in + parse_cast + asn1_partial_id_t + (fun x -> partial_state_prefixr state x) + (fun x -> 31 <= U32.v x) + (fun _ -> _) + (parse_asn1_identifier_head' state))) + +let lemma_parse_asn1_identifier_head_inj () +: Lemma (ensures (and_then_cases_injective parse_asn1_identifier_head)) += let p = parse_asn1_identifier_head in + and_then_cases_injective_intro p (fun buf1 buf2 b1 b2 -> + match (parse (p buf1) b1) with + | Some (state1, l1) -> match (parse (p buf2) b2) with + | Some (state2, l2) -> + (if (U8.v buf1 < 128) then + ( + if (U8.v buf1 < 31) then + _ + else + ( if (U8.v buf2 < 128) then + ( + if (U8.v buf2 < 31) then + _ + else + ( + let _ = initialize_partial_state_inj buf1 buf2 in _ + ) + ) + else + ( + let buf2' = U8.sub buf2 128uy in + if (U8.v buf2' = 0) then + _ + else + ( + let state2' = initialize_partial_state buf2' in + let state2 = parse_cast_inverse + asn1_partial_id_t + (fun x -> partial_state_prefixr state2' x) + (fun x -> 31 <= U32.v x) + (fun _ -> _) + (parse_asn1_identifier_head' state2') + b2 + state2 + l2 + in + partial_state_bound_separation 0 state1 state2 + ) + ) + ) + ) + else + ( + let buf1' = U8.sub buf1 128uy in + if (U8.v buf1' = 0) then + _ + else + ( + let state1' = initialize_partial_state buf1' in + let state1 = parse_cast_inverse + asn1_partial_id_t + (fun x -> partial_state_prefixr state1' x) + (fun x -> 31 <= U32.v x) + (fun _ -> _) + (parse_asn1_identifier_head' state1') + b1 + state1 + l1 + in + ( if (U8.v buf2 < 128) then + ( + if (U8.v buf2 < 31) then + _ + else + ( + let _ = partial_state_bound_separation 0 state2 state1 in _ + ) + ) + else + ( + let buf2' = U8.sub buf2 128uy in + if (U8.v buf2' = 0) then + _ + else + ( + let state2' = initialize_partial_state buf2' in + let state2 = parse_cast_inverse + asn1_partial_id_t + (fun x -> partial_state_prefixr state2' x) + (fun x -> 31 <= U32.v x) + (fun _ -> _) + (parse_asn1_identifier_head' state2') + b2 + state2 + l2 + in + initialize_partial_state_inj buf1' buf2' + ) + ) + ) + ) + ) + ) + ) + +let parse_asn1_identifier_head_alt + (buf : byte) +: parser parse_asn1_identifier_head_kind (U32.t) += if (U8.v buf < 128) then + weaken (parse_asn1_identifier_head_kind) (parse_ret (Cast.uint8_to_uint32 buf)) + else + (let b = U8.sub buf 128uy in + if (b = 0uy) then + fail_parser _ _ + else + (let state = (initialize_partial_state b) in + parse_cast + U32.t + (fun x -> 0 < U32.v x /\ partial_state_prefixr state x) + (fun _ -> True) + (fun _ -> _) + (parse_asn1_identifier_head' state))) + +let lemma_parse_asn1_identifier_head_alt_inj () +: Lemma (ensures (and_then_cases_injective parse_asn1_identifier_head_alt)) += let p = parse_asn1_identifier_head_alt in + and_then_cases_injective_intro p (fun buf1 buf2 b1 b2 -> + match (parse (p buf1) b1) with + | Some (state1, l1) -> match (parse (p buf2) b2) with + | Some (state2, l2) -> + (if (U8.v buf1 < 128) then + (if (U8.v buf2 < 128) then + _ + else + (let buf2' = U8.sub buf2 128uy in + if (U8.v buf2' = 0) then + _ + else + ( + let state2' = initialize_partial_state buf2' in + let state2 = parse_cast_inverse + U32.t + (fun x -> 0 < U32.v x /\ partial_state_prefixr state2' x) + (fun _ -> True) + (fun _ -> _) + (parse_asn1_identifier_head' state2') + b2 state2 l2 + in + partial_state_bound_separation 0 state1 state2 + ) + ) + ) + else + ( + let buf1' = U8.sub buf1 128uy in + if (U8.v buf1' = 0) then + _ + else + ( + let state1' = initialize_partial_state buf1' in + let state1 = parse_cast_inverse + U32.t + (fun x -> 0 < U32.v x /\ partial_state_prefixr state1' x) + (fun _ -> True) + (fun _ -> _) + (parse_asn1_identifier_head' state1') + b1 state1 l1 + in + (if (U8.v buf2 < 128) then + _ + else + ( + let buf2' = U8.sub buf2 128uy in + if (U8.v buf2' = 0) then + _ + else + ( + let state2' = initialize_partial_state buf2' in + let state2 = parse_cast_inverse + U32.t + (fun x -> 0 < U32.v x /\ partial_state_prefixr state2' x) + (fun _ -> True) + (fun _ -> _) + (parse_asn1_identifier_head' state2') + b2 state2 l2 + in + initialize_partial_state_inj buf1' buf2' + ) + ) + ) + ) + ) + ) + ) + +let parse_asn1_identifier_first_kind = strong_parser_kind 0 5 None + +let parse_asn1_identifier_first' + (buf : byte {0 <= U8.v buf /\ U8.v buf <= 31}) +: parser parse_asn1_identifier_first_kind U32.t += if U8.v buf < 31 then + weaken (parse_asn1_identifier_first_kind) (parse_ret (Cast.uint8_to_uint32 buf)) + else + let _ = lemma_parse_asn1_identifier_head_inj () in + let p = weaken (parse_asn1_identifier_first_kind) + (parse_u8 + `and_then` + parse_asn1_identifier_head) in + parse_cast + U32.t + (fun x -> 31 <= U32.v x) + (fun _ -> True) + (fun _ -> _) + p + +let parse_asn1_identifier_first'_inj + (buf1 : byte {0 <= U8.v buf1 /\ U8.v buf1 <= 31}) + (buf2 : byte {0 <= U8.v buf2 /\ U8.v buf2 <= 31}) + (by1 : bytes) + (state1 : U32.t) + (l1 : consumed_length by1) + (by2 : bytes) + (state2 : U32.t) + (l2 : consumed_length by2) +: Lemma + (requires (parse (parse_asn1_identifier_first' buf1) by1 = Some (state1, l1) /\ + parse (parse_asn1_identifier_first' buf2) by2 = Some (state2, l2) /\ + state1 = state2)) + (ensures (buf1 = buf2)) += if U8.v buf1 < 31 then + ( + if U8.v buf2 < 31 then + _ + else + let _ = lemma_parse_asn1_identifier_head_inj () in + let p = weaken (parse_asn1_identifier_first_kind) + (parse_u8 + `and_then` + parse_asn1_identifier_head) in + let state2 = parse_cast_inverse + U32.t + (fun x -> 31 <= U32.v x) + (fun _ -> True) + (fun _ -> _) + p by2 state2 l2 in + _ + ) + else + ( + let _ = lemma_parse_asn1_identifier_head_inj () in + let p = weaken (parse_asn1_identifier_first_kind) + (parse_u8 + `and_then` + parse_asn1_identifier_head) in + let state1 = parse_cast_inverse + U32.t + (fun x -> 31 <= U32.v x) + (fun _ -> True) + (fun _ -> _) + p by1 state1 l1 in + if U8.v buf2 < 31 then + _ + else + _ + ) + +let parse_asn1_identifier_first + (buf : byte) +: parser parse_asn1_identifier_first_kind asn1_id_t += let id_class = decode_asn1_identifier_class' buf in + let id_flag = decode_asn1_identifier_flag' buf in + let b = U8.rem buf 32uy in + let p = parse_asn1_identifier_first' b in + parse_synth p (MK_ASN1_ID id_class id_flag) + +let encode_asn1_first_byte + (id_class : asn1_id_class_t) + (id_flag : asn1_id_flag_t) + (b : byte {0 <= U8.v b /\ U8.v b <= 31}) +: byte += let b0 = (match id_class with + | UNIVERSAL -> 0uy + | APPLICATION -> 1uy + | CONTEXT_SPECIFIC -> 2uy + | _ -> 3uy + ) + in + let b0' = U8.shift_left b0 6ul in + assert (U8.v b0' <= 128 + 64); + let b1 = (match id_flag with + | PRIMITIVE -> 0uy + | _ -> 1uy) + in + assert (U8.v b1 <= 1); + let b1' = U8.shift_left b1 5ul in + assert (U8.v b1' <= U8.v (U8.shift_left 1uy 5ul)); + assert (U8.v b1' <= 32); + U8.add b0' + (U8.add b1' + b) + +let lemma_encode_asn1_first_byte_inverse + (buf : byte) +: Lemma (ensures + buf = encode_asn1_first_byte (decode_asn1_identifier_class' buf) (decode_asn1_identifier_flag' buf) (U8.rem buf 32uy)) += let id_class = decode_asn1_identifier_class' buf in + let id_flag = decode_asn1_identifier_flag' buf in + let b = U8.rem buf 32uy in + let b0 = (match id_class with + | UNIVERSAL -> 0uy + | APPLICATION -> 1uy + | CONTEXT_SPECIFIC -> 2uy + | _ -> 3uy + ) + in + assert (b0 = (U8.shift_right buf) 6ul); + let b0' = U8.shift_left b0 6ul in + assert (U8.v b0' <= 128 + 64); + let b1 = (match id_flag with + | PRIMITIVE -> 0uy + | _ -> 1uy) + in + assert (U8.v b1 <= 1); + assert (b1 = U8.rem ((U8.shift_right buf) 5ul) 2uy); + let b1' = U8.shift_left b1 5ul in + assert (U8.v b1' <= U8.v (U8.shift_left 1uy 5ul)); + assert (U8.v b1' <= 32); + let _ = UInt.shift_right_value_lemma #8 (U8.v buf) 6 in + let _ = UInt.shift_right_value_lemma #8 (U8.v buf) 5 in + let _ = UInt.shift_left_value_lemma #8 (U8.v b0) 6 in + let _ = UInt.shift_left_value_lemma #8 (U8.v b1) 5 in + assert (buf = U8.add b0' (U8.add b1' b)) + +let lemma_decode_asn1_identifier_first_byte_inj + (id_class1 id_class2 : asn1_id_class_t) + (id_flag1 id_flag2 : asn1_id_flag_t) + (b1 b2 : byte) + (buf1 buf2 : byte) +: Lemma (requires + (id_class1 = id_class2 /\ + id_flag1 = id_flag2 /\ + (0 <= U8.v b1 /\ U8.v b1 <= 31) /\ + (0 <= U8.v b2 /\ U8.v b2 <= 31) /\ + b1 = b2 /\ + decode_asn1_identifier_class' buf1 = id_class1 /\ + decode_asn1_identifier_class' buf2 = id_class2 /\ + decode_asn1_identifier_flag' buf1 = id_flag1 /\ + decode_asn1_identifier_flag' buf2 = id_flag2 /\ + U8.rem buf1 32uy = b1 /\ + U8.rem buf2 32uy = b2)) + (ensures (buf1 = buf2)) += let _ = lemma_encode_asn1_first_byte_inverse buf1 in + let _ = lemma_encode_asn1_first_byte_inverse buf2 in + _ + +let lemma_parse_asn1_identifier_first_inj () +: Lemma (ensures (and_then_cases_injective parse_asn1_identifier_first)) += let p = parse_asn1_identifier_first in + and_then_cases_injective_intro p (fun buf1 buf2 by1 by2 -> + match (parse (p buf1) by1) with + | Some (MK_ASN1_ID id_class1' id_flag1' state1', l1') -> match (parse (p buf2) by2) with + | Some (MK_ASN1_ID id_class2' id_flag2' state2', l2') -> + ( + let id_class1 = decode_asn1_identifier_class (U8.shift_right buf1 6ul) in + let id_flag1 = decode_asn1_identifier_flag (U8.rem (U8.shift_right buf1 5ul) 2uy)in + let b1 = U8.rem buf1 32uy in + let p1 = parse_asn1_identifier_first' b1 in + let _ = parse_synth_eq p1 (MK_ASN1_ID id_class1 id_flag1) by1 in + match (parse p1 by1) with + | Some (state1, l1) -> + ( + let id_class2 = decode_asn1_identifier_class (U8.shift_right buf2 6ul) in + let id_flag2 = decode_asn1_identifier_flag (U8.rem (U8.shift_right buf2 5ul) 2uy)in + let b2 = U8.rem buf2 32uy in + let p2 = parse_asn1_identifier_first' b2 in + let _ = parse_synth_eq p2 (MK_ASN1_ID id_class2 id_flag2) by2 in + match (parse p2 by2) with + | Some (state2, l2) -> + let _ = parse_asn1_identifier_first'_inj b1 b2 by1 state1 l1 by2 state2 l2 in + assert (b1 = b2); + lemma_decode_asn1_identifier_first_byte_inj id_class1 id_class2 id_flag1 id_flag2 b1 b2 buf1 buf2 + ) + ) + ) + +let parse_asn1_identifier_U32 : asn1_strong_parser (asn1_id_t) += let _ = lemma_parse_asn1_identifier_first_inj () in + weaken (asn1_strong_parser_kind) + (parse_u8 + `and_then` + parse_asn1_identifier_first) + +let parse_asn1_identifier_U32_alt : parser parse_asn1_identifier_first_kind U32.t += let _ = lemma_parse_asn1_identifier_head_alt_inj () in + weaken (parse_asn1_identifier_first_kind) + (parse_u8 `and_then` parse_asn1_identifier_head_alt) diff --git a/src/ASN1/ASN1.Spec.Interpreter.fst b/src/ASN1/ASN1.Spec.Interpreter.fst new file mode 100644 index 000000000..98e6b9d35 --- /dev/null +++ b/src/ASN1/ASN1.Spec.Interpreter.fst @@ -0,0 +1,312 @@ +module ASN1.Spec.Interpreter +include LowParse.Tot.Base +include LowParse.Tot.Combinators +include LowParse.Tot.List +include LowParse.Tot.Defaultable +include LowParse.Tot.Bytes + +include ASN1.Debug + +open ASN1.Base +open ASN1.Spec.Content.BOOLEAN +open ASN1.Spec.Content.INTEGER +open ASN1.Spec.Content.BITSTRING +open ASN1.Spec.Content.OCTETSTRING +open ASN1.Spec.Content.UTF8STRING +open ASN1.Spec.Content.PRINTABLESTRING +open ASN1.Spec.Content.IA5STRING +open ASN1.Spec.Content.NULL +open ASN1.Spec.Content.OIDU32 +open ASN1.Spec.Content.TIME + +open ASN1.Spec.ILC +open ASN1.Spec.Choice +open ASN1.Spec.Sequence +open ASN1.Spec.Any +open ASN1.Spec.Set + +module List = FStar.List.Tot + +let parse_non_empty_list + (#k : parser_kind) + (#t : Type) + (p : parser k t) +: asn1_weak_parser (non_empty_list t) += weaken _ ((parse_list p) `parse_filter` isNonEmpty) + +let parse_non_empty_set + (#t : Type) + (p : asn1_strong_parser t) +: asn1_weak_parser (non_empty_list t) += (parse_asn1_set_of p) `parse_filter` isNonEmpty + +let auto_asn1_sequence_t_core_equiv #id_decs (items:asn1_gen_items_l id_decs) +: Lemma + (ensures asn1_sequence_t_core items == asn1_sequence_t (l_as_list items)) + [SMTPat (asn1_sequence_t (l_as_list items))] += asn1_sequence_t_core_equiv items + +let auto_asn1_sequence_t_core_equiv' (items:list asn1_gen_item_k) +: Lemma + (ensures asn1_sequence_t_core (list_as_l items) == asn1_sequence_t items) + [SMTPat (asn1_sequence_t items)] += asn1_sequence_t_core_equiv' items + +let auto_asn1_any_t_equiv (#t:eqtype) (ls:list (t & asn1_gen_items_lk)) +: Lemma + (ensures asn1_any_t_core t ls == asn1_any_t t (t_lk_as_t_k ls)) + [SMTPat (asn1_any_t_core t ls)] += asn1_any_t_equiv ls + +let auto_asn1_any_t_equiv' (#t:eqtype) (ls:list (t & asn1_gen_items_k)) +: Lemma + (ensures asn1_any_t_core t (t_k_as_t_lk ls) == asn1_any_t t ls) + [SMTPat (asn1_any_t t ls)] += asn1_any_t_equiv' ls + +let auto_asn1_sequence_any_t_equiv #id_decs (items : asn1_gen_items_l id_decs) (suffix_t : Type) +: Lemma + (ensures asn1_sequence_any_t_core items suffix_t == + asn1_sequence_any_t (l_as_list items) suffix_t) + [SMTPat (asn1_sequence_any_t_core items suffix_t)] += asn1_sequence_any_t_equiv items suffix_t + +let auto_asn1_sequence_any_t_equiv' (items : list asn1_gen_item_k) (suffix_t : Type) +: Lemma + (ensures asn1_sequence_any_t_core (list_as_l items) suffix_t == + asn1_sequence_any_t items suffix_t) + [SMTPat (asn1_sequence_any_t items suffix_t)] += asn1_sequence_any_t_equiv' items suffix_t + +let rec dasn1_terminal_as_parser (k : asn1_terminal_k) : asn1_weak_parser (asn1_terminal_t k) = + parse_debug #(asn1_terminal_t k) #(asn1_weak_parser_kind) "asn1_terminal_as_parser" + (match k with + | ASN1_BOOLEAN -> weaken _ parse_asn1_boolean + | ASN1_INTEGER bound -> weaken _ (parse_untagged_bounded_integer bound) + | ASN1_BITSTRING -> parse_asn1_bitstring + | ASN1_OCTETSTRING -> parse_asn1_octetstring + | ASN1_UTF8STRING -> parse_asn1_utf8string + | ASN1_PRINTABLESTRING -> parse_asn1_printablestring + | ASN1_IA5STRING -> parse_asn1_ia5string + | ASN1_NULL -> parse_asn1_null + | ASN1_OID -> parse_asn1_OIDU32 + | ASN1_UTCTIME -> parse_asn1_UTCTIME + | ASN1_GENERALIZEDTIME -> parse_asn1_GENERALIZEDTIME + | ASN1_PREFIXED_TERMINAL id k -> weaken asn1_weak_parser_kind (parse_asn1_ILC id #(ASN1_TERMINAL k) (dasn1_terminal_as_parser k))) + +and dasn1_sequence_as_parser #id_decs (ls:asn1_gen_items_l id_decs) +: Tot (lp : list gen_decorated_parser_twin {List.map (Mkgendcparser?.d) lp == l_as_list ls }) (decreases ls) += match ls with + | ASN1_GEN_ITEMS_NIL -> [] + | ASN1_GEN_ITEMS_CONS s d hd _ tl -> + dasn1_decorated_as_parser_twin hd :: dasn1_sequence_as_parser tl + +and dasn1_content_as_parser (k : asn1_content_k) : Tot (asn1_weak_parser (asn1_content_t k)) (decreases k) = + match k with + | ASN1_RESTRICTED_TERMINAL k' is_valid -> weaken _ ((dasn1_terminal_as_parser k') `parse_filter` is_valid) + | ASN1_TERMINAL k' -> dasn1_terminal_as_parser k' + | ASN1_SEQUENCE gitems -> make_asn1_sequence_parser (dasn1_sequence_as_parser (dsnd gitems)) + | ASN1_SEQUENCE_OF k' -> parse_non_empty_list (dasn1_as_parser k') + | ASN1_SET_OF k' -> parse_non_empty_set (dasn1_as_parser k') + | ASN1_PREFIXED k' -> weaken _ (dasn1_as_parser k') + | ASN1_ANY_DEFINED_BY id_decs_prefix prefix id key_k ls ofb pf pf' -> + let itemtwins = dasn1_sequence_as_parser prefix in //(l_as_list prefix) in + let key_p_twin = + (let kc = ASN1_TERMINAL key_k in + let p = dasn1_terminal_as_parser key_k in + let _ = parser_asn1_ILC_twin_case_injective id #kc p in + Mkparsertwin #asn1_strong_parser_kind #(asn1_terminal_t key_k) (parse_asn1_ILC id #kc p) (parse_asn1_ILC_twin id #kc p)) + in + let key_p = Mkparsertwin?.p key_p_twin in + let key_fp = Mkparsertwin?.fp key_p_twin in + let supported_p = dasn1_ls_as_parser (asn1_terminal_t key_k) ls in + (match ofb with + | None -> + let suffix_p_twin = (Mkparsertwin #asn1_weak_parser_kind #(make_gen_choice_type (extract_types supported_p)) + (weaken asn1_weak_parser_kind (make_gen_choice_weak_parser key_p supported_p)) + (let _ = make_gen_choice_weak_parser_twin_and_then_cases_injective key_fp supported_p in + fun id -> weaken asn1_weak_parser_kind (make_gen_choice_weak_parser_twin key_fp supported_p id))) + in + make_asn1_sequence_any_parser itemtwins suffix_p_twin + | Some gitems -> + let fallback_p = + Mkgenparser _ + (parse_debug "parse_any_fallback" + (make_asn1_sequence_parser + (dasn1_sequence_as_parser (dsnd gitems)))) in + let suffix_p_twin = (Mkparsertwin #asn1_weak_parser_kind #(make_gen_choice_type_with_fallback (extract_types supported_p) (Mkgenparser?.t fallback_p)) + (weaken asn1_weak_parser_kind (make_gen_choice_with_fallback_weak_parser key_p supported_p fallback_p)) + (let _ = make_gen_choice_with_fallback_weak_parser_twin_and_then_cases_injective key_fp supported_p fallback_p in + fun id -> weaken asn1_weak_parser_kind (make_gen_choice_with_fallback_weak_parser_twin key_fp supported_p fallback_p id))) + in + make_asn1_sequence_any_parser itemtwins suffix_p_twin) + +and dasn1_ls_as_parser (t : eqtype) (ls : list (t * asn1_gen_items_lk)) : Tot (lp : list (t & (gen_parser asn1_weak_parser_kind)) {asn1_any_t_core t ls == extract_types lp}) (decreases ls) = + match ls with + | [] -> [] + | h :: tl -> + let (x, y) = h in + (x, Mkgenparser _ (make_asn1_sequence_parser (dasn1_sequence_as_parser (dsnd y)))) :: (dasn1_ls_as_parser t tl) + +and dasn1_lc_as_parser (lc : list (asn1_id_t & asn1_content_k)) : Tot (lp : list (asn1_id_t & (gen_parser asn1_strong_parser_kind)) {asn1_lc_t lc == extract_types lp}) (decreases lc) = + match lc with + | [] -> [] + | h :: t -> + let (x, y) = h in + (x, Mkgenparser (asn1_content_t y) (parse_asn1_LC (dasn1_content_as_parser y))) :: (dasn1_lc_as_parser t) + +and dasn1_as_parser (#s : _) (k : asn1_k s) : Tot (asn1_strong_parser (asn1_t k)) (decreases k) = + match k with + | ASN1_ILC id k' -> parse_debug "ASN1_ILC" (parse_asn1_ILC id (dasn1_content_as_parser k')) + | ASN1_CHOICE_ILC lc pf -> parse_debug "ASN1_CHOICE" (make_asn1_choice_parser lc pf k (dasn1_lc_as_parser lc)) + | ASN1_ANY_ILC -> parse_debug "ASN1_ANY" (parse_asn1_anyILC) + +and dasn1_as_parser_twin (#s : _) (k : asn1_k s) : Tot (asn1_strong_parser (asn1_t k) & (fp : (asn1_id_t -> asn1_strong_parser (asn1_t k)) {and_then_cases_injective fp})) (decreases k) = + match k with + | ASN1_ILC id k' -> + let p = dasn1_content_as_parser k' in + let _ = parser_asn1_ILC_twin_case_injective id p in + (parse_debug "ASN1_ILC" (parse_asn1_ILC id p), + parse_debugf "ASN1_ILC_f" (parse_asn1_ILC_twin id p)) + | ASN1_CHOICE_ILC lc pf -> + let lp = dasn1_lc_as_parser lc in + let _ = make_asn1_choice_parser_twin_cases_injective lc pf k lp in + (parse_debug "ASN1_CHOICE" (make_asn1_choice_parser lc pf k lp), + parse_debugf "ASN1_CHOICE_f" (make_asn1_choice_parser_twin lc pf k lp)) + | ASN1_ANY_ILC -> + let _ = parse_asn1_anyILC_twin_and_then_cases_injective () in + (parse_debug "ASN1_ANY" (parse_asn1_anyILC), + parse_debugf "ASN1_ANY_f" (parse_asn1_anyILC_twin)) + +and dasn1_decorated_as_parser_twin + (#s : FStar.Set.set asn1_id_t) + (#d : asn1_decorator) + (dk:asn1_decorated_k s d) +: Tot (gp : gen_decorated_parser_twin {Mkgendcparser?.d gp == (| s, d, dk |) }) + (decreases dk) += let item : asn1_gen_item_k = (| s, d, dk |) in + match dk with + | ASN1_PLAIN_ILC k -> + let (p, fp) = dasn1_as_parser_twin k in + Mkgendcparser item p fp + | ASN1_OPTION_ILC k -> + let (p, fp) = dasn1_as_parser_twin k in + Mkgendcparser item p fp + | ASN1_DEFAULT_TERMINAL id #k defv -> + let p = dasn1_terminal_as_parser k in + Mkgendcparser item (parse_asn1_ILC id #(ASN1_TERMINAL k) p) (parse_asn1_ILC_twin id #(ASN1_TERMINAL k) p) + | ASN1_DEFAULT_RESTRICTED_TERMINAL id #k is_valid defv -> + let p : asn1_weak_parser (asn1_decorated_pure_t item) = weaken _ ((dasn1_terminal_as_parser k) `parse_filter` is_valid) in + Mkgendcparser item (parse_asn1_ILC id #(ASN1_RESTRICTED_TERMINAL k is_valid) p) (parse_asn1_ILC_twin id #(ASN1_RESTRICTED_TERMINAL k is_valid) p) + + +let rec asn1_terminal_as_parser (k : asn1_terminal_k) : asn1_weak_parser (asn1_terminal_t k) = + match k with + | ASN1_BOOLEAN -> weaken _ parse_asn1_boolean + | ASN1_INTEGER bound -> weaken _ (parse_untagged_bounded_integer bound) + | ASN1_BITSTRING -> parse_asn1_bitstring + | ASN1_OCTETSTRING -> parse_asn1_octetstring + | ASN1_UTF8STRING -> parse_asn1_utf8string + | ASN1_PRINTABLESTRING -> parse_asn1_printablestring + | ASN1_IA5STRING -> parse_asn1_ia5string + | ASN1_NULL -> parse_asn1_null + | ASN1_OID -> parse_asn1_OIDU32 + | ASN1_UTCTIME -> parse_asn1_UTCTIME + | ASN1_GENERALIZEDTIME -> parse_asn1_GENERALIZEDTIME + | ASN1_PREFIXED_TERMINAL id k -> weaken asn1_weak_parser_kind (parse_asn1_ILC id #(ASN1_TERMINAL k) (asn1_terminal_as_parser k)) + +and asn1_content_as_parser (k : asn1_content_k) : Tot (asn1_weak_parser (asn1_content_t k)) (decreases k) = + match k with + | ASN1_RESTRICTED_TERMINAL k' is_valid -> weaken _ ((asn1_terminal_as_parser k') `parse_filter` is_valid) + | ASN1_TERMINAL k' -> asn1_terminal_as_parser k' + | ASN1_SEQUENCE gitems -> make_asn1_sequence_parser (asn1_sequence_as_parser (dsnd gitems)) + | ASN1_SEQUENCE_OF k' -> parse_non_empty_list (asn1_as_parser k') + | ASN1_SET_OF k' -> parse_non_empty_set (asn1_as_parser k') + | ASN1_PREFIXED k' -> weaken _ (asn1_as_parser k') + | ASN1_ANY_DEFINED_BY id_decs_prefix prefix id key_k ls ofb pf pf' -> + let itemtwins = asn1_sequence_as_parser prefix in + let key_p_twin = + (let kc = ASN1_TERMINAL key_k in + let p = asn1_terminal_as_parser key_k in + let _ = parser_asn1_ILC_twin_case_injective id #kc p in + Mkparsertwin #asn1_strong_parser_kind #(asn1_terminal_t key_k) (parse_asn1_ILC id #kc p) (parse_asn1_ILC_twin id #kc p)) + in + let key_p = Mkparsertwin?.p key_p_twin in + let key_fp = Mkparsertwin?.fp key_p_twin in + let supported_p = asn1_ls_as_parser (asn1_terminal_t key_k) ls in + (match ofb with + | None -> + let suffix_p_twin = (Mkparsertwin #asn1_weak_parser_kind #(make_gen_choice_type (extract_types supported_p)) + (weaken asn1_weak_parser_kind (make_gen_choice_weak_parser key_p supported_p)) + (let _ = make_gen_choice_weak_parser_twin_and_then_cases_injective key_fp supported_p in + fun id -> weaken asn1_weak_parser_kind (make_gen_choice_weak_parser_twin key_fp supported_p id))) + in + make_asn1_sequence_any_parser itemtwins suffix_p_twin + | Some gitems -> + let fallback_p = Mkgenparser _ (make_asn1_sequence_parser (asn1_sequence_as_parser (dsnd gitems))) in + let suffix_p_twin = (Mkparsertwin #asn1_weak_parser_kind #(make_gen_choice_type_with_fallback (extract_types supported_p) (Mkgenparser?.t fallback_p)) + (weaken asn1_weak_parser_kind (make_gen_choice_with_fallback_weak_parser key_p supported_p fallback_p)) + (let _ = make_gen_choice_with_fallback_weak_parser_twin_and_then_cases_injective key_fp supported_p fallback_p in + fun id -> weaken asn1_weak_parser_kind (make_gen_choice_with_fallback_weak_parser_twin key_fp supported_p fallback_p id))) + in + make_asn1_sequence_any_parser itemtwins suffix_p_twin) + +and asn1_ls_as_parser (t : eqtype) (ls : list (t * asn1_gen_items_lk)) : Tot (lp : list (t & (gen_parser asn1_weak_parser_kind)) {asn1_any_t_core t ls == extract_types lp}) (decreases ls) = + match ls with + | [] -> [] + | h :: tl -> + let (x, y) = h in + (x, Mkgenparser _ (make_asn1_sequence_parser (asn1_sequence_as_parser (dsnd y)))) :: (asn1_ls_as_parser t tl) + +and asn1_lc_as_parser (lc : list (asn1_id_t & asn1_content_k)) : Tot (lp : list (asn1_id_t & (gen_parser asn1_strong_parser_kind)) {asn1_lc_t lc == extract_types lp}) (decreases lc) = + match lc with + | [] -> [] + | h :: t -> + let (x, y) = h in + (x, Mkgenparser (asn1_content_t y) (parse_asn1_LC (asn1_content_as_parser y))) :: (asn1_lc_as_parser t) + +and asn1_as_parser (#s : _) (k : asn1_k s) : Tot (asn1_strong_parser (asn1_t k)) (decreases k) = + match k with + | ASN1_ILC id k' -> parse_asn1_ILC id (asn1_content_as_parser k') + | ASN1_CHOICE_ILC lc pf -> make_asn1_choice_parser lc pf k (asn1_lc_as_parser lc) + | ASN1_ANY_ILC -> parse_asn1_anyILC + +and asn1_as_parser_twin (#s : _) (k : asn1_k s) : Tot (asn1_strong_parser (asn1_t k) & (fp : (asn1_id_t -> asn1_strong_parser (asn1_t k)) {and_then_cases_injective fp})) (decreases k) = + match k with + | ASN1_ILC id k' -> + let p = asn1_content_as_parser k' in + let _ = parser_asn1_ILC_twin_case_injective id p in + (parse_asn1_ILC id p, parse_asn1_ILC_twin id p) + | ASN1_CHOICE_ILC lc pf -> + let lp = asn1_lc_as_parser lc in + let _ = make_asn1_choice_parser_twin_cases_injective lc pf k lp in + (make_asn1_choice_parser lc pf k lp, make_asn1_choice_parser_twin lc pf k lp) + | ASN1_ANY_ILC -> + let _ = parse_asn1_anyILC_twin_and_then_cases_injective () in + (parse_asn1_anyILC, parse_asn1_anyILC_twin) + +and asn1_decorated_as_parser_twin #s #d (dk:asn1_decorated_k s d) +: Tot (gp : gen_decorated_parser_twin {Mkgendcparser?.d gp == (| s, d, dk |) }) + (decreases dk) += let item = (| s, d, dk |) in + match dk with + | ASN1_PLAIN_ILC k -> + let (p, fp) = asn1_as_parser_twin k in + Mkgendcparser item p fp + | ASN1_OPTION_ILC k -> + let (p, fp) = asn1_as_parser_twin k in + Mkgendcparser item p fp + | ASN1_DEFAULT_TERMINAL id #k defv -> + let p = asn1_terminal_as_parser k in + Mkgendcparser item (parse_asn1_ILC id #(ASN1_TERMINAL k) p) (parse_asn1_ILC_twin id #(ASN1_TERMINAL k) p) + | ASN1_DEFAULT_RESTRICTED_TERMINAL id #k is_valid defv -> + let p : asn1_weak_parser (asn1_decorated_pure_t item) = weaken _ ((asn1_terminal_as_parser k) `parse_filter` is_valid) in + Mkgendcparser item (parse_asn1_ILC id #(ASN1_RESTRICTED_TERMINAL k is_valid) p) (parse_asn1_ILC_twin id #(ASN1_RESTRICTED_TERMINAL k is_valid) p) + +and asn1_sequence_as_parser + #id_decs (ls:asn1_gen_items_l id_decs) +: Tot (lp : list gen_decorated_parser_twin {List.map (Mkgendcparser?.d) lp == l_as_list ls }) (decreases ls) += match ls with + | ASN1_GEN_ITEMS_NIL -> [] + | ASN1_GEN_ITEMS_CONS s d dk _ tl -> + asn1_decorated_as_parser_twin dk :: asn1_sequence_as_parser tl diff --git a/src/ASN1/ASN1.Spec.LengthU32.fst b/src/ASN1/ASN1.Spec.LengthU32.fst new file mode 100755 index 000000000..f76500436 --- /dev/null +++ b/src/ASN1/ASN1.Spec.LengthU32.fst @@ -0,0 +1,20 @@ +module ASN1.Spec.LengthU32 + +open ASN1.Base + +open LowParse.Tot.Base + +module LPDER = LowParse.Tot.DER + +(* Ref: X.690 8.1.3 9.1 10.1 *) + +(* Reusing LowParse.Spec.DER *) + +let parse_asn1_length_u32_t_kind = LPDER.parse_bounded_der_length32_kind 0 4294967295 + +let parse_asn1_length_u32_t : parser parse_asn1_length_u32_t_kind asn1_length_u32_t += LPDER.parse_bounded_der_length32 0 4294967295 + +let serialize_asn1_length_u32_t : serializer parse_asn1_length_u32_t += LPDER.serialize_bounded_der_length32 0 4294967295 + diff --git a/src/ASN1/ASN1.Spec.Sequence.fst b/src/ASN1/ASN1.Spec.Sequence.fst new file mode 100755 index 000000000..f65e028c1 --- /dev/null +++ b/src/ASN1/ASN1.Spec.Sequence.fst @@ -0,0 +1,492 @@ +module ASN1.Spec.Sequence + +open ASN1.Base + +open LowParse.Tot.Base +open LowParse.Tot.Combinators + +open LowParse.Tot.Defaultable + +open ASN1.Spec.IdentifierU32 + +module List = FStar.List.Tot +module Seq = FStar.Seq +module Bytes = FStar.Bytes +module Set = FStar.Set + +let generate_defaultable_item (item : gen_decorated_parser_twin) : + Tot (option (asn1_decorated_t (Mkgendcparser?.d item))) += match (Mkgendcparser?.d item) with + | (| _, _, dk |) -> match dk with + | ASN1_PLAIN_ILC k -> None + | ASN1_OPTION_ILC k -> Some (None #(asn1_t k)) + | ASN1_DEFAULT_TERMINAL id #k defv -> Some (Default #(asn1_terminal_t k) #defv) + | ASN1_DEFAULT_RESTRICTED_TERMINAL id #k is_valid defv -> Some (Default #(asn1_decorated_pure_t (Mkgendcparser?.d item)) #defv) + +let rec generate_defaultable_items (itemtwins : list (gen_decorated_parser_twin)) : + Tot (option (asn1_sequence_t (List.map (Mkgendcparser?.d) itemtwins))) += match itemtwins with + | [] -> Some () + | [hd] -> generate_defaultable_item hd + | hd :: tl -> mk_option_tuple (generate_defaultable_item hd) (generate_defaultable_items tl) + +let defaultv_filter (#a : eqtype) (defaultv : a) : a -> Tot bool = + fun v -> not (v = defaultv) + +let defaultv_synth (#a : eqtype) (defaultv : a) (v : a {~(v = defaultv)}) : default_tv defaultv = + Nondefault v + +let parse_asn1_sequence_item_twin (item : gen_decorated_parser_twin) : + Tot (asn1_id_t -> asn1_strong_parser (asn1_decorated_t (Mkgendcparser?.d item))) += match item with + | Mkgendcparser d p fp -> + match d with + | (| _, _, dk|) -> + match dk with + | ASN1_PLAIN_ILC _ -> fun id -> (fp id) + | ASN1_OPTION_ILC _ -> fun id -> ((fp id) `parse_synth` (Some)) + | ASN1_DEFAULT_TERMINAL _ defaultv -> fun id -> + (((fp id) + `parse_filter` + (defaultv_filter defaultv)) + `parse_synth` + (defaultv_synth defaultv)) + | ASN1_DEFAULT_RESTRICTED_TERMINAL _ is_valid defaultv -> fun id -> + ((((fp id) + `parse_filter` + is_valid) + `parse_filter` + (defaultv_filter #(asn1_decorated_pure_t d) defaultv)) + `parse_synth` + (defaultv_synth #(asn1_decorated_pure_t d) defaultv)) + + +let parse_asn1_sequence_item_twin_nondefault + (item : gen_decorated_parser_twin) + (id : asn1_id_t) + (input : bytes) +: Lemma + (ensures (parse_defaultable_injective_cond (generate_defaultable_item item) (parse_asn1_sequence_item_twin item id) input)) += let p = (parse_asn1_sequence_item_twin item id) in + match item with + | Mkgendcparser d p' fp -> + match d with + | (| _, _, dk|) -> + match dk with + | ASN1_PLAIN_ILC _ -> _ + | ASN1_OPTION_ILC _ -> + let defv = generate_defaultable_item item in + (match parse p input with + | None -> () + | Some (v, _) -> + let _ = parse_synth_eq (fp id) (Some) input in + ()) + | ASN1_DEFAULT_TERMINAL _ defaultv -> + let defv = generate_defaultable_item item in + (match parse p input with + | None -> () + | Some (v, _) -> + let _ = parse_synth_eq ((fp id) `parse_filter` (defaultv_filter defaultv)) (defaultv_synth defaultv) input in + ()) + | ASN1_DEFAULT_RESTRICTED_TERMINAL _ is_valid defaultv -> + let defv = generate_defaultable_item item in + (match parse p input with + | None -> () + | Some (v, _) -> + let _ = parse_synth_eq (((fp id) `parse_filter` is_valid) `parse_filter` (defaultv_filter #(asn1_decorated_pure_t d) defaultv)) (defaultv_synth #(asn1_decorated_pure_t d) defaultv) input in + ()) + + +let and_then_cases_injective_some + (#t: Type) + (#t' : Type) + (p': (option t -> Tot (bare_parser t'))) +: GTot Type0 += forall (x1 x2: option t) (b1 b2: bytes) . {:pattern (parse (p' x1) b1); (parse (p' x2) b2)} + and_then_cases_injective_precond p' x1 x2 b1 b2 /\ Some? x1 /\ Some? x2 ==> + x1 == x2 + +let and_then_cases_injective_some_intro + (#t:Type) + (#t':Type) + (p': (option t -> Tot (bare_parser t'))) + (lem: ( + (x1: option t) -> + (x2: option t) -> + (b1: bytes) -> + (b2: bytes) -> + Lemma + (requires (and_then_cases_injective_precond p' x1 x2 b1 b2 /\ Some? x1 /\ Some? x2)) + (ensures (x1 == x2)) + )) +: Lemma + (and_then_cases_injective_some p') += Classical.forall_intro_3 (fun x1 x2 b1 -> Classical.forall_intro (Classical.move_requires (lem x1 x2 b1))) + +let and_then_cases_injective_elim + (#t:Type) + (#t':Type) + (p': (t -> Tot (bare_parser t'))) + (x1 x2 : t) + (b1 b2 : bytes) + : Lemma + (requires (and_then_cases_injective p' /\ and_then_cases_injective_precond p' x1 x2 b1 b2)) + (ensures (x1 == x2)) += () + +let and_then_cases_injective_some_elim + (#t:Type) + (#t':Type) + (p': (option t -> Tot (bare_parser t'))) + (x1 x2 : t) + (b1 b2 : bytes) + : Lemma + (requires (and_then_cases_injective_some p' /\ and_then_cases_injective_precond p' (Some x1) (Some x2) b1 b2)) + (ensures (x1 == x2)) += () + +let parse_asn1_sequence_item_twin_cases_injective + (item : gen_decorated_parser_twin) +: Lemma + (ensures (and_then_cases_injective (parse_asn1_sequence_item_twin item))) += match item with + | Mkgendcparser d _ fp -> + match d with + | (| _, _, dk|) -> + match dk with + | ASN1_PLAIN_ILC _ -> () + | ASN1_OPTION_ILC _ -> + let p = parse_asn1_sequence_item_twin item in + and_then_cases_injective_intro p ( + fun id1 id2 b1 b2 -> + parse_synth_eq (fp id1) (Some) b1; + parse_synth_eq (fp id2) (Some) b2; + and_then_cases_injective_elim fp id1 id2 b1 b2 + ) + | ASN1_DEFAULT_TERMINAL _ defaultv -> + let p = parse_asn1_sequence_item_twin item in + and_then_cases_injective_intro p ( + fun id1 id2 b1 b2 -> + parse_synth_eq ((fp id1) `parse_filter` (defaultv_filter defaultv)) (defaultv_synth defaultv) b1; + parse_synth_eq ((fp id2) `parse_filter` (defaultv_filter defaultv)) (defaultv_synth defaultv) b2; + parse_filter_eq (fp id1) (defaultv_filter defaultv) b1; + parse_filter_eq (fp id2) (defaultv_filter defaultv) b2; + and_then_cases_injective_elim fp id1 id2 b1 b2 + ) + | ASN1_DEFAULT_RESTRICTED_TERMINAL _ is_valid defaultv -> + let p = parse_asn1_sequence_item_twin item in + and_then_cases_injective_intro p ( + fun id1 id2 b1 b2 -> + parse_synth_eq (((fp id1) `parse_filter` is_valid) `parse_filter` (defaultv_filter #(asn1_decorated_pure_t d) defaultv)) (defaultv_synth #(asn1_decorated_pure_t d) defaultv) b1; + parse_synth_eq (((fp id2) `parse_filter` is_valid) `parse_filter` (defaultv_filter #(asn1_decorated_pure_t d) defaultv)) (defaultv_synth #(asn1_decorated_pure_t d) defaultv) b2; + parse_filter_eq ((fp id1) `parse_filter` is_valid) (defaultv_filter #(asn1_decorated_pure_t d) defaultv) b1; + parse_filter_eq ((fp id2) `parse_filter` is_valid) (defaultv_filter #(asn1_decorated_pure_t d) defaultv) b2; + parse_filter_eq (fp id1) is_valid b1; + parse_filter_eq (fp id2) is_valid b2; + and_then_cases_injective_elim fp id1 id2 b1 b2 + ) + + +(* FIXME: using option type as the state might cause problems for extracting the validator *) + +(* Thinking the loop as a fixpoint on option id so that disappears *) + +(* + |-id-> (T) +(G) -> (B) -id-> (T) --> (G) ... -id-> (T) + | | + V V + D + |-new-id--V +-Oid-> (G) -Oid-> (B) -id-> (T) -Oid-> (G) ... + | | + V V + D S +*) + +let make_asn1_sequence_parser_body_twin + (itemtwins : list (gen_decorated_parser_twin) {Cons? itemtwins}) +// (pf : (asn1_sequence_k_wf (List.map project_set_decorator itemtwins))) + (ploop : (l : list (gen_decorated_parser_twin) {l << itemtwins}) -> (st : (option asn1_id_t)) -> (asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) l)))) + : Pure (asn1_id_t -> asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) itemtwins))) + (requires (forall l. (l << itemtwins) ==> + (forall id. parse_defaultable_injective_cond_prop (generate_defaultable_items l) (ploop l (Some id))))) + (ensures (fun fp -> + (forall id. parse_defaultable_injective_cond_prop (generate_defaultable_items itemtwins) (fp id)))) += let (ret : (id : asn1_id_t) -> (p : asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) itemtwins)) + {parse_defaultable_injective_cond_prop (generate_defaultable_items itemtwins) p})) = fun id -> + (match itemtwins with + | [hd] -> + (match hd with + | Mkgendcparser d p fp -> + match d with + | (| s, de, dk |) -> + let _ = Classical.forall_intro (parse_asn1_sequence_item_twin_nondefault hd id) in + let (p : asn1_weak_parser (asn1_sequence_t (List.map Mkgendcparser?.d itemtwins))) = weaken _ (parse_asn1_sequence_item_twin hd id) in + //assert (parse_defaultable_injective_cond_prop (generate_defaultable_items itemtwins) p); + p) + | hd :: tl -> + let (p, ns) = + (match hd with + | Mkgendcparser d p fp -> + match d with + | (| s, de, dk |) -> + if (Set.mem id s) then + (parse_asn1_sequence_item_twin hd id, None) + else + match de with + | PLAIN -> (fail_parser asn1_strong_parser_kind _, None) + | _ -> + let defv' = generate_defaultable_item hd in + match defv' with + | Some defv -> + (weaken asn1_strong_parser_kind (parse_ret defv), Some id)) in + let p' = ploop tl ns in + let _ = (match hd with + | Mkgendcparser d pp fp -> + match d with + | (| s, de, dk |) -> + if (Set.mem id s) then + let _ = Classical.forall_intro (parse_asn1_sequence_item_twin_nondefault hd id) in + let _ = nondep_then_defaultable p (generate_defaultable_item hd) p' (generate_defaultable_items tl) in + () + else + match de with + | PLAIN -> () + | _ -> + let defv' = generate_defaultable_item hd in + match defv' with + | Some defv -> + let _ = + nondep_then_defaultable_snd p (generate_defaultable_item hd) p' (generate_defaultable_items tl) in + ()) in + let rp = weaken asn1_weak_parser_kind (p `nondep_then` p') in + //assert (parse_defaultable_injective_cond_prop (generate_defaultable_items itemtwins) rp); + rp) in + //assert (forall id. parse_defaultable_injective_cond_prop (generate_defaultable_items itemtwins) (ret id)); + ret + +let make_asn1_sequence_parser_body_twin_and_then_cases_injective + (itemtwins : list (gen_decorated_parser_twin) {Cons? itemtwins}) +// (pf : (asn1_sequence_k_wf (List.map project_set_decorator itemtwins))) + (ploop : (l : list (gen_decorated_parser_twin) {l << itemtwins}) -> (st : (option asn1_id_t)) -> (asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) l)))) +: Lemma + (requires (forall l. (l << itemtwins) ==> + (and_then_cases_injective_some (ploop l)) /\ + (forall id. parse_defaultable_injective_cond_prop (generate_defaultable_items l) (ploop l (Some id))))) + (ensures (and_then_cases_injective (make_asn1_sequence_parser_body_twin itemtwins ploop))) += let p = make_asn1_sequence_parser_body_twin itemtwins ploop in + and_then_cases_injective_intro p ( + fun id1 id2 b1 b2 -> + match itemtwins with + | [hd] -> + (match hd with + | Mkgendcparser d p fp -> + match d with + | (| s, de, dk |) -> + parse_asn1_sequence_item_twin_cases_injective hd) + | hd :: tl -> + (match hd with + | Mkgendcparser d p fp -> + match d with + | (| s, de, dk |) -> + match (Set.mem id1 s), (Set.mem id2 s) with + | true, true -> + let p1 = parse_asn1_sequence_item_twin hd id1 in + let p2 = parse_asn1_sequence_item_twin hd id2 in + let p = ploop tl None in + parse_asn1_sequence_item_twin_cases_injective hd; + nondep_then_eq p1 p b1; + nondep_then_eq p2 p b2 + | true, false -> + (match de with + | PLAIN -> + let p' = fail_parser asn1_strong_parser_kind (asn1_decorated_t d) in + let p = ploop tl None in + nondep_then_eq p' p b2 + | _ -> + let p1' = parse_asn1_sequence_item_twin hd id1 in + let p1 = ploop tl None in + let p2' = + let defv' = generate_defaultable_item hd in + match defv' with + | Some defv -> + (weaken asn1_strong_parser_kind (parse_ret defv)) in + let p2 = ploop tl (Some id2) in + nondep_then_eq p1' p1 b1; + nondep_then_eq p2' p2 b2; + parse_asn1_sequence_item_twin_nondefault hd id1 b1) + | false, true -> + (match de with + | PLAIN -> + let p' = fail_parser asn1_strong_parser_kind (asn1_decorated_t d) in + let p = ploop tl None in + nondep_then_eq p' p b1 + | _ -> + let p1' = + let defv' = generate_defaultable_item hd in + match defv' with + | Some defv -> + (weaken asn1_strong_parser_kind (parse_ret defv)) in + let p1 = ploop tl (Some id1) in + let p2' = parse_asn1_sequence_item_twin hd id2 in + let p2 = ploop tl None in + nondep_then_eq p1' p1 b1; + nondep_then_eq p2' p2 b2; + parse_asn1_sequence_item_twin_nondefault hd id2 b2) + | false, false -> + match de with + | PLAIN -> + let p' = fail_parser asn1_strong_parser_kind (asn1_decorated_t d) in + let p = ploop tl None in + nondep_then_eq p' p b1; + nondep_then_eq p' p b2 + | _ -> + let p' = + let defv' = generate_defaultable_item hd in + match defv' with + | Some defv -> + (weaken asn1_strong_parser_kind (parse_ret defv)) in + let p1 = ploop tl (Some id1) in + let p2 = ploop tl (Some id2) in + nondep_then_eq p' p1 b1; + nondep_then_eq p' p2 b2; + and_then_cases_injective_some_elim (ploop tl) id1 id2 b1 b2)) + +let make_asn1_sequence_parser_body_twin_spec + (#itemtwins : list (gen_decorated_parser_twin) {Cons? itemtwins}) + (pbodytwin : asn1_id_t -> (asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) itemtwins)))) += (and_then_cases_injective pbodytwin) + /\ (forall (id : asn1_id_t). parse_defaultable_injective_cond_prop (generate_defaultable_items itemtwins) (pbodytwin id)) + +let make_asn1_sequence_parser_body + (#itemtwins : list (gen_decorated_parser_twin) {Cons? itemtwins}) +// (pf : (asn1_sequence_k_wf (List.map project_set_decorator itemtwins))) + (pbodytwin : asn1_id_t -> (asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) itemtwins)))) + (st : option asn1_id_t) + : Pure (asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) itemtwins))) + (requires (make_asn1_sequence_parser_body_twin_spec pbodytwin)) + (ensures (fun p -> parse_defaultable_injective_cond_prop (generate_defaultable_items itemtwins) p)) += let k = glb asn1_strong_parser_kind parse_ret_kind in + let p = + (match st with + | None -> weaken k parse_asn1_identifier_U32 + | Some id -> weaken k (parse_ret id)) in + let _ = + (let ov = generate_defaultable_items itemtwins in + match ov with + | None -> () + | _ -> and_then_defaultable p pbodytwin ov) in + weaken asn1_weak_parser_kind (p `and_then` pbodytwin) + +let make_asn1_sequence_parser_body_and_then_cases_injective + (#itemtwins : list (gen_decorated_parser_twin) {Cons? itemtwins}) +// (pf : (asn1_sequence_k_wf (List.map project_set_decorator itemtwins))) + (pbodytwin : asn1_id_t -> (asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) itemtwins)))) + : Lemma + (requires (make_asn1_sequence_parser_body_twin_spec pbodytwin)) + (ensures (and_then_cases_injective_some (make_asn1_sequence_parser_body pbodytwin))) += and_then_cases_injective_some_intro (make_asn1_sequence_parser_body pbodytwin) + (fun x1 x2 b1 b2 -> + match x1, x2 with + | None, _ -> _ + | _, None -> _ + | Some v1, Some v2 -> + let k = glb asn1_strong_parser_kind parse_ret_kind in + let p' = pbodytwin in + let p1 = weaken k (parse_ret v1) in + and_then_eq p1 p' b1; + let p2 = weaken k (parse_ret v2) in + and_then_eq p2 p' b2; + and_then_cases_injective_elim p' v1 v2 b1 b2) + +let make_asn1_sequence_parser_body_spec + (itemtwins : list (gen_decorated_parser_twin)) + (pbody : (l : list (gen_decorated_parser_twin) {Cons? l /\ (l << itemtwins \/ (l == itemtwins /\ 0 << 1))}) -> (st : option asn1_id_t) -> asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) l))) += match itemtwins with + | [] -> True + | _ -> and_then_cases_injective_some (pbody itemtwins) /\ (forall id. (parse_defaultable_injective_cond_prop (generate_defaultable_items itemtwins) (pbody itemtwins id))) + +let make_asn1_sequence_parser_guard + (itemtwins : list (gen_decorated_parser_twin)) +// (pf : (asn1_sequence_k_wf (List.map project_set_decorator itemtwins))) + (pbody : (l : list (gen_decorated_parser_twin) {Cons? l /\ (l << itemtwins \/ (l == itemtwins /\ 0 << 1))}) -> (st : option asn1_id_t) -> asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) l))) + (st : option asn1_id_t) + : Pure (asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) itemtwins))) + (requires (make_asn1_sequence_parser_body_spec itemtwins pbody)) + (ensures (fun p -> match st with + | Some id -> parse_defaultable_injective_cond_prop (generate_defaultable_items itemtwins) p + | None -> True)) += match itemtwins with + | [] -> (match st with + | None -> weaken asn1_weak_parser_kind (parse_empty) + | Some _ -> fail_parser _ _) + | _ -> (let p = + pbody itemtwins st in + let defv = + match st with + | None -> (generate_defaultable_items itemtwins) + | Some _ -> + let _ = + defaultable_trivial_eq p; + eq_defaultable p (generate_defaultable_items itemtwins) (parse_defaultable None p) in + None in + weaken asn1_weak_parser_kind (parse_defaultable defv p)) + +let make_asn1_sequence_parser_guard_and_then_cases_injective + (itemtwins : list (gen_decorated_parser_twin)) +// (pf : (asn1_sequence_k_wf (List.map project_set_decorator itemtwins))) + (pbody : (l : list (gen_decorated_parser_twin) {Cons? l /\ (l << itemtwins \/ (l == itemtwins /\ 0 << 1))}) -> (st : option asn1_id_t) -> asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) l))) + : Lemma + (requires (make_asn1_sequence_parser_body_spec itemtwins pbody)) + (ensures (and_then_cases_injective_some (make_asn1_sequence_parser_guard itemtwins pbody))) += match itemtwins with + | [] -> _ + | _ -> and_then_cases_injective_some_intro (make_asn1_sequence_parser_guard itemtwins pbody) + (fun x1 x2 b1 b2 -> + match x1, x2 with + | None, _ -> _ + | _, None -> _ + | Some v1, Some v2 -> + let fp = pbody itemtwins in + defaultable_trivial_eq (fp x1); + defaultable_trivial_eq (fp x2); + and_then_cases_injective_some_elim fp v1 v2 b1 b2) + +let rec make_asn1_sequence_parser'' + (itemtwins : list (gen_decorated_parser_twin) {Cons? itemtwins}) +// (pf : (asn1_sequence_k_wf (List.map project_set_decorator itemtwins))) +: Pure (option asn1_id_t -> asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) itemtwins))) + (requires True) + (ensures fun fp -> + (and_then_cases_injective_some fp) /\ + (forall st. parse_defaultable_injective_cond_prop (generate_defaultable_items itemtwins) (fp st))) + (decreases %[itemtwins;0]) += let p = make_asn1_sequence_parser_body_twin itemtwins make_asn1_sequence_parser' in + let _ = + (make_asn1_sequence_parser_body_twin_and_then_cases_injective itemtwins make_asn1_sequence_parser') + in + assert (make_asn1_sequence_parser_body_twin_spec p); + let _ = + (make_asn1_sequence_parser_body_and_then_cases_injective p) + in + make_asn1_sequence_parser_body p + +and make_asn1_sequence_parser' + (itemtwins : list (gen_decorated_parser_twin)) +// (pf : (asn1_sequence_k_wf (List.map project_set_decorator itemtwins))) +: Pure (option asn1_id_t -> asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) itemtwins))) + (requires True) + (ensures fun fp -> + (and_then_cases_injective_some fp) /\ + (forall id. parse_defaultable_injective_cond_prop (generate_defaultable_items itemtwins) (fp (Some id)))) + (decreases %[itemtwins;1]) += assert (make_asn1_sequence_parser_body_spec itemtwins make_asn1_sequence_parser''); + let _ = (make_asn1_sequence_parser_guard_and_then_cases_injective itemtwins make_asn1_sequence_parser'') in + make_asn1_sequence_parser_guard itemtwins (make_asn1_sequence_parser'') + +let make_asn1_sequence_parser + (itemtwins : list (gen_decorated_parser_twin)) +// (pf : (asn1_sequence_k_wf (List.map project_set_decorator itemtwins))) +: Tot (asn1_weak_parser (asn1_sequence_t (List.map (Mkgendcparser?.d) itemtwins))) += make_asn1_sequence_parser' itemtwins None diff --git a/src/ASN1/ASN1.Spec.Set.fst b/src/ASN1/ASN1.Spec.Set.fst new file mode 100644 index 000000000..2befd4863 --- /dev/null +++ b/src/ASN1/ASN1.Spec.Set.fst @@ -0,0 +1,336 @@ +module ASN1.Spec.Set + +module LPL = LowParse.Spec.List +module LPC = LowParse.Spec.Combinators + +let rec gsorted (f: ('a -> 'a -> GTot bool)) (l: list 'a) : GTot bool = + match l with + | [] + | [_] -> true + | x::y::tl -> f x y && gsorted f (y::tl) + +let rec sorted_gsorted // sanity-check + (#a: Type) + (f: (a -> a -> Tot bool)) + (l: list a) +: Lemma + (FStar.List.Tot.sorted f l == gsorted f l) += match l with + | [] -> () + | [_] -> () + | x::y::tl -> sorted_gsorted f (y :: tl) + +let repr_order_prop + (byte_order: (LPC.bytes -> LPC.bytes -> prop)) + (#t: Type) + (#k: LPC.parser_kind) + (p: LPC.parser k t) + (x1 x2: t) +: Tot prop += + (exists b1 . match LPC.parse p b1 with None -> False | Some (x1', _) -> x1' == x1) /\ + (exists b2 . match LPC.parse p b2 with None -> False | Some (x2', _) -> x2' == x2) /\ + (forall b1 b2 . + match LPC.parse p b1, LPC.parse p b2 with + | Some (x1', consumed1), Some (x2', consumed2) -> + (x1' == x1 /\ x2' == x2) ==> + Seq.slice b1 0 consumed1 `byte_order` Seq.slice b2 0 consumed2 + | _ -> True + ) + +let repr_order_spec + (byte_order: (LPC.bytes -> LPC.bytes -> prop)) + (#t: Type) + (#k: LPC.parser_kind) + (p: LPC.parser k t) + (x1 x2: t) +: GTot bool += FStar.StrongExcludedMiddle.strong_excluded_middle (repr_order_prop byte_order p x1 x2) + +let parse_byte_sorted_list_filter + (byte_order: (LPC.bytes -> LPC.bytes -> prop)) + (#t: Type) + (#k: LPC.parser_kind) + (p: LPC.parser k t) + (x: list t) +: GTot bool += gsorted (repr_order_spec byte_order p) x + +let synth_byte_sorted_list + (byte_order: (LPC.bytes -> LPC.bytes -> prop)) + (#t: Type) + (#k: LPC.parser_kind) + (p: LPC.parser k t) + (x: LPC.parse_filter_refine (parse_byte_sorted_list_filter byte_order p)) +: Tot (list t) += x + +let parse_byte_sorted_list + (byte_order: (LPC.bytes -> LPC.bytes -> prop)) + (#t: Type) + (#k: LPC.parser_kind) + (p: LPC.parser k t) +: Tot (LPC.parser (LPC.parse_filter_kind LPL.parse_list_kind) (list t)) += LPL.parse_list p `LPC.parse_filter` parse_byte_sorted_list_filter byte_order p `LPC.parse_synth` synth_byte_sorted_list byte_order p + +module LPT = LowParse.Tot.Base + +let rec tot_parse_byte_sorted_list_aux + (tot_byte_order: (LPC.bytes -> LPC.bytes -> bool)) + (#t: Type) + (#k: LPC.parser_kind) + (p: LPT.parser k t) + (previous_bytes: LPT.bytes) + (b: LPT.bytes) +: Tot (option (list t & LPT.consumed_length b)) + (decreases (Seq.length b)) += if Seq.length b = 0 + then Some ([], 0) + else match p b with + | None -> None + | Some (a, consumed1) -> + if consumed1 = 0 + then None + else + let current_bytes = Seq.slice b 0 consumed1 in + if tot_byte_order previous_bytes current_bytes + then begin + let b' = Seq.slice b consumed1 (Seq.length b) in + match tot_parse_byte_sorted_list_aux tot_byte_order p current_bytes b' with + | None -> None + | Some (q, consumed2) -> Some (a::q, consumed1 + consumed2) + end + else None + +let repr_order_prop_intro + (byte_order: (LPC.bytes -> LPC.bytes -> prop)) + (#t: Type) + (#k: LPC.parser_kind) + (p: LPC.parser k t) + (x1 x2: t) + (b1 b2: LPC.bytes) +: Lemma + (requires ( + match p b1, p b2 with + | Some (x1', consumed1), Some (x2', consumed2) -> + x1' == x1 /\ + x2' == x2 + | _ -> False + )) + (ensures ( + let Some (_, consumed1) = p b1 in + let Some (_, consumed2) = p b2 in + byte_order (Seq.slice b1 0 consumed1) (Seq.slice b2 0 consumed2) <==> repr_order_prop byte_order p x1 x2 + )) += + let Some (_, consumed1) = p b1 in + let Some (_, consumed2) = p b2 in + let prf + (b1' b2' : LPC.bytes) + : Lemma + (match LPC.parse p b1', LPC.parse p b2' with + | Some (x1', consumed1'), Some (x2', consumed2') -> + (x1' == x1 /\ x2' == x2) ==> ( + (Seq.slice b1 0 consumed1 `byte_order` Seq.slice b2 0 consumed2) <==> + (Seq.slice b1' 0 consumed1' `byte_order` Seq.slice b2' 0 consumed2') + ) + | _ -> True) + = match LPC.parse p b1', LPC.parse p b2' with + | Some (x1', _), Some (x2', _) -> + if FStar.StrongExcludedMiddle.strong_excluded_middle (x1' == x1 /\ x2' == x2) + then begin + LPC.parse_injective p b1 b1'; + LPC.parse_injective p b2 b2' + end + else () + | _ -> () + in + Classical.forall_intro_2 prf + +#push-options "--z3rlimit 16" +#restart-solver + +let rec tot_parse_byte_sorted_list_aux_correct + (byte_order: (LPC.bytes -> LPC.bytes -> prop)) + (tot_byte_order: ((x: LPC.bytes) -> (y: LPC.bytes) -> Pure bool True (fun z -> z == true <==> byte_order x y))) + (#t: Type) + (#k: LPC.parser_kind) + (p: LPT.parser k t) + (previous_bytes: LPT.bytes) + (b: LPT.bytes) +: Lemma + (requires ( + k.parser_kind_subkind == Some LPC.ParserStrong /\ + begin match LPC.parse p previous_bytes with + | None -> False + | Some (_, consumed_hd) -> consumed_hd <> 0 /\ consumed_hd == Seq.length previous_bytes + end + )) + (ensures ( + match parse_byte_sorted_list byte_order #_ #k p (previous_bytes `Seq.append` b), tot_parse_byte_sorted_list_aux tot_byte_order p previous_bytes b with + | None, None -> True + | Some (x, consumed), Some (tot_x, tot_consumed) -> + x == fst (Some?.v (LPC.parse p previous_bytes)) :: tot_x /\ + consumed == Seq.length previous_bytes + tot_consumed + | _ -> False + )) + (decreases (Seq.length b)) += let Some (hd, consumed_hd) = p previous_bytes in + let b0 = previous_bytes `Seq.append` b in + LPC.parse_synth_eq + (LPC.parse_filter (LPL.parse_list #k p) (parse_byte_sorted_list_filter byte_order #_ #k p)) + (synth_byte_sorted_list byte_order #_ #k p) + b0; + LPC.parse_filter_eq + (LPL.parse_list #k p) + (parse_byte_sorted_list_filter byte_order #_ #k p) + b0; + LPL.parse_list_eq #k p b0; + LPC.parse_strong_prefix #k p previous_bytes b0; + assert (b `Seq.equal` Seq.slice b0 consumed_hd (Seq.length b0)); + LPL.parse_list_eq #k p b; + LPC.parse_filter_eq + (LPL.parse_list #k p) + (parse_byte_sorted_list_filter byte_order #_ #k p) + b; + LPC.parse_synth_eq + (LPC.parse_filter (LPL.parse_list #k p) (parse_byte_sorted_list_filter byte_order #_ #k p)) + (synth_byte_sorted_list byte_order #_ #k p) + b; + if Seq.length b = 0 + then () + else match p b with + | None -> () + | Some (elt, consumed1) -> + if consumed1 = 0 + then () + else begin + repr_order_prop_intro byte_order #_ #k p hd elt previous_bytes b; + let current_bytes = Seq.slice b 0 consumed1 in + LPC.parse_strong_prefix #k p b current_bytes; + if tot_byte_order previous_bytes current_bytes + then begin + let b' = Seq.slice b consumed1 (Seq.length b) in + assert ((current_bytes `Seq.append` b') `Seq.equal` b); + tot_parse_byte_sorted_list_aux_correct byte_order tot_byte_order p current_bytes b'; + () + end + else () + end + +#pop-options + +let tot_parse_byte_sorted_list_bare + (tot_byte_order: (LPC.bytes -> LPC.bytes -> bool)) + (#t: Type) + (#k: LPC.parser_kind) + (p: LPT.parser k t) + (b: LPT.bytes) +: Tot (option (list t & LPT.consumed_length b)) += if Seq.length b = 0 + then Some ([], 0) + else match p b with + | None -> None + | Some (hd, consumed1) -> + if consumed1 = 0 + then None + else begin + let current_bytes = Seq.slice b 0 consumed1 in + let b' = Seq.slice b consumed1 (Seq.length b) in + match tot_parse_byte_sorted_list_aux tot_byte_order p current_bytes b' with + | None -> None + | Some (tl, consumed2) -> Some (hd::tl, consumed1 + consumed2) + end + +let tot_parse_byte_sorted_list_correct + (byte_order: (LPC.bytes -> LPC.bytes -> prop)) + (tot_byte_order: ((x: LPC.bytes) -> (y: LPC.bytes) -> Pure bool True (fun z -> z == true <==> byte_order x y))) + (#t: Type) + (#k: LPC.parser_kind) + (p: LPT.parser k t {k.parser_kind_subkind == Some LPC.ParserStrong}) + (b: LPT.bytes) +: Lemma + (ensures ( + parse_byte_sorted_list byte_order #_ #k p b == tot_parse_byte_sorted_list_bare tot_byte_order p b + )) += + LPC.parse_synth_eq + (LPC.parse_filter (LPL.parse_list #k p) (parse_byte_sorted_list_filter byte_order #_ #k p)) + (synth_byte_sorted_list byte_order #_ #k p) + b; + LPC.parse_filter_eq + (LPL.parse_list #k p) + (parse_byte_sorted_list_filter byte_order #_ #k p) + b; + LPL.parse_list_eq #k p b; + if Seq.length b = 0 + then () + else match p b with + | None -> () + | Some (hd, consumed1) -> + if consumed1 = 0 + then () + else begin + let current_bytes = Seq.slice b 0 consumed1 in + let b' = Seq.slice b consumed1 (Seq.length b) in + assert (b `Seq.equal` (current_bytes `Seq.append` b')); + LPC.parse_strong_prefix #k p b current_bytes; + tot_parse_byte_sorted_list_aux_correct byte_order tot_byte_order p current_bytes b' + end + +let prop_order_of_bool_order + (#t: Type) + (bool_order: (t -> t -> bool)) + (x1 x2: t) +: Tot prop += bool_order x1 x2 == true + +let tot_parse_byte_sorted_list + (byte_order: ((x: LPC.bytes) -> (y: LPC.bytes) -> bool)) + (#t: Type) + (#k: LPC.parser_kind) + (p: LPT.parser k t) +: Pure (LPT.parser (LPC.parse_filter_kind LPL.parse_list_kind) (list t)) + (requires k.parser_kind_subkind == Some LPC.ParserStrong) + (ensures (fun y -> forall x . y x == LPC.parse (parse_byte_sorted_list (prop_order_of_bool_order byte_order) #_ #k p) x)) += Classical.forall_intro (tot_parse_byte_sorted_list_correct (prop_order_of_bool_order byte_order) (fun x1 x2 -> byte_order x1 x2) p); + let p' = tot_parse_byte_sorted_list_bare (fun x1 x2 -> byte_order x1 x2) p in + LPC.parser_kind_prop_ext + (LPC.parse_filter_kind LPL.parse_list_kind) + (parse_byte_sorted_list (prop_order_of_bool_order byte_order) #_ #k p) + p'; + p' + +module A = ASN1.Base + +let rec lex_byte_order' + (n1: nat) + (b1: Seq.lseq LPC.byte n1) + (n2: nat) + (b2: Seq.lseq LPC.byte n2) +: Tot bool + (decreases (n1 + n2)) += if n1 = 0 + then true + else if n2 = 0 + then false + else + let x1 = Seq.index b1 0 in + let x2 = Seq.index b2 0 in + if x1 `FStar.UInt8.lt` x2 + then true + else if x2 `FStar.UInt8.lt` x1 + then false + else lex_byte_order' (n1 - 1) (Seq.slice b1 1 n1) (n2 - 1) (Seq.slice b2 1 n2) + +let lex_byte_order + (b1: Seq.seq LPC.byte) + (b2: Seq.seq LPC.byte) +: Tot bool += lex_byte_order' (Seq.length b1) b1 (Seq.length b2) b2 + +let parse_asn1_set_of + (#t: Type) + (p: A.asn1_strong_parser t) +: Tot (A.asn1_weak_parser (list t)) += LPT.weaken _ (tot_parse_byte_sorted_list lex_byte_order p) diff --git a/src/ASN1/ASN1.Spec.Time.fst b/src/ASN1/ASN1.Spec.Time.fst new file mode 100644 index 000000000..05f6a6eae --- /dev/null +++ b/src/ASN1/ASN1.Spec.Time.fst @@ -0,0 +1,256 @@ +module ASN1.Spec.Time + +module U32 = FStar.UInt32 +module U8 = FStar.UInt8 +module B = FStar.Bytes +module Cast = FStar.Int.Cast + +let days_of_month + (is_leap : bool) + (mm : U32.t {U32.lte 1ul mm /\ U32.lte mm 12ul}) += if U32.lte mm 6ul then + if U32.lte mm 3ul then + if U32.eq mm 1ul then + 31ul + else if U32.eq mm 2ul then + if is_leap then + 29ul + else + 28ul + else + 31ul + else + if U32.eq mm 4ul then + 30ul + else if U32.eq mm 5ul then + 31ul + else + 30ul + else + if U32.lte mm 9ul then + if U32.eq mm 7ul then + 31ul + else if U32.eq mm 8ul then + 31ul + else + 30ul + else + if U32.eq mm 10ul then + 31ul + else if U32.eq mm 11ul then + 30ul + else + 31ul + +let is_valid_calendar_date2dy + (yy : U32.t) + (mm : U32.t) + (dd : U32.t) += if U32.lte 100ul yy then + false + else + if U32.lt mm 1ul || U32.lt 12ul mm then + false + else begin + let is_leap = (U32.eq (U32.rem yy 4ul) 0ul) in + if (U32.lte 1ul dd) && (U32.lte dd (days_of_month is_leap mm)) then + true + else + false + end + +let is_valid_calendar_date4dy + (yyyy : U32.t) + (mm : U32.t) + (dd : U32.t) += if U32.lte 10000ul yyyy then + false + else + if U32.lt mm 1ul || U32.lt 12ul mm then + false + else begin + let is_leap = (U32.eq (U32.rem yyyy 4ul) 0ul) && ((U32.eq (U32.rem yyyy 100ul) 0ul) = false || (U32.eq (U32.rem yyyy 400ul) 0ul)) in + if (U32.lte 1ul dd) && (U32.lte dd (days_of_month is_leap mm)) then + true + else + false + end + +let is_valid_time_hh + (hh : U32.t) += (U32.lte hh 23ul) + +let is_valid_time_hhmm + (hh : U32.t) + (mm : U32.t) += (U32.lte hh 23ul) && (U32.lte mm 59ul) + +let is_valid_time_hhmmss + (hh mm ss : U32.t) += (U32.lte hh 23ul) && (U32.lte mm 59ul) && (U32.lte ss 59ul) + +let is_valid_utc_positive_timezone + (hh mm : U32.t) += (U32.lte hh 12ul) && (U32.lte 1ul hh) && ((U32.eq mm 0ul) || (U32.eq mm 30ul && (U32.eq hh 9ul || U32.eq hh 3ul))) + +let is_valid_utc_negative_timezone + (hh mm : U32.t) += (U32.lte hh 14ul) && (U32.lte 1ul hh) && + ((U32.eq mm 0ul) || + (U32.eq mm 30ul && ((U32.lte 3ul hh && U32.lte hh 6ul) || (U32.eq hh 9ul) || (U32.eq hh 10ul))) || + (U32.eq mm 45ul && ((U32.eq hh 5ul) || (U32.eq hh 8ul) || (U32.eq hh 12ul)))) + +let is_valid_digit + (ch : U8.t) += U8.lte 48uy ch && U8.lte ch 57uy + +let read_digit + (ch : U8.t) +: Pure U32.t + (requires is_valid_digit ch) + (ensures fun d -> U32.lte 0ul d /\ U32.lte d 9ul) += let d = U8.sub ch 48uy in + Cast.uint8_to_uint32 d + +let read_2dnum + (ch1 : U8.t {is_valid_digit ch1}) + (ch2 : U8.t {is_valid_digit ch2}) += (U32.add (U32.mul (read_digit ch1) 10ul) (read_digit ch2)) + +let read_4dnum + (ch1 : U8.t {is_valid_digit ch1}) + (ch2 : U8.t {is_valid_digit ch2}) + (ch3 : U8.t {is_valid_digit ch3}) + (ch4 : U8.t {is_valid_digit ch4}) += (U32.add (U32.mul (U32.add (U32.mul (U32.add (U32.mul (read_digit ch1) 10ul) (read_digit ch2)) 10ul) (read_digit ch3)) 10ul) (read_digit ch4)) + +let rec is_valid_digit_range + (b : B.bytes) + (s : U32.t) + (l : U32.t {U32.v s + U32.v l <= (B.length b)}) +: Tot bool (decreases (U32.v l)) += if l = 0ul then + true + else + let l' = U32.sub l 1ul in + is_valid_digit (B.index b (U32.v (U32.add s l'))) && + is_valid_digit_range b s l' + +#push-options "--fuel 8 --split_queries always" + +let is_valid_yymmdd + (b : B.bytes) + (s : U32.t {U32.v s + 6 <= B.length b}) += if (is_valid_digit_range b s 6ul) then begin + let yy = read_2dnum (B.index b (U32.v s)) (B.index b (U32.v s + 1)) in + let mm = read_2dnum (B.index b (U32.v s + 2)) (B.index b (U32.v s + 3)) in + let dd = read_2dnum (B.index b (U32.v s + 4)) (B.index b (U32.v s + 5)) in + is_valid_calendar_date2dy yy mm dd + end + else + false + +let is_valid_yyyymmdd + (b : B.bytes) + (s : U32.t {U32.v s + 8 <= B.length b}) += if (is_valid_digit_range b s 8ul) then begin + let yyyy = read_4dnum (B.index b (U32.v s)) (B.index b (U32.v s + 1)) (B.index b (U32.v s + 2)) (B.index b (U32.v s + 3)) in + let mm = read_2dnum (B.index b (U32.v s + 4)) (B.index b (U32.v s + 5)) in + let dd = read_2dnum (B.index b (U32.v s + 6)) (B.index b (U32.v s + 7)) in + is_valid_calendar_date4dy yyyy mm dd + end + else + false + +let is_valid_hh + (b : B.bytes) + (s : U32.t {U32.v s + 2 <= B.length b}) += if (is_valid_digit_range b s 2ul) then begin + let hh = read_2dnum (B.index b (U32.v s)) (B.index b (U32.v s + 1)) in + is_valid_time_hh hh + end + else + false + +let is_valid_hhmm + (b : B.bytes) + (s : U32.t {U32.v s + 4 <= B.length b}) += if (is_valid_digit_range b s 4ul) then begin + let hh = read_2dnum (B.index b (U32.v s)) (B.index b (U32.v s + 1)) in + let mm = read_2dnum (B.index b (U32.v s + 2)) (B.index b (U32.v s + 3)) in + is_valid_time_hhmm hh mm + end + else + false + +let is_valid_hhmmss + (b : B.bytes) + (s : U32.t {U32.v s + 6 <= B.length b}) += if (is_valid_digit_range b s 6ul) then begin + let hh = read_2dnum (B.index b (U32.v s)) (B.index b (U32.v s + 1)) in + let mm = read_2dnum (B.index b (U32.v s + 2)) (B.index b (U32.v s + 3)) in + let ss = read_2dnum (B.index b (U32.v s + 4)) (B.index b (U32.v s + 5)) in + is_valid_time_hhmmss hh mm ss + end + else + false + +let is_valid_utc_timezone + (b : B.bytes) + (s : U32.t {U32.v s + 5 <= B.length b}) += if is_valid_digit_range b (U32.add s 1ul) 4ul then + let hh = read_2dnum (B.index b (U32.v s + 1)) (B.index b (U32.v s + 2)) in + let mm = read_2dnum (B.index b (U32.v s + 3)) (B.index b (U32.v s + 4)) in + let sign = B.index b (U32.v s) in + if (sign = 43uy) then // + + is_valid_utc_positive_timezone hh mm + else if (sign = 45uy) then // - + is_valid_utc_negative_timezone hh mm + else + false + else + false + +let is_valid_ASN1UTCTIME + (b : B.bytes) += let len = B.length b in + if len = 11 then + is_valid_yymmdd b 0ul && is_valid_hhmm b 6ul && B.index b 10 = 90uy + else if len = 15 then + is_valid_yymmdd b 0ul && is_valid_hhmm b 6ul && is_valid_utc_timezone b 10ul + else if len = 13 then + is_valid_yymmdd b 0ul && is_valid_hhmmss b 6ul && B.index b 12 = 90uy + else if len = 17 then + is_valid_yymmdd b 0ul && is_valid_hhmmss b 6ul && is_valid_utc_timezone b 12ul + else + false + +let is_valid_localtime + (b : B.bytes) + (len : nat {len <= B.length b}) += if len = 10 then + is_valid_yyyymmdd b 0ul && is_valid_hh b 8ul + else if len = 12 then + is_valid_yyyymmdd b 0ul && is_valid_hhmm b 8ul + else if len = 14 then + is_valid_yyyymmdd b 0ul && is_valid_hhmmss b 8ul + else if 15 < len && len <= 18 then + is_valid_yyyymmdd b 0ul && is_valid_hhmmss b 8ul && (B.index b 14 = 46uy) && (B.index b (len - 1) <> 48uy) + else + false + +let is_valid_ASN1GENERALIZEDTIME + (b : B.bytes) += let len = B.length b in + if len < 10 || len > 23 then + false + else + if (B.index b (len - 1) = 90uy) then + is_valid_localtime b (len - 1) + else if (is_valid_digit (B.index b (len - 5))) then + is_valid_localtime b (len - 5) && is_valid_utc_timezone b (UInt32.uint_to_t (len - 5)) + else + is_valid_localtime b len + +#pop-options + diff --git a/src/ASN1/ASN1.Syntax.fst b/src/ASN1/ASN1.Syntax.fst new file mode 100644 index 000000000..7defc2e05 --- /dev/null +++ b/src/ASN1/ASN1.Syntax.fst @@ -0,0 +1,211 @@ +module ASN1.Syntax +open ASN1.Base + +module U32 = FStar.UInt32 +module List = FStar.List.Tot +module T = FStar.Tactics + +// for identifiers + +let mk_constant_id (x : UInt.uint_t 32) : asn1_id_t += MK_ASN1_ID UNIVERSAL PRIMITIVE (U32.uint_to_t x) + +let mk_constant_constructed_id (x : UInt.uint_t 32) : asn1_id_t += MK_ASN1_ID UNIVERSAL CONSTRUCTED (U32.uint_to_t x) + +let mk_custom_id (c : asn1_id_class_t) (f : asn1_id_flag_t) (x : UInt.uint_t 32) : asn1_id_t += MK_ASN1_ID c f (U32.uint_to_t x) + +// for OID definitions + +let mk_oid (l : list (UInt.uint_t 32)) : Pure (asn1_oid_t) + (requires asn1_OID_wf (List.map U32.uint_to_t l)) + (ensures fun _ -> True) += List.map U32.uint_to_t l +let mk_oid_app (oid : asn1_oid_t) (x : UInt.uint_t 32) : asn1_oid_t += norm [zeta;iota;delta;primops;nbe] (List.append oid [U32.uint_to_t x]) +let op_Slash_Plus = mk_oid_app + +// for choices + +let is_ILC (#s) (x : asn1_k s) += match x with + | ASN1_ILC _ _ -> true + | _ -> false + +let proj_identifier (#s) (x : asn1_k s {is_ILC x}) += match x with + | ASN1_ILC id c -> id + +let proj_content (#s) (x : asn1_k s {is_ILC x}) += match x with + | ASN1_ILC id c -> c + +let mk_prefixed (id : asn1_id_t) (#s) (x : asn1_k s) += ASN1_ILC id (ASN1_PREFIXED x) + +let mk_retagged (id : asn1_id_t) (#s) (x : asn1_k s {is_ILC x}) += ASN1_ILC id (proj_content x) + +let op_Star_Hat (name : string) (#t) (x : t) += x + +let op_Hat_Star (name : string) (#s) (x : asn1_k s {is_ILC x}) : (asn1_id_t * asn1_content_k) += match x with + | ASN1_ILC id c -> (id, c) + +noextract +let choice_tac () = T.norm [iota;zeta;delta;primops]; T.trefl () + +let asn1_choice (ls : list (asn1_id_t * asn1_content_k)) (pf : squash (List.noRepeats (List.map fst ls) )) += ASN1_CHOICE_ILC ls pf + +// for sequences + +let is_terminal (#s) (x : asn1_k s) += if is_ILC x then + match (proj_content x) with + | ASN1_TERMINAL _ -> true + | _ -> false + else + false + +let proj_terminal_k (#s) (x : asn1_k s {is_terminal x}) += match proj_content x with + | ASN1_TERMINAL tk -> tk + +let proj_terminal_t (#s) (x : asn1_k s {is_terminal x}) += match proj_content x with + | ASN1_TERMINAL tk -> asn1_terminal_t tk + +let mk_default_field (#s) (x : asn1_k s {is_terminal x}) (v : proj_terminal_t x) += ASN1_DEFAULT_TERMINAL (proj_identifier x) #(proj_terminal_k x) v + +let mk_restricted_default_field (#s) (x : asn1_k s {is_terminal x}) (f : proj_terminal_t x -> bool) (v : proj_terminal_t x {f v}) += ASN1_DEFAULT_RESTRICTED_TERMINAL (proj_identifier x) #(proj_terminal_k x) f v + +let mk_restricted_field (#s) (x : asn1_k s {is_terminal x}) (f : proj_terminal_t x -> bool) += ASN1_ILC (proj_identifier x) (ASN1_RESTRICTED_TERMINAL (proj_terminal_k x) f) + +let field_type (s : Set.set asn1_id_t) (d : asn1_decorator) += match d with + | DEFAULT -> asn1_decorated_k s d + | _ -> asn1_k s + +let op_Hat_Colon (#s : Set.set asn1_id_t) + (d : asn1_decorator) + (f : field_type s d) +: asn1_gen_item_k += match d with + | DEFAULT -> mk_ASN1_GEN_ITEM #s #d f + | PLAIN -> mk_ASN1_GEN_ITEM (ASN1_PLAIN_ILC #s f) + | OPTION -> mk_ASN1_GEN_ITEM (ASN1_OPTION_ILC #s f) + +noextract +let seq_tac () = T.norm[iota;zeta;delta;primops;simplify]; T.smt() + +let mk_gen_items (items : list asn1_gen_item_k) (pf : squash (asn1_sequence_k_wf (List.map proj2_of_3 items))) +: asn1_gen_items_lk += k_as_lk (| items, pf |) + +// constants + +let boolean_id = mk_constant_id 1 + +let asn1_boolean = ASN1_ILC boolean_id (ASN1_TERMINAL ASN1_BOOLEAN) + +let integer_id = mk_constant_id 2 + +let asn1_bounded_integer (bound : pos) = ASN1_ILC integer_id (ASN1_TERMINAL (ASN1_INTEGER bound)) + +let asn1_integer = asn1_bounded_integer 20 + +let bitstring_id = mk_constant_id 3 + +let asn1_bitstring = ASN1_ILC bitstring_id (ASN1_TERMINAL ASN1_BITSTRING) + +let octetstring_id = mk_constant_id 4 + +let asn1_octetstring = ASN1_ILC octetstring_id (ASN1_TERMINAL ASN1_OCTETSTRING) + +let null_id = mk_constant_id 5 + +let asn1_null = ASN1_ILC null_id (ASN1_TERMINAL ASN1_NULL) + +let oid_id = mk_constant_id 6 + +let asn1_oid = ASN1_ILC oid_id (ASN1_TERMINAL ASN1_OID) + +let utf8string_id = mk_constant_id 12 + +let asn1_utf8string = ASN1_ILC utf8string_id (ASN1_TERMINAL ASN1_UTF8STRING) + +let sequence_id = mk_constant_constructed_id 16 + +let asn1_sequence (items : list asn1_gen_item_k) (pf : squash (asn1_sequence_k_wf (List.map proj2_of_3 items))) += ASN1_ILC sequence_id (ASN1_SEQUENCE (k_as_lk (| items, pf |) )) + +let asn1_sequence_of (#s) (item : asn1_k s) = ASN1_ILC sequence_id (ASN1_SEQUENCE_OF item) + +let set_id = mk_constant_constructed_id 17 + +let asn1_set_of (#s) (item : asn1_k s) = ASN1_ILC set_id (ASN1_SET_OF item) + +let printablestring_id = mk_constant_id 19 + +let asn1_printablestring = ASN1_ILC printablestring_id (ASN1_TERMINAL ASN1_PRINTABLESTRING) + +let teletexstring_id = mk_constant_id 20 + +let asn1_teletexstring = ASN1_ILC teletexstring_id (ASN1_TERMINAL ASN1_OCTETSTRING) + +let ia5string_id = mk_constant_id 22 + +let asn1_ia5string = ASN1_ILC ia5string_id (ASN1_TERMINAL ASN1_IA5STRING) + +let utctime_id = mk_constant_id 23 + +let asn1_utctime = ASN1_ILC utctime_id (ASN1_TERMINAL ASN1_UTCTIME) + +let generalizedtime_id = mk_constant_id 24 + +let asn1_generalizedtime = ASN1_ILC generalizedtime_id (ASN1_TERMINAL ASN1_GENERALIZEDTIME) + +let visiblestring_id = mk_constant_id 26 + +let asn1_visiblestring = ASN1_ILC visiblestring_id (ASN1_TERMINAL ASN1_OCTETSTRING) + +let universalstring_id = mk_constant_id 28 + +let asn1_universalstring = ASN1_ILC universalstring_id (ASN1_TERMINAL ASN1_OCTETSTRING) + +let bMPstring_id = mk_constant_id 30 + +let asn1_bMPstring = ASN1_ILC bMPstring_id (ASN1_TERMINAL ASN1_OCTETSTRING) + +// for any + +let asn1_any = ASN1_ANY_ILC + +let asn1_any_oid_prefix + (prefix : list asn1_gen_item_k) + (name : string) + (supported : list (asn1_oid_t * asn1_gen_items_lk)) + (pf_wf : squash (asn1_any_prefix_k_wf (Set.singleton oid_id) (List.map proj2_of_3 prefix))) + (pf_sup : squash (List.noRepeats (List.map fst supported))) += ASN1_ILC sequence_id (ASN1_ANY_DEFINED_BY _ (list_as_l prefix) oid_id ASN1_OID supported None pf_wf pf_sup) + +let asn1_any_oid + (name : string) + (supported : list (asn1_oid_t * asn1_gen_items_lk)) + (pf_wf : squash (asn1_any_prefix_k_wf (Set.singleton oid_id) (List.map proj2_of_3 []))) + (pf_sup : squash (List.noRepeats (List.map fst supported))) += ASN1_ILC sequence_id (ASN1_ANY_DEFINED_BY _ (list_as_l []) oid_id ASN1_OID supported None pf_wf pf_sup) + +let asn1_any_oid_with_fallback + (name : string) + (supported : list (asn1_oid_t * asn1_gen_items_lk)) + (fallback : asn1_gen_items_lk) + (pf_wf : squash (asn1_any_prefix_k_wf (Set.singleton oid_id) (List.map proj2_of_3 []))) + (pf_sup : squash (List.noRepeats (List.map fst supported))) += ASN1_ILC sequence_id (ASN1_ANY_DEFINED_BY _ (list_as_l []) oid_id ASN1_OID supported (Some fallback) pf_wf pf_sup) diff --git a/src/ASN1/ASN1.Test.Interpreter.fst b/src/ASN1/ASN1.Test.Interpreter.fst new file mode 100644 index 000000000..fef7e80cc --- /dev/null +++ b/src/ASN1/ASN1.Test.Interpreter.fst @@ -0,0 +1,30 @@ +module ASN1.Test.Interpreter + +open ASN1.Spec.Interpreter + +open LowParse.Spec.Base + +let bool_id : asn1_id_t = MK_ASN1_ID UNIVERSAL PRIMITIVE 5ul + +// This sample type is not well-formed +// +// let sigAlg (seq_id:_) : asn1_k _ = +// ASN1_ILC seq_id ( +// ASN1_SEQUENCE +// [ASN1_ILC bool_id (ASN1_TERMINAL ASN1_BOOLEAN); +// ASN1_ILC bool_id (ASN1_TERMINAL ASN1_BOOLEAN)] +// (admit()) +// ) + +let seq_id : asn1_id_t = MK_ASN1_ID UNIVERSAL CONSTRUCTED 16ul + +let sigAlg : asn1_k (Set.singleton seq_id) = + ASN1_ILC seq_id ( + ASN1_SEQUENCE + [ mk_ASN1_GEN_ITEM (ASN1_PLAIN_ILC (ASN1_ILC bool_id (ASN1_TERMINAL ASN1_BOOLEAN))) ; + mk_ASN1_GEN_ITEM (ASN1_OPTION_ILC (ASN1_ILC bool_id (ASN1_TERMINAL ASN1_BOOLEAN))) ] + _ ) + + +let sigAlg_parser : parser _ (asn1_t (sigAlg)) = + asn1_as_parser sigAlg diff --git a/src/ASN1/ASN1.X509.fst b/src/ASN1/ASN1.X509.fst new file mode 100644 index 000000000..3e1d42b80 --- /dev/null +++ b/src/ASN1/ASN1.X509.fst @@ -0,0 +1,608 @@ +module ASN1.X509 + +module U32 = FStar.UInt32 +module List = FStar.List.Tot + +open ASN1.Base +open ASN1.Syntax + +(* Known looseness from rfc5280 section-4: + . Enforcing version must be 2 (v3) + . Enforcing extensions must appear + . Enforced by crypto: the algorithmIdentifier in the signature, subjectPublicKeyInfo, and signatureAlgorithm being consistent + . Not parsing extension value field of extensions + . TODO: A list of constants for Name + . Backward compatibility for Name + . Only support algorithms and extensions included in the list +*) + +// TODO: insert list of supported algorithms here + +let pkcs_1 = mk_oid [1;2;840;113549;1;1] + +let rSAEncryption_oid = pkcs_1 /+ 1 + +let md2WithRSAEncryption_oid = mk_oid [1;2;840;113549;1;1;2] + +let md5WithRSAEncryption_oid = mk_oid [1;2;840;113549;1;1;4] + +let sha_1WithRSAEncryption_oid = mk_oid [1;2;840;113549;1;1;5] + +let sha256WithRSAEncryption_oid = mk_oid [1;2;840;113549;1;1;11] + +let sha384WithRSAEncryption_oid = pkcs_1 /+ 12 + +let sha512WithRSAEncryption_oid = pkcs_1 /+ 13 + +let sha224WithRSAEncryption_oid = pkcs_1 /+ 14 + +let option_NULL_parameters += mk_gen_items ["parameters" *^ (OPTION ^: asn1_null)] (_ by (seq_tac ())) + +let id_dsa_with_sha1 = mk_oid [1;2;840;10040;4;3] + +let omitted_parameters += mk_gen_items ["parameters" *^ (DEFAULT ^: mk_default_field asn1_null ())] (_ by (seq_tac ())) + +let ansi_X9_62 = mk_oid [1;2;840;10045] + +let id_ecSigType = ansi_X9_62 /+ 4 + +let id_ecdsa_with_sha1 = id_ecSigType /+ 1 + +let id_dsa = mk_oid [1;2;840;10040;4;1] + +let dss_parms += asn1_sequence + ["p" *^ (PLAIN ^: asn1_bounded_integer 200); + "q" *^ (PLAIN ^: asn1_bounded_integer 200); + "g" *^ (PLAIN ^: asn1_bounded_integer 200)] + (_ by (seq_tac ())) + +let dss_parms_field += mk_gen_items ["parameters" *^ (OPTION ^: dss_parms)] (_ by (seq_tac ())) + +let dhpublicnumber = mk_oid [1;2;840;10046;2;1] + +let validationParms += asn1_sequence + ["seed" *^ (PLAIN ^: asn1_bitstring); + "pgenCounter" *^ (PLAIN ^: asn1_integer)] + (_ by (seq_tac ())) + +let domainParameters += asn1_sequence + ["p" *^ (PLAIN ^: asn1_integer); + "g" *^ (PLAIN ^: asn1_integer); + "q" *^ (PLAIN ^: asn1_integer); + "j" *^ (OPTION ^: asn1_integer); + "validationParms" *^ (OPTION ^: validationParms)] + (_ by (seq_tac ())) + +let domainParameters_field += mk_gen_items ["parameters" *^ (PLAIN ^: domainParameters)] (_ by (seq_tac ())) + +let id_keyExchangeAlgorithm = mk_oid [2;16;840;1;101;2;1;1;22] + +let kEA_Parms_Id_field += mk_gen_items ["parameters" *^ (PLAIN ^: asn1_octetstring)] (_ by (seq_tac ())) + +let id_public_key_type = ansi_X9_62 /+ 2 + +let id_ecPublicKey = id_public_key_type /+ 1 + +let fieldElement = asn1_octetstring + +let curve += asn1_sequence + ["a" *^ (PLAIN ^: fieldElement); + "b" *^ (PLAIN ^: fieldElement); + "seed" *^ (OPTION ^: asn1_bitstring)] + (_ by (seq_tac ())) + +let eCPVer += mk_restricted_field asn1_integer (fun x -> x = 1) + +let id_fieldType = ansi_X9_62 /+ 1 + +let prime_field_oid = id_fieldType /+ 1 + +let prime_p += mk_gen_items ["parameters" *^ (PLAIN ^: asn1_integer)] (_ by (seq_tac ())) + +let characteristic_two_field_oid = id_fieldType /+ 2 + +let id_characteristic_two_basis = characteristic_two_field_oid /+ 1 + +let gnBasis_oid = id_characteristic_two_basis /+ 1 + +let gnBasis_parameters += mk_gen_items [PLAIN ^: asn1_null] (_ by (seq_tac ())) + +let tpBasis_oid = id_characteristic_two_basis /+ 2 + +let tpBasis_parameters += mk_gen_items ["Trinomial" *^ (PLAIN ^: asn1_integer)] (_ by (seq_tac ())) + +let ppBasis_oid = id_characteristic_two_basis /+ 3 + +let pentanomial += asn1_sequence [ + "k1" *^ (PLAIN ^: asn1_integer); + "k2" *^ (PLAIN ^: asn1_integer); + "k3" *^ (PLAIN ^: asn1_integer)] + (_ by (seq_tac ())) + +let ppBasis_parameters += mk_gen_items ["Pentanomial" *^ (PLAIN ^: pentanomial)] (_ by (seq_tac ())) + +let characteristic_two += asn1_any_oid_prefix + ["m" *^ (PLAIN ^: asn1_integer)] + "basis" + [(gnBasis_oid, gnBasis_parameters); + (tpBasis_oid, tpBasis_parameters); + (ppBasis_oid, ppBasis_parameters)] + (_ by (seq_tac ())) + (_ by (choice_tac ())) + +let characteristic_two_field += mk_gen_items ["parameters" *^ (PLAIN ^: characteristic_two)] (_ by (seq_tac ())) + +let fieldID += asn1_any_oid + "fieldType" + [(prime_field_oid, prime_p); + (characteristic_two_field_oid, characteristic_two_field)] + (_ by (seq_tac ())) + (_ by (choice_tac ())) + +let eCParameters += asn1_sequence [ + "version" *^ (PLAIN ^: eCPVer); + "fieldID" *^ (PLAIN ^: fieldID); + "curve" *^ (PLAIN ^: curve); + "base" *^ (PLAIN ^: asn1_octetstring); + "order" *^ (PLAIN ^: asn1_integer); + "cofactor" *^ (OPTION ^: asn1_integer)] + (_ by (seq_tac ())) + +// Warning: Not filtering with the list of named curves + +let ecpkParameters += asn1_choice [ + "ecParameters" ^* eCParameters; + "namedCurve" ^* asn1_oid; + "implicitlyCA" ^* asn1_null] + (_ by (choice_tac ())) + +let ecpkParameters_field += mk_gen_items ["parameters" *^ (PLAIN ^: ecpkParameters)] (_ by (seq_tac ())) + +let supported_algorithms += [(rSAEncryption_oid, option_NULL_parameters); + (md2WithRSAEncryption_oid, option_NULL_parameters); + (md5WithRSAEncryption_oid, option_NULL_parameters); + (sha_1WithRSAEncryption_oid, option_NULL_parameters); + (sha256WithRSAEncryption_oid, option_NULL_parameters); + (sha384WithRSAEncryption_oid, option_NULL_parameters); + (sha512WithRSAEncryption_oid, option_NULL_parameters); + (sha224WithRSAEncryption_oid, option_NULL_parameters); + (id_dsa_with_sha1, omitted_parameters); + (id_ecdsa_with_sha1, omitted_parameters); + (id_dsa, dss_parms_field); + (dhpublicnumber, domainParameters_field); + (id_keyExchangeAlgorithm, kEA_Parms_Id_field); + (id_ecPublicKey, ecpkParameters_field)] + +let version += mk_restricted_field asn1_integer (fun x -> x = 2) + +let certificateSerialNumber = asn1_integer + +let ignoreUnknownCrypto += mk_gen_items [OPTION ^: asn1_any] (_ by (seq_tac ())) + +let algorithmIdentifier += asn1_any_oid_with_fallback + "algorithm" + supported_algorithms + ignoreUnknownCrypto + (_ by (seq_tac ())) + (_ by (choice_tac ())) + +let attributeType = asn1_oid + +//Warning: backward compatibility + +//Warning: a list of constants might be needed here + +let directoryString += asn1_choice [ + "telextexString" ^* asn1_teletexstring; + "printableString" ^* asn1_printablestring; + "universalString" ^* asn1_universalstring; + "utf8String" ^* asn1_utf8string; + "bmpString" ^* asn1_bMPstring] + (_ by (choice_tac ())) + +let attributeValue = directoryString + +let attributeValue_field += mk_gen_items ["value" *^ (PLAIN ^: attributeValue)] (_ by (seq_tac ())) + +let emailAddress_oid = mk_oid [1;2;840;113549;1;9;1] + +let emailAddress_field += mk_gen_items ["value" *^ (PLAIN ^: asn1_ia5string)] (_ by (seq_tac ())) + +let unstructuredName_oid = mk_oid [1;2;840;113549;1;9;2] + +let pKCS9String += asn1_choice [ + "telextexString" ^* asn1_teletexstring; + "printableString" ^* asn1_printablestring; + "universalString" ^* asn1_universalstring; + "utf8String" ^* asn1_utf8string; + "bmpString" ^* asn1_bMPstring; + "ia5String" ^* asn1_ia5string] + (_ by (choice_tac ())) + +let unstructuredName_field += mk_gen_items ["id" *^ (PLAIN ^: pKCS9String)] (_ by (seq_tac ())) + +let domainComponent_oid = mk_oid [0;9;2342;19200300;100;1;25] + +let domainComponent_field += mk_gen_items ["value" *^ (PLAIN ^: asn1_ia5string)] (_ by (seq_tac ())) + +let attributeTypeAndValue += asn1_any_oid_with_fallback + "type" + [(emailAddress_oid, emailAddress_field); + (domainComponent_oid, domainComponent_field); + (unstructuredName_oid, unstructuredName_field)] + attributeValue_field + (_ by (seq_tac ())) + (_ by (choice_tac ())) + +let relativeDistinguishedName += asn1_set_of attributeTypeAndValue + +let rDNSequence += asn1_sequence_of relativeDistinguishedName + +let name += asn1_choice ["rsnSequence" ^* rDNSequence] (_ by (choice_tac ())) + +let time += asn1_choice [ + "utcTime" ^* asn1_utctime; + "generalTime" ^* asn1_generalizedtime] + (_ by (choice_tac ())) + +let validity += asn1_sequence [ + "notBefore" *^ (PLAIN ^: time); + "notAfter" *^ (PLAIN ^: time)] + (_ by (seq_tac ())) + +let subjectPublicKeyInfo += asn1_sequence [ + "algorithm" *^ (PLAIN ^: algorithmIdentifier); + "subjectPublicKey" *^ (PLAIN ^: asn1_bitstring)] + (_ by (seq_tac ())) + +let uniqueIdentifier = asn1_bitstring + +let id_pkix = mk_oid [1;3;6;1;5;5;7] + +let id_pe = id_pkix /+ 1 + +let id_pe_authorityInformationAccess = id_pe /+ 1 + +//Warning: Partly using the mitls spec which is loosened from rfc5280 + +let mk_expansion (critical : asn1_gen_item_k) (#s : _) (value : asn1_k s) + (pf : squash (asn1_sequence_k_wf [proj2_of_3 critical; (Set.singleton octetstring_id, PLAIN)])) += let items = [critical; "extnValue" *^ (PLAIN ^: (ASN1_ILC octetstring_id (ASN1_PREFIXED value)))] in + mk_gen_items items pf + +let critical_field += mk_default_field asn1_boolean false + +let critical_field_MUST_false += mk_restricted_default_field asn1_boolean (fun b -> b = false) false + +let critical_field_MUST_true += mk_restricted_field asn1_boolean (fun b -> b = true) + +let id_pe_ipAddrBlocks = id_pe /+ 7 + +//Can be refined + +let iPAddress = asn1_bitstring + +let iPAddressRange += asn1_sequence [ + "min" *^ (PLAIN ^: iPAddress); + "max" *^ (PLAIN ^: iPAddress)] + (_ by (seq_tac ())) + +let iPAddressOrRange += asn1_choice [ + "addressPrefix" ^* iPAddress; + "addressRange" ^* iPAddressRange] + (_ by (choice_tac ())) + +let iPAddressChoice += asn1_choice [ + "inherit" ^* asn1_null; + "addressesOrRanges" ^* (asn1_sequence_of iPAddressOrRange)] + (_ by (choice_tac ())) + +let iPAddressFamily += asn1_sequence [ + "addressFamily" *^ (PLAIN ^: (mk_restricted_field asn1_octetstring (fun s -> let l = Bytes.length s in 2 <= l && l <= 3))); + "ipAddressChoice" *^ (PLAIN ^: iPAddressChoice)] + (_ by (seq_tac ())) + +let iPAddrBlocks += asn1_sequence_of iPAddressFamily + +let iPAddrBlocks_expansion += mk_expansion (DEFAULT ^: critical_field) iPAddrBlocks (_ by (seq_tac ())) + +let id_ad = id_pkix /+ 48 + +let id_ad_caIssuers = id_ad /+ 1 + +let id_ad_ocsp = id_ad /+ 2 + +let accessMethod += mk_restricted_field asn1_oid (fun oid -> oid = id_ad_caIssuers || oid = id_ad_ocsp) + +let otherName += asn1_any_oid_with_fallback + "type-id" + [] + (mk_gen_items ["value" *^ (PLAIN ^: (mk_prefixed (mk_custom_id CONTEXT_SPECIFIC CONSTRUCTED 0) asn1_any))] (_ by (seq_tac ()))) + (_ by (seq_tac ())) + (_ by (choice_tac ())) + +let generalName += asn1_choice [ + "otherName" ^* (mk_retagged (mk_custom_id CONTEXT_SPECIFIC CONSTRUCTED 0) otherName); + "rfc822Name" ^* (mk_retagged (mk_custom_id CONTEXT_SPECIFIC PRIMITIVE 1) asn1_ia5string); + "dNSName" ^* (mk_retagged (mk_custom_id CONTEXT_SPECIFIC PRIMITIVE 2) asn1_ia5string); + "directoryName" ^* (mk_prefixed (mk_custom_id CONTEXT_SPECIFIC CONSTRUCTED 4) name); + "uniformResourceIdentifier" ^* (mk_retagged (mk_custom_id CONTEXT_SPECIFIC PRIMITIVE 6) asn1_ia5string); + "iPAddress" ^* (mk_retagged (mk_custom_id CONTEXT_SPECIFIC PRIMITIVE 7) asn1_octetstring); + "registeredID" ^* (mk_retagged (mk_custom_id CONTEXT_SPECIFIC PRIMITIVE 8) asn1_oid)] + (_ by (choice_tac ())) + +let generalNames = asn1_sequence_of generalName + +let accessDescription += asn1_sequence [ + "accessMethod" *^ (PLAIN ^: asn1_oid); + "accessLocation" *^ (PLAIN ^: generalName)] + (_ by (seq_tac ())) + +let authorityInfoAccessSyntax = asn1_sequence_of accessDescription + +let authorityInformationAccess_expansion += mk_expansion (DEFAULT ^: critical_field_MUST_false) authorityInfoAccessSyntax (_ by (seq_tac ())) + +let id_ce = mk_oid [2;5;29] + +let id_ce_subjectKeyIdentifier = id_ce /+ 14 + +let keyIdentifier = asn1_octetstring + +let subjectKeyIdentifier = keyIdentifier + +let subjectKeyIdentifier_expansion += mk_expansion (DEFAULT ^: critical_field_MUST_false) subjectKeyIdentifier (_ by (seq_tac ())) + +let id_ce_keyUsage = id_ce /+ 15 + +let keyUsage = asn1_bitstring + +let keyUsage_expansion += mk_expansion (DEFAULT ^: critical_field) keyUsage (_ by (seq_tac ())) + +let id_ce_subjectAlternativeName = id_ce /+ 17 + +let subjectAlternativeName_expansion += mk_expansion (DEFAULT ^: critical_field) generalNames (_ by (seq_tac ())) + +let id_ce_basicConstraints = id_ce /+ 19 + +let basicConstraints += asn1_sequence [ + "cA" *^ (DEFAULT ^: (mk_default_field asn1_boolean false)); + "pathLenConstraint" *^ (OPTION ^: (mk_restricted_field asn1_integer (fun x -> x >= 0)))] + (_ by (seq_tac ())) + +let basicConstraints_expansion += mk_expansion (DEFAULT ^: critical_field) basicConstraints (_ by (seq_tac ())) + +let id_ce_cRLDistributionPoints = id_ce /+ 31 + +let distributionPointName += asn1_choice [ + "fullName" ^* (mk_retagged (mk_custom_id CONTEXT_SPECIFIC CONSTRUCTED 0) generalNames); + "nameRelativeToCRLIssuer" ^* (mk_retagged (mk_custom_id CONTEXT_SPECIFIC CONSTRUCTED 1) relativeDistinguishedName)] + (_ by (choice_tac ())) + +let reasonFlags = asn1_bitstring + +let distributionPoint += asn1_sequence [ + "distributionPoint" *^ (OPTION ^: (mk_prefixed (mk_custom_id CONTEXT_SPECIFIC CONSTRUCTED 0) distributionPointName)); + "reasons" *^ (OPTION ^: (mk_retagged (mk_custom_id CONTEXT_SPECIFIC PRIMITIVE 1) reasonFlags)); + "cRLIssuer" *^ (OPTION ^: (mk_retagged (mk_custom_id CONTEXT_SPECIFIC CONSTRUCTED 2) generalNames))] + (_ by (seq_tac ())) + +let cRLDistributionPoints += asn1_sequence_of distributionPoint + +let cRLDistributionPoints_expansion += mk_expansion (DEFAULT ^: critical_field) cRLDistributionPoints (_ by (seq_tac ())) + +let id_ce_certificatePolicies = id_ce /+ 32 + +let cPSuri = asn1_ia5string + +let cPSuri_field = mk_gen_items ["qualifier" *^ (PLAIN ^: cPSuri)] (_ by (seq_tac ())) + +let id_qt = id_pkix /+ 2 + +let id_qt_cps = id_qt /+ 1 + +let id_qt_unotice = id_qt /+ 2 + +// Warning: Not enforcing the length + +let displayText += asn1_choice [ + "ia5String" ^* asn1_ia5string; + "visibleString" ^* asn1_visiblestring; + "bmpString" ^* asn1_bMPstring; + "utf8String" ^* asn1_utf8string] + (_ by (choice_tac ())) + +let noticeReference += asn1_sequence [ + "organization" *^ (PLAIN ^: displayText); + "noticeNumbers" *^ (PLAIN ^: (asn1_sequence_of asn1_integer))] + (_ by (seq_tac ())) + +let userNotice += asn1_sequence [ + "noticeRef" *^ (OPTION ^: noticeReference); + "explicitText" *^ (OPTION ^: displayText)] + (_ by (seq_tac ())) + +let userNotice_field += mk_gen_items ["qualifier" *^ (PLAIN ^: userNotice)] (_ by (seq_tac ())) + +let supported_policyQualifier += [(id_qt_cps, cPSuri_field); + (id_qt_unotice, userNotice_field)] + +let policyQualifierInfo += asn1_any_oid + "policyQulifierId" + supported_policyQualifier + (_ by (seq_tac ())) + (_ by (choice_tac ())) + +let certPolicyId = asn1_oid + +let policyInformation += asn1_sequence [ + "policyIdentifier" *^ (PLAIN ^: certPolicyId); + "policyQualifiers" *^ (OPTION ^: asn1_sequence_of policyQualifierInfo)] + (_ by (seq_tac ())) + +let certificatePolicies += asn1_sequence_of policyInformation + +let certificatePolicies_expansion += mk_expansion (DEFAULT ^: critical_field) certificatePolicies (_ by (seq_tac ())) + +let id_ce_authorityKeyIdentifier = id_ce /+ 35 + +// Warning: Not handling both being present +let authorityKeyIdentifier += asn1_sequence [ + "KeyIdentifier" *^ (OPTION ^: (mk_retagged (mk_custom_id CONTEXT_SPECIFIC PRIMITIVE 0) asn1_octetstring)); + "authorityCertIssuer" *^ (OPTION ^: (mk_retagged (mk_custom_id CONTEXT_SPECIFIC CONSTRUCTED 1) generalNames)); + "authorityCertSerialNumber" *^ (OPTION ^: (mk_retagged (mk_custom_id CONTEXT_SPECIFIC PRIMITIVE 2) asn1_integer))] + (_ by (seq_tac ())) + +let authorityKeyIdentifier_expansion += mk_expansion (DEFAULT ^: critical_field_MUST_false) authorityKeyIdentifier (_ by (seq_tac ())) + +let id_ce_extKeyUsage = id_ce /+ 37 + +let extKeyUsageSyntax = asn1_sequence_of asn1_oid + +let extKeyUsage_expansion += mk_expansion (DEFAULT ^: critical_field) extKeyUsageSyntax (_ by (seq_tac ())) + +let supported_extensions += [(id_pe_authorityInformationAccess, authorityInformationAccess_expansion); + (id_pe_ipAddrBlocks, iPAddrBlocks_expansion); + (id_ce_subjectKeyIdentifier, subjectKeyIdentifier_expansion); + (id_ce_keyUsage, keyUsage_expansion); + (id_ce_subjectAlternativeName, subjectAlternativeName_expansion); + (id_ce_basicConstraints, basicConstraints_expansion); + (id_ce_cRLDistributionPoints, cRLDistributionPoints_expansion); + (id_ce_certificatePolicies, certificatePolicies_expansion); + (id_ce_authorityKeyIdentifier, authorityKeyIdentifier_expansion); + (id_ce_extKeyUsage, extKeyUsage_expansion)] + +let extension_fallback += mk_gen_items [ + "critical" *^ (DEFAULT ^: critical_field_MUST_false); + "extnValue" *^ (PLAIN ^: asn1_octetstring)] + (_ by (seq_tac ())) + +let extension += asn1_any_oid_with_fallback + "extnId" + supported_extensions + extension_fallback + (_ by (seq_tac ())) + (_ by (choice_tac ())) + +let extensions += asn1_sequence_of extension + +#push-options "--z3rlimit 16" + +let x509_TBSCertificate += asn1_sequence [ + "version" *^ (PLAIN ^: (mk_prefixed (mk_custom_id CONTEXT_SPECIFIC CONSTRUCTED 0) version)); + "serialNumber" *^ (PLAIN ^: certificateSerialNumber); + "signature" *^ (PLAIN ^: algorithmIdentifier); + "issuer" *^ (PLAIN ^: name); + "validity" *^ (PLAIN ^: validity); + "subject" *^ (PLAIN ^: name); + "subjectPublicKeyInfo" *^ (PLAIN ^: subjectPublicKeyInfo); + "issuerUniqueID" *^ (OPTION ^: (mk_retagged (mk_custom_id CONTEXT_SPECIFIC PRIMITIVE 1) uniqueIdentifier)); + "subjectUniqueID" *^ (OPTION ^: (mk_retagged (mk_custom_id CONTEXT_SPECIFIC PRIMITIVE 2) uniqueIdentifier)); + "extensions" *^ (OPTION ^: (mk_prefixed (mk_custom_id CONTEXT_SPECIFIC CONSTRUCTED 3) extensions))] + (_ by (seq_tac ())) + +#pop-options + +let x509_certificate += asn1_sequence [ + "tbsCertificate" *^ (PLAIN ^: x509_TBSCertificate); + "signatureAlgorithm" *^ (PLAIN ^: algorithmIdentifier); + "signatureValue" *^ (PLAIN ^: asn1_bitstring)] + (_ by (seq_tac ())) + +// let's go boom! + +open ASN1.Spec.Interpreter + +let x509_certificate_parser = asn1_as_parser x509_certificate + + +[@@normalize_for_extraction [delta; + zeta; + iota; + primops]] +let parse_cert (b:bytes) = x509_certificate_parser b + +[@@normalize_for_extraction [delta; + zeta; + iota; + primops]] +let dparse_cert (b:bytes) = dasn1_as_parser x509_certificate b + diff --git a/src/ASN1/ASN1Test.fst b/src/ASN1/ASN1Test.fst new file mode 100755 index 000000000..d494d6bdc --- /dev/null +++ b/src/ASN1/ASN1Test.fst @@ -0,0 +1,57 @@ +module ASN1Test + +include ASN1.Low.Content.BOOLEAN +include ASN1.Low.LengthU32 + +open FStar.HyperStack.ST +open FStar.HyperStack.IO +open C +open C.String +open FStar.Bytes +module LB = LowStar.Buffer +module LPL = LowParse.Low.Base + +let from_bytes (b:bytes{length b <> 0}) : StackInline (LB.buffer LPL.byte) + (requires (fun h0 -> True)) + (ensures (fun h0 buf h1 -> + LB.(modifies loc_none h0 h1) /\ + LB.live h1 buf /\ LB.unused_in buf h0 /\ + LB.length buf = length b /\ + reveal b `Seq.equal` LB.as_seq h1 buf)) + = + let h0 = FStar.HyperStack.ST.get () in + let lb = LB.alloca 0uy (len b) in + store_bytes b lb; + let h1 = FStar.HyperStack.ST.get () in + LB.(modifies_only_not_unused_in loc_none h0 h1); + lb + + +let test_asn1_boolean () : St bool = + assume false; + print (!$"Testing ASN1 Boolean.\n"); + let buf = bytes_of_hex "ff" in + let open FStar.UInt32 in + let open FStar.Bytes in + let input = from_bytes buf in + let foo = LPL.validate (validate_asn1_lengthu32 ()) input (len buf) in + if not (LPL.validate (validate_asn1_boolean ()) input (len buf)) then + (print !$"Validator failed!"; false) + else + (print !$"Validator succeeded!"; true) + +let test_zeroarg () : St C.exit_code = + let b = true in + let b = test_asn1_boolean () in + if b then C.EXIT_SUCCESS else C.EXIT_FAILURE + +let main + (argc: Int32.t) + (argv: LowStar.Buffer.buffer C.String.t) +: ST C.exit_code + (requires (fun h -> + LowStar.Buffer.live h argv /\ + Int32.v argc == LowStar.Buffer.length argv + )) + (ensures (fun _ _ _ -> True)) += test_zeroarg () diff --git a/src/ASN1/Dockerfile b/src/ASN1/Dockerfile new file mode 100644 index 000000000..9d008da42 --- /dev/null +++ b/src/ASN1/Dockerfile @@ -0,0 +1,18 @@ +ARG ocaml_version=4.12 +FROM ocaml/opam:ubuntu-ocaml-$ocaml_version +RUN sudo apt-get install --no-install-recommends --yes wget + +ADD --chown=opam:opam ./ $HOME/asn1star/ +WORKDIR $HOME/asn1star + +SHELL ["/bin/bash", "--login", "-c"] + +# Dependencies (F*, Karamel, EverParse, and their opam package dependencies) +ARG CI_THREADS=24 +RUN env OPAMYES=1 CI_THREADS=$CI_THREADS NO_INTERACTIVE=1 ./install-everparse.sh + +ENV FSTAR_HOME=$HOME/asn1star/everest/FStar +ENV KRML_HOME=$HOME/asn1star/everest/karamel +ENV EVERPARSE_HOME=$HOME/asn1star/everest/everparse + +ENTRYPOINT ["/bin/bash", "--login"] diff --git a/src/ASN1/Makefile b/src/ASN1/Makefile new file mode 100755 index 000000000..f01579135 --- /dev/null +++ b/src/ASN1/Makefile @@ -0,0 +1,86 @@ +all: verify compile + +EVERPARSE_HOME ?= ../.. +KRML_HOME ?= $(EVERPARSE_HOME)/../karamel + +LOWPARSE_HOME=$(EVERPARSE_HOME)/src/lowparse + +INCLUDE_PATH = $(LOWPARSE_HOME) $(KRML_HOME)/krmllib $(KRML_HOME)/krmllib/obj + +FSTAR_OPTIONS = --cache_checked_modules --ext context_pruning \ + --already_cached *,-ASN1Test,-ASN1 \ + --cmi \ + --odir ocaml/extracted \ + $(addprefix --include ,$(INCLUDE_PATH)) \ + $(OTHERFLAGS) + + +ifdef FSTAR_HOME +FSTAR_EXE = $(FSTAR_HOME)/bin/fstar.exe +else +FSTAR_EXE = fstar.exe +endif +FSTAR = $(FSTAR_EXE) $(FSTAR_OPTIONS) + +NOT_INCLUDED=ASN1.Tmp.fst ASN1.Test.Interpreter.fst $(wildcard ASN1.Low.*) ASN1Test.fst $(wildcard ASN1.bak*) + +ALL_SOURCE_FILES = $(filter-out $(NOT_INCLUDED), $(wildcard *.fst *.fsti)) + +.depend: $(ALL_SOURCE_FILES) Makefile + $(FSTAR) --dep full --extract '* -Prims -FStar -LowParse.Low -ASN1Test -ASN1.Low' $(ALL_SOURCE_FILES) > $@.tmp + mv $@.tmp $@ + +depend: .depend + +-include .depend + +$(ALL_CHECKED_FILES): %.checked: + $(FSTAR) $< + @touch -c $@ + +verify: $(ALL_CHECKED_FILES) + echo $* + +extract: $(ALL_ML_FILES) + +%.fst-in %.fsti-in: + @echo $(FSTAR_OPTIONS) + +ocaml/extracted/%.ml: + $(FSTAR) $(notdir $(subst .checked,,$<)) --codegen OCaml --extract_module $(basename $(notdir $(subst .checked,,$<))) + +ocaml/%.krml: + $(FSTAR) --codegen krml $(notdir $(basename $<)) --extract_module $(notdir $(basename $(basename $<))) --warn_error '@241' + touch $@ + +krml.rsp: $(ALL_KRML_FILES) + for f in $^ ; do echo $$f ; done > $@.tmp + mv $@.tmp $@ + +Test.c: krml.rsp + $(KRML_HOME)/krml \ + -bundle 'ASN1Test=ASN1Test.\*,Prims,FStar.\*,C.\*,C,LowStar.\*,LowParse.\*' \ + -no-prefix ASN1Test \ + -skip-makefiles \ + -skip-compilation \ + -o $@ \ + @$^ + +# test: ASN1Test.c # test.exe +# cat $^ + +compile: extract + $(MAKE) -C ocaml + cp ocaml/_build/default/ASN1_Parser.exe evaluation/ + chmod +w evaluation/ASN1_Parser.exe + +test: eval + +eval: compile + $(MAKE) -C evaluation + +clean: + -rm -rf *.checked *.krml .depend *.c *.h *.o test.exe krml.rsp *.tmp compile_flags.txt + $(MAKE) -C ocaml clean + +.PHONY: all verify clean depend compile test eval diff --git a/src/ASN1/README-internal.txt b/src/ASN1/README-internal.txt new file mode 100644 index 000000000..a98dceb7e --- /dev/null +++ b/src/ASN1/README-internal.txt @@ -0,0 +1,3 @@ +To build the package, just run: + ./package.sh +Then, a new package, asn1star.tgz, will be created. diff --git a/src/ASN1/README.txt b/src/ASN1/README.txt new file mode 100644 index 000000000..5a09efbe6 --- /dev/null +++ b/src/ASN1/README.txt @@ -0,0 +1,17 @@ +1. DEPENDENCIES: EVERPARSE + +This package depends on EverParse. To install EverParse, run +./install-everparse.sh and follow the instructions. + +Alternatively, you can use the Dockerfile to have an environment with +EverParse already installed: + docker build -t asn1star . + docker run -i -t asn1star + +2. HOW TO VERIFY, EXTRACT AND EVALUATE + +To verify, extract the OCaml code and compile it, run: + make -j + +To run the evaluation experiments, run: + make eval diff --git a/src/ASN1/evaluation/Makefile b/src/ASN1/evaluation/Makefile new file mode 100644 index 000000000..4008f4d79 --- /dev/null +++ b/src/ASN1/evaluation/Makefile @@ -0,0 +1,22 @@ +all: + +$(MAKE) prepare + +$(MAKE) run + +prepare: + unzip data.zip -d . + +run_CRL_positive: + ./run.sh CRL positive + +run_CRL_negative: + ./run.sh CRL negative + +run_X509_positive: + ./run.sh X509 positive + +run_X509_negative: + ./run.sh X509 negative + +run: run_X509_negative run_X509_positive run_CRL_negative run_CRL_positive + +.PHONY: prepare all run run_X509_negative run_X509_positive run_CRL_negative run_CRL_positive diff --git a/src/ASN1/evaluation/data.zip b/src/ASN1/evaluation/data.zip new file mode 100644 index 000000000..087d0a2dc Binary files /dev/null and b/src/ASN1/evaluation/data.zip differ diff --git a/src/ASN1/evaluation/extract_run.sh b/src/ASN1/evaluation/extract_run.sh new file mode 100755 index 000000000..c2a912fac --- /dev/null +++ b/src/ASN1/evaluation/extract_run.sh @@ -0,0 +1,81 @@ +declare -i filecnt=0 +format=$1 +declare -i bound=10000 +declare -i totalbound=-1 +tmpfolder="extract_tmp" +declare -i result=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) +declare -i TOT=0 +firstfile="" +lastfile="" + +function run_parser { + for file in "$tmpfolder"/*.der + do + TOT=$TOT+1 + filebase=$(basename $file) + if [ $filecnt -eq 10000 ]; then + grep -q $filebase data/$format/positive_ignore.txt + if [ $? -eq 0 ]; then + result[1]=${result[1]}+1 + echo "Ignored due to white list:" $filebase + else + ./ASN1_Parser.exe $format "$file" > /dev/null + ret=$? + result[$ret]=${result[$ret]}+1 + fi + else + ./ASN1_Parser.exe $format "$file" > /dev/null + ret=$? + result[$ret]=${result[$ret]}+1 + fi + if [ $(expr $TOT % 1000) -eq 0 ]; then + echo Processed "$TOT": $file + fi + if [ $TOT -eq 1 ]; then + firstfile=$filebase + fi + lastfile=$filebase + done + rm -rf $tmpfolder +} + +rm -rf $tmpfolder +mkdir $tmpfolder +for folder1 in scan/* +do + for folder2 in $folder1/* + do + for file in $folder2/*.results + do + out="$tmpfolder"/"$(basename $file)" + ./extractor/extract $file $out > /dev/null 2> /dev/null + filecnt=$filecnt+1 + if [ $(expr $filecnt % $bound) -eq 0 ]; then + run_parser + mkdir $tmpfolder + fi + if [ $filecnt -eq $totalbound ]; then + break 3 + fi + done + done +done + +if [ $(expr $filecnt % $bound) -ne 0 ]; then + run_parser +else + rm -rf $tmpfolder +fi + +echo First: $firstfile +echo Last: $lastfile + +echo Total extract: $filecnt + +echo Total parser: $TOT + +for i in ${!result[@]}; do + echo Exit with return value "$i": ${result[$i]} +done + + diff --git a/src/ASN1/evaluation/extractor/extract.cpp b/src/ASN1/evaluation/extractor/extract.cpp new file mode 100644 index 000000000..cfbbd949e --- /dev/null +++ b/src/ASN1/evaluation/extractor/extract.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include + +using namespace std; + +const size_t BUFFER_SIZE = 65536, NAME_SIZE = 50; + +char buffer[BUFFER_SIZE], outfilename[NAME_SIZE]; + +unsigned read_Handshake_length_unchecked(char *s, unsigned pt, unsigned len) { + return ((((unsigned)((unsigned char)s[pt]) << 8) | (unsigned char)s[pt + 1]) << 8) | (unsigned char)s[pt + 2]; +} + +unsigned jump_Handshake_tuple(char *s, unsigned pt, unsigned len) { + if (pt + 4 > len) { + // Not enough data + return len; + } else { + return pt + 4 + (read_Handshake_length_unchecked(s, pt + 1, len)); + } +} + +unsigned read_TLS_length_unchecked(char *s, unsigned pt, unsigned len) { + return ((unsigned)((unsigned char)s[pt]) << 8) | (unsigned char)s[pt + 1]; +} + +unsigned jump_TLS_tuple(char *s, unsigned pt, unsigned len) { + if (pt + 5 > len) { + // Not enough data + return len; + } else { + return pt + 5 + (read_TLS_length_unchecked(s, pt + 3, len)); + } +} + +int main(int argn, char *argv[]) { + if (argn != 2 && argn != 3) { + printf("Usage: extract filename [output_path default=filename] \n"); + printf(" Output filenames: [output_path].n.der\n"); + return 0; + } + char *filename = argv[1], *outputPath = argv[2]; + FILE *TLSMessage_f= fopen(filename, "rb"); + if (TLSMessage_f) { + unsigned len = fread(buffer, 1, BUFFER_SIZE, TLSMessage_f); + if (!feof(TLSMessage_f)) { + fprintf(stderr, "Error: File %s too large\n", filename); + return -2; + } + fclose(TLSMessage_f); + int cnt = 0; + unsigned pt = 0; + while (pt < len) { + if (pt + 5 > len) { + fprintf(stderr, "Error: File %s too small\n", filename); + return -3; + } + if (buffer[pt] != 0x16) { + fprintf(stderr, "Warning: Not a handshake message in file %s, ignored\n", filename); + pt = jump_TLS_tuple(buffer, pt, len); + } else { + unsigned tpt = pt + 5; + pt = jump_TLS_tuple(buffer, pt, len); + while (tpt < pt) { + if (tpt + 4 > pt) { + fprintf(stderr, "Error: Failed to parse Handshake tuple\n"); + return -4; + } + if (buffer[tpt] != 0x0B) { + // not a certificate message + tpt = jump_Handshake_tuple(buffer, tpt, pt); + } else { + unsigned ttpt = tpt + 7; + tpt = jump_Handshake_tuple(buffer, tpt, pt); + while (ttpt < tpt) { + ++cnt; + if (ttpt + 3 > tpt) { + fprintf(stderr, "Error: Failed to parse %d-th certificate\n", cnt); + return -5; + } + unsigned clen = read_Handshake_length_unchecked(buffer, ttpt, tpt); + if (ttpt + 3 + clen > tpt) { + fprintf(stderr, "Error: Failed to parse %d-th certificate\n", cnt); + return -5; + } + if (argn == 3) { + sprintf(outfilename, "%s.%d.der", outputPath, cnt); + } else { + sprintf(outfilename, "%s.%d.der", filename, cnt); + } + FILE *X509Output_f = fopen(outfilename, "wb"); + fwrite(buffer + ttpt + 3, 1, clen, X509Output_f); + fclose(X509Output_f); + ttpt += 3 + clen; + } + } + } + } + } + printf("Succeefully extracted %d certificates\n", cnt); + } else { + fprintf(stderr, "Error: File %s not found\n", filename); + return -1; + } + return 0; +} diff --git a/src/ASN1/evaluation/run.sh b/src/ASN1/evaluation/run.sh new file mode 100755 index 000000000..a16002067 --- /dev/null +++ b/src/ASN1/evaluation/run.sh @@ -0,0 +1,57 @@ +#!/bin/bash +format=$1 +subfolder=$2 +declare -i result=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) +declare -i TOT=0 +declare -i failed=0 +declare -i ignore=0 + +for file in data/$format/$subfolder/*.der +do + TOT=$TOT+1 + ignore=0 + filebase=$(basename $file) + grep -q $filebase data/$format/"$subfolder"_ignore.txt + if [ $? -eq 0 ]; then + result[1]=${result[1]}+1 + echo "Ignored due to white list:" $filebase + ignore=1 + else + if [ "$format" = "CRL" ]; then + filesize=$(wc -c $file | awk '{print $1}') + if [ $filesize -ge 100000 ]; then + result[1]=${result[1]}+1 + echo "Ignored due to filesize:" $filebase + ignore=1 + fi + fi + fi + if [ $ignore -eq 0 ]; then + ./ASN1_Parser.exe $format "$file" > /dev/null + ret=$? + if [ "$subfolder" = "negative" ]; then + if [ $ret -ne 0 ]; then + ret=1 + fi + fi + result[$ret]=${result[$ret]}+1 + fi + if [ $(expr $TOT % 1000) -eq 0 ]; then + echo Processed "$TOT" + fi + if [ $TOT -eq 1 ]; then + firstfile=$filebase + fi + lastfile=$filebase +done + +echo First: $firstfile +echo Last: $lastfile + +echo Total: $TOT + +for i in ${!result[@]}; do + echo Exit with return value "$i": ${result[$i]} +done + + diff --git a/src/ASN1/install-everparse.sh b/src/ASN1/install-everparse.sh new file mode 100755 index 000000000..a2b7aa368 --- /dev/null +++ b/src/ASN1/install-everparse.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +set -e + +if [[ -z "$CI_THREADS" ]] ; then + CI_THREADS=1 +fi + +if [[ -n "$NO_INTERACTIVE" ]] ; then + NO_INTERACTIVE=--yes +fi + +git clone https://github.com/project-everest/everest.git everest +pushd everest +if ! ./everest $NO_INTERACTIVE opam z3 ; then + echo "Please follow the instructions above and re-run this script" + exit 1 +fi +export PATH=$PWD/z3/bin:$PATH +./everest $NO_INTERACTIVE reset +./everest $NO_INTERACTIVE -j "$CI_THREADS" FStar make karamel make +make -C everparse -j "$CI_THREADS" lowparse + +echo "Please set the following environment variables:" +echo "FSTAR_HOME=$(pwd)/FStar" +echo "KRML_HOME=$(pwd)/karamel" +echo "EVERPARSE_HOME=$(pwd)/everparse" +popd diff --git a/src/ASN1/ocaml/.gitignore b/src/ASN1/ocaml/.gitignore new file mode 100644 index 000000000..8d3a309e6 --- /dev/null +++ b/src/ASN1/ocaml/.gitignore @@ -0,0 +1 @@ +extracted diff --git a/src/ASN1/ocaml/ASN1_Debug.ml b/src/ASN1/ocaml/ASN1_Debug.ml new file mode 100644 index 000000000..0a19b961d --- /dev/null +++ b/src/ASN1/ocaml/ASN1_Debug.ml @@ -0,0 +1,46 @@ +let string_of_byte a = + let s = Printf.sprintf "%x" a in + if a < 16 + then "0" ^ s + else s + +let rec string_of_all_bytes' accu n s i = + if i >= n + then accu + else + let a = FStar_Seq_Base.index s i in + let accu' = accu ^ " " ^ string_of_byte a in + string_of_all_bytes' accu' n s (Z.add i (Prims.of_int 1)) + +let string_of_all_bytes accu s = + string_of_all_bytes' accu (FStar_Seq_Base.length s) s (Prims.of_int 0) + +let parse_debug _ msg p input = + let msg' = string_of_all_bytes msg input in + print_endline ("STARTED: " ^ msg'); + let res = p input in + let status = match res with + | Some _ -> + "SUCCESS: " + | None -> + "FAILURE: " + in + print_endline (status ^ msg'); + res + +let parse_debugf _ msg fp x input = + let msg' = string_of_all_bytes msg input in + print_endline ("STARTED: " ^ msg'); + let res = fp x input in + let status = match res with + | Some _ -> + "SUCCESS: " + | None -> + "FAILURE: " + in + print_endline (status ^ msg'); + res + +let print_debug msg v = + print_endline msg; + v diff --git a/src/ASN1/ocaml/ASN1_Parser.ml b/src/ASN1/ocaml/ASN1_Parser.ml new file mode 100644 index 000000000..2961a813a --- /dev/null +++ b/src/ASN1/ocaml/ASN1_Parser.ml @@ -0,0 +1,146 @@ +let lp_bytes_of_bytes (b:FStar_Bytes.bytes) + : LowParse_Bytes.bytes + = let rec aux (i:int) (out:LowParse_Bytes.bytes) + : LowParse_Bytes.bytes + = if Z.of_int i = FStar_Bytes.length b + then out + else aux (i + 1) (FStar_Seq_Properties.snoc out (FStar_Bytes.get b (Stdint.Uint32.of_int i))) + in + aux 0 (FStar_Seq_Base.empty()) + +let lp_bytes_of_string s : LowParse_Bytes.bytes + = FStar_Seq_Base.MkSeq (List.map int_of_char (List.init (String.length s) (String.get s))) + +let check_omitted_default (s:string) + : bool + = let pattern = Str.regexp_string "\x30\x0F\x06\x03\x55\x1D\x13\x01\x01\xFF\x04\x05\x30\x03\x01\x01\x00" in + try (let _ = Str.search_forward pattern s 0 in + print_string "Found default field not omitted in BasicConstraints\n"; + true) with + Not_found -> false + +let check_certificate_version (s : string) : bool + = let pattern = Str.regexp_string "\xA0\x03\x02\x01\x02" in + try (let _ = Str.search_forward pattern s 0 in false) with + Not_found -> + print_string "Incorrect certificate version\n"; + true + +let check_directorytype (s : string) : bool + = let pattern = Str.regexp "\x06\x03\x55\x04\\(.\\|\x0A\\)\x16" in + try (let _ = Str.search_forward pattern s 0 in + print_string "Found invalid directory string type\n"; + true) with + Not_found -> false + +let check_directorytype2 (s : string) : bool + = let pattern = Str.regexp_string "\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01\x13" in + try (let _ = Str.search_forward pattern s 0 in + print_string "Found invalid directory string type\n"; + true) with + Not_found -> false + +let check_omitted_default2 (s:string) + : bool + = let pattern = Str.regexp "\x06\x03\x55\x1D.\x01\x01\x00" in + try (let _ = Str.search_forward pattern s 0 in + print_string "Found default field not omitted in extension\n"; + true) with + Not_found -> false + +let check_wrong_timeformat (s:string) + :bool + = let pattern = Str.regexp "\x17\x11[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]\\(+\\|-\\)0000" in + try (let _ = Str.search_forward pattern s 0 in + print_string "Found invalid time format\n"; + true) with + Not_found -> false + +let check_empty_sequence (s:string) + : bool + = let pattern = Str.regexp_string "\x30\x09\x06\x03\x55\x1D\x11\x04\x02\x30\x00" in + try (let _ = Str.search_forward pattern s 0 in + print_string "Found empty sequence in SubjectAlternativeName extension\n"; + true) with + Not_found -> false + +let check_bad_boolean (s:string) + : bool + = let pattern = Str.regexp_string "\x06\x03\x55\x1D\x13\x01\x01\x01" in + try (let _ = Str.search_forward pattern s 0 in + print_string "Found bad boolean in BasicConstraints\n"; + true) with + Not_found -> false + +let check_overly_large (s:string) + : bool + = let pattern = Str.regexp_string "\xA0\x03\x02\x01\x02" in + try (let x = Str.search_forward pattern s 0 in + let lch = String.get s (x + 6) in + if int_of_char lch > 20 then + (print_string "Found overly large serial number\n"; + true) + else + false) with + Not_found -> false + +let get_failure_reason (s:string) + : int + = if check_certificate_version s then + 3 + else if check_omitted_default s then + 4 + else if check_omitted_default2 s then + 4 + else if check_directorytype s then + 5 + else if check_directorytype2 s then + 5 + else if check_wrong_timeformat s then + 6 + else if check_overly_large s then + 6 + else if check_bad_boolean s then + 6 + else if check_empty_sequence s then + 7 + else + 2 + +let main = + let argc = Array.length Sys.argv in + if argc <> 3 && argc <> 4 + then ( + print_string "Usage: ASN1_Parser format filename [-debug]\n"; + print_string "Supported format: X509 CRL\n"; + exit 1 + ) + else ( + let formatname = Sys.argv.(1) in + let debugflag = argc = 4 in + let p = if String.equal formatname "X509" then + (if debugflag then ASN1_X509.dparse_cert else ASN1_X509.parse_cert) + else if String.equal formatname "CRL" then + (if debugflag then ASN1_CRL.dparse_crl else ASN1_CRL.parse_crl) + else + fun _ -> None + in + let filename = Sys.argv.(2) in + try + let file = open_in_bin filename in + let filelen = in_channel_length file in + let str = really_input_string file filelen in + close_in file; + let b = lp_bytes_of_string str in + print_string ("About to parse " ^ string_of_int (Z.to_int (FStar_Bytes.length str)) ^ " bytes from " ^ filename ^ " ... \n"); + match p b with + | None -> print_string "parsing failed\n"; + if formatname = "X509" then + exit (get_failure_reason str) + else + exit 2 + | Some (_, n) -> + print_string ("parsing succeeded consuming " ^ Z.to_string n ^ " bytes\n") + with + _ -> print_string (filename ^ " could not be read\n"); exit 3 + ) diff --git a/src/ASN1/ocaml/Makefile b/src/ASN1/ocaml/Makefile new file mode 100644 index 000000000..c30252eac --- /dev/null +++ b/src/ASN1/ocaml/Makefile @@ -0,0 +1,7 @@ +export OCAMLPATH += $(FSTAR_HOME)/lib + +all: + dune build + +clean: + rm -rf _build extracted diff --git a/src/ASN1/ocaml/dune b/src/ASN1/ocaml/dune new file mode 100644 index 000000000..3baadc499 --- /dev/null +++ b/src/ASN1/ocaml/dune @@ -0,0 +1,15 @@ +(include_subdirs unqualified) + +(executable + (name ASN1_Parser) + (libraries + batteries + fstar.lib + process + yojson + ppx_deriving_yojson.runtime + re + ) + (preprocess (pps ppx_deriving_yojson)) + (flags (:standard -w -8-9-11-26-27-33-39-20)) +) diff --git a/src/ASN1/ocaml/dune-project b/src/ASN1/ocaml/dune-project new file mode 100644 index 000000000..60a63062f --- /dev/null +++ b/src/ASN1/ocaml/dune-project @@ -0,0 +1,3 @@ +(lang dune 2.8) +(name x509) + diff --git a/src/ASN1/package.sh b/src/ASN1/package.sh new file mode 100755 index 000000000..3a3aaa646 --- /dev/null +++ b/src/ASN1/package.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e +set -x + +unset CDPATH +cd "$( dirname "${BASH_SOURCE[0]}" )" + +git clean -ffdx + +ln -s . asn1star +tar czf asn1star.tgz $(find asn1star/ -type f -and -not -name .gitignore -and -not -name package.sh -and -not -name asn1star.tgz -and -not -path asn1star/test_norm -and -not -name README-internal.txt) +rm asn1star diff --git a/src/ASN1/test_norm/ASN1.Spec.Content.BOOLEAN.Test.fst b/src/ASN1/test_norm/ASN1.Spec.Content.BOOLEAN.Test.fst new file mode 100755 index 000000000..b01f751ae --- /dev/null +++ b/src/ASN1/test_norm/ASN1.Spec.Content.BOOLEAN.Test.fst @@ -0,0 +1,20 @@ +module ASN1.Spec.Content.BOOLEAN.Test + +open ASN1.Spec.Content.BOOLEAN + +open FStar.Tactics + +friend FStar.Seq.Base +friend LowParse.Spec.Base +friend LowParse.Spec.Combinators +friend LowParse.Spec.Int + +let testNil : squash (parse_asn1_boolean (Seq.empty) == None) = _ by (trefl (); qed ()) + +let testTrue : squash (parse_asn1_boolean (Seq.create 1 (0xFFuy)) == Some (true, 1) ) = _ by (trefl (); qed ()) + +let testFalse : squash (parse_asn1_boolean (Seq.create 1 (0x00uy)) == Some (false, 1) ) = _ by (trefl (); qed ()) + +let testFail : squash (parse_asn1_boolean (Seq.create 1 (0x01uy)) == None) = _ by (trefl (); qed ()) + + diff --git a/src/ASN1/test_norm/ASN1.Spec.Content.BOOLEAN.Test.fsti b/src/ASN1/test_norm/ASN1.Spec.Content.BOOLEAN.Test.fsti new file mode 100755 index 000000000..e00e6b4ec --- /dev/null +++ b/src/ASN1/test_norm/ASN1.Spec.Content.BOOLEAN.Test.fsti @@ -0,0 +1 @@ +module ASN1.Spec.Content.BOOLEAN.Test diff --git a/src/lowparse/LowParse.Spec.Combinators.fst b/src/lowparse/LowParse.Spec.Combinators.fst index c05799f6d..fe56a92ac 100644 --- a/src/lowparse/LowParse.Spec.Combinators.fst +++ b/src/lowparse/LowParse.Spec.Combinators.fst @@ -7,8 +7,6 @@ module U32 = FStar.UInt32 module T = FStar.Tactics -#reset-options "--using_facts_from '* -FStar.Tactis -FStar.Reflection'" - let and_then #k #t p #k' #t' p' = let f : bare_parser t' = and_then_bare p p' in and_then_correct p p' ; @@ -562,7 +560,7 @@ let serialize_nondep_then_upd_bw_left )) = serialize_nondep_then_upd_left s1 s2 x y -#reset-options "--z3refresh --z3rlimit 64 --z3cliopt smt.arith.nl=false --using_facts_from '* -FStar.Tactis -FStar.Reflection'" +#reset-options "--z3refresh --z3rlimit 64 --z3cliopt smt.arith.nl=false" let serialize_nondep_then_upd_bw_left_chain (#k1: parser_kind) @@ -652,7 +650,7 @@ let serialize_nondep_then_upd_right_chain seq_upd_seq_right_to_left s l2 s2' i' s'; seq_upd_seq_slice_idem s l2 (Seq.length s) -#reset-options "--z3rlimit 32 --using_facts_from '* -FStar.Tactis -FStar.Reflection'" +#reset-options "--z3rlimit 32" let tot_serialize_nondep_then #k1 #t1 #p1 s1 #k2 #t2 #p2 s2 diff --git a/src/lowparse/LowParse.Spec.Combinators.fsti b/src/lowparse/LowParse.Spec.Combinators.fsti index bc23f12c5..a0828b07b 100644 --- a/src/lowparse/LowParse.Spec.Combinators.fsti +++ b/src/lowparse/LowParse.Spec.Combinators.fsti @@ -7,8 +7,6 @@ module U32 = FStar.UInt32 module T = FStar.Tactics -#reset-options "--using_facts_from '* -FStar.Tactis -FStar.Reflection'" - (** Constant-size parsers *) let make_constant_size_parser_aux @@ -531,8 +529,6 @@ let and_then_correct and_then_injective p p'; and_then_no_lookahead p p' -#reset-options "--using_facts_from '* -FStar.Tactis -FStar.Reflection'" - val and_then (#k: parser_kind) (#t:Type) @@ -1747,7 +1743,7 @@ val serialize_nondep_then_upd_bw_left serialize (serialize_nondep_then s1 s2) (y, snd x) == seq_upd_bw_seq s len2 (serialize s1 y) )) -#reset-options "--z3refresh --z3rlimit 64 --z3cliopt smt.arith.nl=false --using_facts_from '* -FStar.Tactis -FStar.Reflection'" +#reset-options "--z3refresh --z3rlimit 64 --z3cliopt smt.arith.nl=false" val serialize_nondep_then_upd_bw_left_chain (#k1: parser_kind) @@ -1910,7 +1906,7 @@ val tot_serialize_nondep_then_eq : Lemma (bare_serialize (tot_serialize_nondep_then s1 s2) input == tot_bare_serialize_nondep_then s1 s2 input) -#reset-options "--z3rlimit 32 --using_facts_from '* -FStar.Tactis -FStar.Reflection'" +#reset-options "--z3rlimit 32" (** Apply a total transformation on parsed data *) diff --git a/src/lowparse/LowParse.Spec.Defaultable.fst b/src/lowparse/LowParse.Spec.Defaultable.fst new file mode 100644 index 000000000..34327415b --- /dev/null +++ b/src/lowparse/LowParse.Spec.Defaultable.fst @@ -0,0 +1,182 @@ +module LowParse.Spec.Defaultable +include LowParse.Spec.Base +include LowParse.Spec.Combinators + +module Seq = FStar.Seq + +let parse_defaultable_bare (#k: parser_kind) (#t : Type) (defaultablev : option t) (p: parser k t) : Tot (bare_parser t) = + fun (input : bytes) -> + match defaultablev with + | None -> parse p input + | Some v -> if (Seq.length input = 0) then Some (v, 0) else (parse p input) + +let parse_defaultable_bare_injective (#k : parser_kind) (#t : Type) (defaultablev : option t) (p : parser k t) (b1 b2 : bytes) : Lemma + (requires ((parse_defaultable_injective_cond_prop defaultablev p) /\ (injective_precond (parse_defaultable_bare defaultablev p) b1 b2))) + (ensures (injective_postcond (parse_defaultable_bare defaultablev p) b1 b2)) += parser_kind_prop_equiv k p; + match defaultablev with + | None -> assert (injective_precond p b1 b2) + | Some v -> match (Seq.length b1 = 0), (Seq.length b2 = 0) with + | true, true -> () + | true, false -> assert (parse_defaultable_injective_cond defaultablev p b2) + | false, true -> assert (parse_defaultable_injective_cond defaultablev p b1) + | false, false -> assert (injective_precond p b1 b2) + +let parse_defaultable (#k: parser_kind) (#t : Type) (defaultablev : option t) (p : parser k t) : Pure (parser (parse_defaultable_kind k) t) + (requires (parse_defaultable_injective_cond_prop defaultablev p)) + (ensures (fun _ -> True)) += Classical.forall_intro_2 (fun x -> Classical.move_requires (parse_defaultable_bare_injective defaultablev p x)); + parser_kind_prop_equiv k p; + parser_kind_prop_equiv (parse_defaultable_kind k) (parse_defaultable_bare defaultablev p); + parse_defaultable_bare defaultablev p + +let tot_parse_defaultable_bare (#k: parser_kind) (#t : Type) (defaultablev : option t) (p: tot_parser k t) : Tot (tot_bare_parser t) = + fun (input : bytes) -> + match defaultablev with + | None -> p input + | Some v -> if (Seq.length input = 0) then Some (v, 0) else (p input) + +let tot_parse_defaultable #k #t defaultablev p = + parser_kind_prop_ext + (parse_defaultable_kind k) + (parse_defaultable #k defaultablev p) + (tot_parse_defaultable_bare defaultablev p); + tot_parse_defaultable_bare defaultablev p + +let and_then_defaultable' + (#k : parser_kind) + (#t : eqtype) + (p : parser k t) + (#k' : parser_kind) + (#t' : Type) + (fp : t -> parser k' t') + (defv : option t') + (input : bytes) +: Lemma + (requires (and_then_cases_injective fp /\ (forall (v : t). parse_defaultable_injective_cond_prop defv (fp v)))) + (ensures (parse_defaultable_injective_cond defv (p `and_then` fp) input)) += match defv with + | None -> () + | Some dv -> + let _ = and_then_eq p fp input in + match parse p input with + | Some (id, l) -> + let input' = Seq.slice input l (Seq.length input) in + assert (parse_defaultable_injective_cond defv (fp id) input') + | None -> () + +let and_then_defaultable + (#k : parser_kind) + (#t : eqtype) + (p : parser k t) + (#k' : parser_kind) + (#t' : Type) + (fp : t -> parser k' t') + (defv : option t') +: Lemma + (requires (and_then_cases_injective fp /\ (forall (v : t). parse_defaultable_injective_cond_prop defv (fp v)))) + (ensures (parse_defaultable_injective_cond_prop defv (p `and_then` fp))) += Classical.forall_intro (Classical.move_requires (and_then_defaultable' p fp defv)) + +let nondep_then_defaultable' + (#k : parser_kind) + (#t : Type) + (p : parser k t) + (defv : option t) + (#k' : parser_kind) + (#t' : Type) + (p' : parser k' t') + (defv' : option t') + (input : bytes) +: Lemma + (requires (parse_defaultable_injective_cond_prop defv p)) + (ensures (parse_defaultable_injective_cond (mk_option_tuple defv defv') (p `nondep_then` p') input)) += match defv, defv' with + | None, _ -> () + | _, None -> () + | Some dv, Some dv' -> + let _ = nondep_then_eq p p' input in + match parse p input with + | Some (v, l) -> + assert (~ (v == dv)); + (let input' = Seq.slice input l (Seq.length input) in + match parse p' input' with + | Some (v', l') -> + assert (~ ((v, v') == (dv, dv'))) + | None -> ()) + | None -> () + +let nondep_then_defaultable + (#k : parser_kind) + (#t : Type) + (p : parser k t) + (defv : option t) + (#k' : parser_kind) + (#t' : Type) + (p' : parser k' t') + (defv' : option t') +: Lemma + (requires (parse_defaultable_injective_cond_prop defv p)) + (ensures (parse_defaultable_injective_cond_prop (mk_option_tuple defv defv') (p `nondep_then` p'))) += Classical.forall_intro (Classical.move_requires (nondep_then_defaultable' p defv p' defv')) + +let nondep_then_defaultable_snd' + (#k : parser_kind) + (#t : Type) + (p : parser k t) + (defv : option t) + (#k' : parser_kind) + (#t' : Type) + (p' : parser k' t') + (defv' : option t') + (input : bytes) +: Lemma + (requires (parse_defaultable_injective_cond_prop defv' p')) + (ensures (parse_defaultable_injective_cond (mk_option_tuple defv defv') (p `nondep_then` p') input)) += match defv, defv' with + | None, _ -> () + | _, None -> () + | Some dv, Some dv' -> + let _ = nondep_then_eq p p' input in + match parse p input with + | Some (v, l) -> + (let input' = Seq.slice input l (Seq.length input) in + match parse p' input' with + | Some (v', l') -> + assert (~ ((v, v') == (dv, dv'))) + | None -> ()) + | None -> () + +let nondep_then_defaultable_snd + (#k : parser_kind) + (#t : Type) + (p : parser k t) + (defv : option t) + (#k' : parser_kind) + (#t' : Type) + (p' : parser k' t') + (defv' : option t') +: Lemma + (requires (parse_defaultable_injective_cond_prop defv' p')) + (ensures (parse_defaultable_injective_cond_prop (mk_option_tuple defv defv') (p `nondep_then` p'))) += Classical.forall_intro (Classical.move_requires (nondep_then_defaultable_snd' p defv p' defv')) + +let defaultable_trivial_eq + (#k : parser_kind) + (#t : Type) + (p : parser k t) +: Lemma + (ensures (forall input. parse (parse_defaultable None p) input == parse p input)) += () + +let eq_defaultable + (#k : parser_kind) + (#t : Type) + (p : parser k t) + (defv : option t) + (#k' : parser_kind) + (p' : parser k' t) +: Lemma + (requires (parse_defaultable_injective_cond_prop defv p) /\ (forall input. parse p input == parse p' input)) + (ensures (parse_defaultable_injective_cond_prop defv p')) += () diff --git a/src/lowparse/LowParse.Spec.Defaultable.fsti b/src/lowparse/LowParse.Spec.Defaultable.fsti new file mode 100644 index 000000000..4a770fdc6 --- /dev/null +++ b/src/lowparse/LowParse.Spec.Defaultable.fsti @@ -0,0 +1,97 @@ +module LowParse.Spec.Defaultable +include LowParse.Spec.Base +include LowParse.Spec.Combinators + +let mk_option_tuple + (#t #t' : Type) + (a : option t) + (b : option t') +: option (t * t') += match a, b with + | None, _ + | _, None -> None + | Some u, Some v -> Some (u, v) + +let parse_defaultable_kind (k : parser_kind) : Tot parser_kind = { + parser_kind_metadata = None; + parser_kind_low = 0; + parser_kind_high = k.parser_kind_high; + parser_kind_subkind = None; +} + +let parse_defaultable_injective_cond (#k : parser_kind) (#t : Type) (defaultablev : option t) (p : parser k t) (b : bytes) : GTot Type0 = + match defaultablev with + | None -> True + | Some v -> match (parse p b) with + | None -> True + | Some (v', _) -> ~ (v == v') + +let parse_defaultable_injective_cond_prop (#k : parser_kind) (#t : Type) (defaultablev : option t) (p : parser k t) : GTot Type0 = + forall (b : bytes) . parse_defaultable_injective_cond defaultablev p b + + +val parse_defaultable (#k: parser_kind) (#t : Type) (defaultablev : option t) (p : parser k t) : Pure (parser (parse_defaultable_kind k) t) + (requires (parse_defaultable_injective_cond_prop defaultablev p)) + (ensures (fun _ -> True)) + +val tot_parse_defaultable (#k: parser_kind) (#t : Type) (defaultablev : option t) (p : tot_parser k t) : Pure (tot_parser (parse_defaultable_kind k) t) + (requires (parse_defaultable_injective_cond_prop #k defaultablev p)) + (ensures (fun y -> + forall x . parse y x == parse (parse_defaultable #k defaultablev p) x + )) + +val and_then_defaultable + (#k : parser_kind) + (#t : eqtype) + (p : parser k t) + (#k' : parser_kind) + (#t' : Type) + (fp : t -> parser k' t') + (defv : option t') +: Lemma + (requires (and_then_cases_injective fp /\ (forall (v : t). parse_defaultable_injective_cond_prop defv (fp v)))) + (ensures (parse_defaultable_injective_cond_prop defv (p `and_then` fp))) + +val nondep_then_defaultable + (#k : parser_kind) + (#t : Type) + (p : parser k t) + (defv : option t) + (#k' : parser_kind) + (#t' : Type) + (p' : parser k' t') + (defv' : option t') +: Lemma + (requires (parse_defaultable_injective_cond_prop defv p)) + (ensures (parse_defaultable_injective_cond_prop (mk_option_tuple defv defv') (p `nondep_then` p'))) + +val nondep_then_defaultable_snd + (#k : parser_kind) + (#t : Type) + (p : parser k t) + (defv : option t) + (#k' : parser_kind) + (#t' : Type) + (p' : parser k' t') + (defv' : option t') +: Lemma + (requires (parse_defaultable_injective_cond_prop defv' p')) + (ensures (parse_defaultable_injective_cond_prop (mk_option_tuple defv defv') (p `nondep_then` p'))) + +val defaultable_trivial_eq + (#k : parser_kind) + (#t : Type) + (p : parser k t) +: Lemma + (ensures (forall input. parse (parse_defaultable None p) input == parse p input)) + +val eq_defaultable + (#k : parser_kind) + (#t : Type) + (p : parser k t) + (defv : option t) + (#k' : parser_kind) + (p' : parser k' t) +: Lemma + (requires (parse_defaultable_injective_cond_prop defv p) /\ (forall input. parse p input == parse p' input)) + (ensures (parse_defaultable_injective_cond_prop defv p')) diff --git a/src/lowparse/LowParse.Tot.Defaultable.fst b/src/lowparse/LowParse.Tot.Defaultable.fst new file mode 100644 index 000000000..f02737049 --- /dev/null +++ b/src/lowparse/LowParse.Tot.Defaultable.fst @@ -0,0 +1,86 @@ +module LowParse.Tot.Defaultable +include LowParse.Spec.Defaultable +include LowParse.Tot.Combinators + +let parse_defaultable_injective_cond (#k : parser_kind) (#t : Type) (defaultablev : option t) (p : parser k t) (b : bytes) : GTot Type0 = + parse_defaultable_injective_cond #k defaultablev p b + +let parse_defaultable_injective_cond_prop (#k : parser_kind) (#t : Type) (defaultablev : option t) (p : parser k t) : GTot Type0 = + parse_defaultable_injective_cond_prop #k defaultablev p + +val parse_defaultable (#k: parser_kind) (#t : Type) (defaultablev : option t) (p : parser k t) : Pure (parser (parse_defaultable_kind k) t) + (requires (parse_defaultable_injective_cond_prop defaultablev p)) + (ensures (fun y -> + forall x . parse y x == parse (parse_defaultable #k defaultablev p) x + )) + +let parse_defaultable #k #t = tot_parse_defaultable #k #t + +val and_then_defaultable + (#k : parser_kind) + (#t : eqtype) + (p : parser k t) + (#k' : parser_kind) + (#t' : Type) + (fp : t -> parser k' t') + (defv : option t') +: Lemma + (requires (and_then_cases_injective fp /\ (forall (v : t). parse_defaultable_injective_cond_prop defv (fp v)))) + (ensures (parse_defaultable_injective_cond_prop defv (p `and_then` fp))) + +let and_then_defaultable #k #t p #k' #t' fp defv = + and_then_defaultable #k #t p #k' #t' fp defv + +val nondep_then_defaultable + (#k : parser_kind) + (#t : Type) + (p : parser k t) + (defv : option t) + (#k' : parser_kind) + (#t' : Type) + (p' : parser k' t') + (defv' : option t') +: Lemma + (requires (parse_defaultable_injective_cond_prop defv p)) + (ensures (parse_defaultable_injective_cond_prop (mk_option_tuple defv defv') (p `nondep_then` p'))) + +let nondep_then_defaultable #k #t p defv #k' #t' p' defv' = + nondep_then_defaultable #k #t p defv #k' #t' p' defv' + +val nondep_then_defaultable_snd + (#k : parser_kind) + (#t : Type) + (p : parser k t) + (defv : option t) + (#k' : parser_kind) + (#t' : Type) + (p' : parser k' t') + (defv' : option t') +: Lemma + (requires (parse_defaultable_injective_cond_prop defv' p')) + (ensures (parse_defaultable_injective_cond_prop (mk_option_tuple defv defv') (p `nondep_then` p'))) + +let nondep_then_defaultable_snd #k #t p defv #k' #t' p' defv' = + nondep_then_defaultable_snd #k #t p defv #k' #t' p' defv' + +val defaultable_trivial_eq + (#k : parser_kind) + (#t : Type) + (p : parser k t) +: Lemma + (ensures (forall input. parse (parse_defaultable None p) input == parse p input)) + +let defaultable_trivial_eq #k #t p = defaultable_trivial_eq #k #t p + +val eq_defaultable + (#k : parser_kind) + (#t : Type) + (p : parser k t) + (defv : option t) + (#k' : parser_kind) + (p' : parser k' t) +: Lemma + (requires (parse_defaultable_injective_cond_prop defv p) /\ (forall input. parse p input == parse p' input)) + (ensures (parse_defaultable_injective_cond_prop defv p')) + +let eq_defaultable #k #t p defv #k' p' = eq_defaultable #k #t p defv #k' p' diff --git a/src/lowparse/Makefile b/src/lowparse/Makefile index 13e867615..60ffed243 100644 --- a/src/lowparse/Makefile +++ b/src/lowparse/Makefile @@ -28,7 +28,7 @@ endif INCLUDE_KRML=--include $(KRMLLIB) --include $(KRMLLIB)/obj ALREADY_CACHED = --already_cached *,-LowParse -FSTAR_OPTIONS += --use_hints --cache_checked_modules $(addprefix --include , $(INCLUDE_PATHS)) $(INCLUDE_KRML) $(ALREADY_CACHED) +FSTAR_OPTIONS += --ext context_pruning --cache_checked_modules $(addprefix --include , $(INCLUDE_PATHS)) $(INCLUDE_KRML) $(ALREADY_CACHED) LOWPARSE_FILES=$(wildcard LowParse.*.fst) $(wildcard LowParse.*.fsti)