Skip to content

Commit 07d6ebe

Browse files
committed
m
1 parent d790d25 commit 07d6ebe

File tree

1 file changed

+73
-18
lines changed

1 file changed

+73
-18
lines changed

DynamoDbEncryption/dafny/DynamoDbEncryption/src/DynamoToStruct.dfy

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,13 @@ module DynamoToStruct {
674674
//= specification/dynamodb-encryption-client/ddb-attribute-serialization.md#list-entry-value
675675
//# A List MAY hold any DynamoDB Attribute Value data type,
676676
//# and MAY hold values of different types.
677-
function method {:opaque} CollectList(
677+
678+
// Can't be {:tailrecursion} because it calls AttrToBytes which might again call CollectList
679+
// However, we really need this to loop and not recurse.
680+
// This verifies without the `by method`, but Dafny is too broken to let it verify by method
681+
// for example, a call to CollectList somehow does not satisfy the decreases clause
682+
// hence the {:verify false}
683+
function {:opaque} {:verify false} {:axiom} CollectList(
678684
listToSerialize : ListAttributeValue,
679685
depth : uint32,
680686
serialized : seq<uint8> := []
@@ -687,10 +693,18 @@ module DynamoToStruct {
687693
if |listToSerialize| == 0 then
688694
Success(serialized)
689695
else
690-
// Can't do the `pos` optimization, because I can't appease the `decreases`
691696
var val :- AttrToBytes(listToSerialize[0], true, depth+1);
692697
CollectList(listToSerialize[1..], depth, serialized + val)
693698
}
699+
by method {
700+
var result := serialized;
701+
for i := 0 to |listToSerialize|
702+
{
703+
var val :- AttrToBytes(listToSerialize[i], true, depth+1);
704+
result := result + val;
705+
}
706+
return Success(result);
707+
}
694708

695709
function method SerializeMapItem(key : string, value : seq<uint8>) : (ret : Result<seq<uint8>, string>)
696710

@@ -892,31 +906,23 @@ module DynamoToStruct {
892906
DeserializeNumberSet(serialized[len..], remainingCount-1, origSerializedSize, AttrValueAndLength(nattr, resultSet.len + len + LENGTH_LEN32))
893907
}
894908

895-
// Bytes to List
896-
// Can't be {:tailrecursion} because it calls BytesToAttr which might again call DeserializeList
897-
function method {:vcs_split_on_every_assert} {:opaque} DeserializeList(
909+
function method {:vcs_split_on_every_assert} DeserializeListEntry(
898910
serialized : seq<uint8>,
899911
pos : uint32,
900-
ghost orig_pos : uint32,
901-
remainingCount : uint32,
902912
depth : uint32,
903913
resultList : AttrValueAndLength
904914
)
905-
: (ret : Result<AttrValueAndLength, string>)
915+
: (ret : Result<(AttrValueAndLength, uint32), string>)
906916
requires |serialized| < UINT32_MAX as int
907917
requires pos as int <= |serialized|
908-
requires orig_pos <= pos
909918
requires depth <= MAX_STRUCTURE_DEPTH
910919
requires resultList.val.L?
911-
ensures ret.Success? ==> ret.value.val.L?
912-
requires pos == Add32(orig_pos, resultList.len)
913-
ensures ret.Success? ==> ret.value.len <= |serialized| as uint32 - orig_pos
914-
decreases |serialized| as uint32 - pos
920+
ensures ret.Success? ==> ret.value.0.val.L?
921+
ensures ret.Success? ==> pos < ret.value.1 <= |serialized| as uint32
922+
decreases |serialized| as uint32 - pos, 0
915923
{
916924
var serialized_size := |serialized| as uint32;
917-
if remainingCount == 0 then
918-
Success(resultList)
919-
else if serialized_size-pos < PREFIX_LEN32 then
925+
if serialized_size-pos < PREFIX_LEN32 then
920926
Failure("Out of bytes reading Type of List element")
921927
else
922928
var TerminalTypeId := serialized[pos..pos+2];
@@ -927,9 +933,57 @@ module DynamoToStruct {
927933
else
928934
assert serialized_size == |serialized| as uint32;
929935
var nval :- BytesToAttr(serialized, TerminalTypeId, Some(len), depth+1, new_pos);
936+
var new_pos := new_pos + nval.len;
930937
var nattr := AttributeValue.L(resultList.val.L + [nval.val]);
931-
var nResultList := AttrValueAndLength(nattr, Add32_3(resultList.len, len, PREFIX_LEN32));
932-
DeserializeList(serialized, new_pos+len, orig_pos, remainingCount-1, depth, nResultList)
938+
var nResultList := AttrValueAndLength(nattr, Add32(resultList.len, new_pos-pos));
939+
Success((nResultList, new_pos))
940+
}
941+
942+
// Bytes to List
943+
// Can't be {:tailrecursion} because it calls BytesToAttr which might again call DeserializeList
944+
// However, we really need this to loop and not recurse.
945+
// This verifies without the `by method`, but Dafny is too broken to let it verify by method
946+
// for example, a call to DeserializeListEntry somehow does not satisfy the decreases clause
947+
// hence the {:verify false}
948+
function {:vcs_split_on_every_assert} {:opaque} {:verify false} DeserializeList(
949+
serialized : seq<uint8>,
950+
pos : uint32,
951+
ghost orig_pos : uint32,
952+
remainingCount : uint32,
953+
depth : uint32,
954+
resultList : AttrValueAndLength
955+
)
956+
: (ret : Result<AttrValueAndLength, string>)
957+
requires |serialized| < UINT32_MAX as int
958+
requires pos as int <= |serialized|
959+
requires orig_pos <= pos
960+
requires depth <= MAX_STRUCTURE_DEPTH
961+
requires resultList.val.L?
962+
ensures ret.Success? ==> ret.value.val.L?
963+
requires pos == Add32(orig_pos, resultList.len)
964+
ensures ret.Success? ==> ret.value.len <= |serialized| as uint32 - orig_pos
965+
decreases |serialized| as uint32 - pos, 1
966+
{
967+
if remainingCount == 0 then
968+
Success(resultList)
969+
else
970+
var (newResultList, npos) :- DeserializeListEntry(serialized, pos, depth, resultList);
971+
DeserializeList(serialized, npos, orig_pos, remainingCount - 1, depth, newResultList)
972+
}
973+
by method {
974+
var npos : uint32 := pos;
975+
var newResultList := resultList;
976+
for i := 0 to remainingCount
977+
invariant serialized == old(serialized)
978+
invariant newResultList.val.L?
979+
invariant npos as int <= |serialized|
980+
invariant npos == Add32(orig_pos, newResultList.len)
981+
{
982+
var test :- DeserializeListEntry(serialized, npos, depth, newResultList);
983+
newResultList := test.0;
984+
npos := test.1;
985+
}
986+
ret := Success(newResultList);
933987
}
934988

935989
function method {:vcs_split_on_every_assert} DeserializeMapEntry(
@@ -990,6 +1044,7 @@ module DynamoToStruct {
9901044
// Can't be {:tailrecursion} because it calls BytesToAttr which might again call DeserializeMap
9911045
// However, we really need this to loop and not recurse.
9921046
// This verifies without the `by method`, but Dafny is too broken to let it verify by method
1047+
// for example, a call to DeserializeMapEntry somehow does not satisfy the decreases clause
9931048
// hence the {:verify false}
9941049
function {:vcs_split_on_every_assert} {:opaque} {:verify false} DeserializeMap(
9951050
serialized : seq<uint8>,

0 commit comments

Comments
 (0)