Skip to content

Commit 9e4115a

Browse files
committed
m
1 parent 324af84 commit 9e4115a

File tree

9 files changed

+231
-39
lines changed

9 files changed

+231
-39
lines changed

DynamoDbEncryption/dafny/DynamoDbEncryption/src/Beacon.dfy

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ module BaseBeacon {
113113
partOnly : bool,
114114
asSet : bool,
115115
share : Option<string>,
116-
numberOfBuckets : Option<BucketCount>
116+
numberOfBuckets : BucketCount
117117
)
118118
: (ret : Result<ValidStandardBeacon, Error>)
119119
ensures ret.Success? ==>
@@ -123,8 +123,6 @@ module BaseBeacon {
123123
var termLoc :- TermLoc.MakeTermLoc(loc);
124124
var beaconName := BeaconPrefix + name;
125125
:- Need(DDB.IsValid_AttributeName(beaconName), E(beaconName + " is not a valid attribute name."));
126-
var numBuckets : nat := if numberOfBuckets.Some? then numberOfBuckets.value as nat else 0;
127-
// :- Need(numBuckets < 256, E(beaconName + " has numberOfBuckets greater than 255"));
128126
Success(StandardBeacon.StandardBeacon(
129127
BeaconBase (
130128
client := client,
@@ -136,7 +134,7 @@ module BaseBeacon {
136134
partOnly,
137135
asSet,
138136
share,
139-
numBuckets as OptBucketCount
137+
numberOfBuckets
140138
))
141139
}
142140
datatype StandardBeacon = StandardBeacon (
@@ -146,15 +144,15 @@ module BaseBeacon {
146144
partOnly : bool,
147145
asSet : bool,
148146
share : Option<string>,
149-
numberOfBuckets : OptBucketCount
147+
numberOfBuckets : BucketCount
150148
) {
151149

152150
function method constrained_bucket(bucket : BucketNumber) : BucketBytes
153151
{
154152
if numberOfBuckets == 0 || bucket == 0 then
155153
BucketNumberToBytes(bucket)
156154
else
157-
var newBucket : BucketNumber := (bucket as OptBucketCount % numberOfBuckets) as BucketNumber;
155+
var newBucket : BucketNumber := (bucket as BucketCount % numberOfBuckets) as BucketNumber;
158156
BucketNumberToBytes(newBucket)
159157
}
160158

DynamoDbEncryption/dafny/DynamoDbEncryption/src/CompoundBeacon.dfy

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,15 @@ module CompoundBeacon {
132132
&& OrderedParts(parts, numSigned)
133133
}
134134

135+
function method getNumQueries(globalMax : BucketCount) : BucketCount
136+
{
137+
var counts := GetBucketCountsFromValue(DDB.AttributeValue.S(""));
138+
if counts.Failure? then
139+
1
140+
else
141+
lcmSeq(counts.value, globalMax)
142+
}
143+
135144
// no prefix is a prefix of another prefix
136145
// that is, no ambiguity when determining which prefix is used in a value
137146
predicate ValidPrefixSet()
@@ -328,6 +337,28 @@ module CompoundBeacon {
328337
Success(DDB.AttributeValue.S(result))
329338
}
330339

340+
function method GetBucketCountsFromParts(inParts : seq<BeaconPart>) : seq<BucketCount>
341+
{
342+
if |inParts| == 0 then
343+
[]
344+
else if inParts[0].Encrypted? then
345+
[inParts[0].beacon.numberOfBuckets]
346+
else
347+
[]
348+
}
349+
350+
// for the given attribute value, return the beacon value
351+
function method GetBucketCountsFromValue(value : DDB.AttributeValue) : Result<seq<BucketCount>, Error>
352+
{
353+
if !value.S? then
354+
Failure(E("CompoundBeacon " + base.name + " can only be queried as a string, not as " + AttrTypeToStr(value)))
355+
else
356+
var parts := Split(value.S, split);
357+
var partsUsed :- Sequence.MapWithResult(s => getPartFromPrefix(s), parts);
358+
var _ :- ValidatePartOrder(partsUsed, value.S);
359+
Success(GetBucketCountsFromParts(partsUsed))
360+
}
361+
331362
// return the beacon value for this constructor, if possible
332363
function method {:opaque} {:tailrecursion} TryConstructor(
333364
consFields : seq<ConstructorPart>,

DynamoDbEncryption/dafny/DynamoDbEncryption/src/ConfigToInfo.dfy

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -526,18 +526,25 @@ module SearchConfigToInfo {
526526
Failure(E("Beacon " + name + " is shared to " + share + " which is not defined."))
527527
}
528528

529-
predicate method BucketConstraintOk(outer : DynamoDbTableEncryptionConfig, inner : Option<BucketCount>)
529+
function method GetBucketCount(outer : DynamoDbTableEncryptionConfig, inner : Option<BucketCount>, name : string) :
530+
Result<BucketCount,Error>
530531
{
531-
if inner.None? || outer.search.None? || |outer.search.value.versions| == 0 then
532-
true
532+
if outer.search.None? || |outer.search.value.versions| == 0 then
533+
Success(1)
533534
else
534535
var num := outer.search.value.versions[0].numberOfBuckets;
535-
if num.None? then
536-
false
536+
if BucketCountNone(num) then
537+
if !BucketCountNone(inner) then
538+
Failure(E("Constrained numberOfBuckets for " + name + " is " + Base10Int2String(inner.value as int) + " but there is not global numberOfBuckets set."))
539+
else
540+
Success(1)
541+
else if BucketCountNone(inner) then
542+
Success(num.value)
543+
else if inner.value < num.value then
544+
Success(inner.value)
537545
else
538-
inner.value <= num.value
546+
Failure(E("Constrained numberOfBuckets for " + name + " is " + Base10Int2String(inner.value as int) + " but it must be less than the global numberOfBuckets " + Base10Int2String(num.value as int)))
539547
}
540-
541548
// convert configured StandardBeacons to internal Beacons
542549
method {:tailrecursion} AddStandardBeacons(
543550
beacons : seq<StandardBeacon>,
@@ -601,9 +608,9 @@ module SearchConfigToInfo {
601608
case sharedSet(t) => share := Some(t.other); isAsSet := true;
602609
}
603610
}
604-
:- Need(BucketConstraintOk(outer, beacons[0].numberOfBuckets), E("Constrained numberOfBuckets for " + beacons[0].name + " must be less than the global numberOfBuckets."));
611+
var bucketCount :- GetBucketCount(outer, beacons[0].numberOfBuckets, beacons[0].name);
605612
var newBeacon :- B.MakeStandardBeacon(client, beacons[0].name, beacons[0].length as B.BeaconLength, locString,
606-
isPartOnly, isAsSet, share, beacons[0].numberOfBuckets);
613+
isPartOnly, isAsSet, share, bucketCount);
607614

608615
//= specification/searchable-encryption/search-config.md#beacon-version-initialization
609616
//# Initialization MUST fail if the [terminal location](virtual.md#terminal-location)

DynamoDbEncryption/dafny/DynamoDbEncryption/src/DDBSupport.dfy

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,9 +315,8 @@ module DynamoDBSupport {
315315
:- Need(values.None? || BucketQueriesName !in values.value, E(""));
316316

317317
// No bucket specified is OK if no encrypted fields are searched
318-
var encrypted := set k <- actions | actions[k] == SE.ENCRYPT_AND_SIGN :: k;
319-
var filterHasEncField := Filter.UsesEncryptedField(Filter.ParseExprOpt(filterExpr), encrypted, names);
320-
var keyHasEncField := Filter.UsesEncryptedField(Filter.ParseExprOpt(keyExpr), encrypted, names);
318+
var filterHasEncField := Filter.UsesEncryptedField(Filter.ParseExprOpt(filterExpr), actions, names);
319+
var keyHasEncField := Filter.UsesEncryptedField(Filter.ParseExprOpt(keyExpr), actions, names);
321320
if keyHasEncField.Some? || filterHasEncField.Some? {
322321
return Failure(E("When numberOfBuckets is greater than one, FilterExpression must start with 'aws_dbe_bucket = NN && '"));
323322
} else {
@@ -343,6 +342,15 @@ module DynamoDBSupport {
343342

344343
var foo :- ExtractBucket(search.value.curr(), req.FilterExpression, req.KeyConditionExpression, req.ExpressionAttributeNames, req.ExpressionAttributeValues, actions);
345344
var (newValues, bucket, bucket_queries) := foo;
345+
var numQueries :- Filter.GetNumQueries(actions, req.KeyConditionExpression, req.ExpressionAttributeNames, search.value.curr());
346+
if numQueries < bucket {
347+
return Failure(E("Bucket number was " + String.Base10Int2String(bucket as int) + " but should have been less than number of queries : " + String.Base10Int2String(numQueries as int)));
348+
}
349+
if bucket_queries.Some? {
350+
if numQueries != bucket_queries.value {
351+
return Failure(E("Number of queries was " + String.Base10Int2String(numQueries as int) + " but should have been " + String.Base10Int2String(bucket_queries.value as int)));
352+
}
353+
}
346354
var oldContext := Filter.ExprContext(req.KeyConditionExpression, req.FilterExpression, newValues, req.ExpressionAttributeNames);
347355
var newContext :- Filter.DoBeaconize(search.value.curr(), oldContext, keyId, bucket, bucket_queries);
348356
return Success(req.(

DynamoDbEncryption/dafny/DynamoDbEncryption/src/FilterExpr.dfy

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,6 +1796,10 @@ module DynamoDBFilterExpr {
17961796
allUnchanged := false;
17971797
break;
17981798
}
1799+
if new_context.values.value[new_values[i]] != old_context.values.value[new_values[i]] {
1800+
allUnchanged := false;
1801+
break;
1802+
}
17991803
}
18001804
if allUnchanged {
18011805
return Success(old_context);
@@ -1924,15 +1928,15 @@ module DynamoDBFilterExpr {
19241928
// return an error if any encrypted field exists in the query
19251929
method UsesEncryptedField(
19261930
expr : seq<Token>,
1927-
encrypted : set<string>,
1931+
actions : AttributeActions,
19281932
names : Option<DDB.ExpressionAttributeNameMap>
19291933
)
19301934
returns (output : Option<string>)
19311935
{
19321936
for i := 0 to |expr| {
19331937
if expr[i].Attr? {
19341938
var name := GetAttrName(expr[i], names);
1935-
if name in encrypted {
1939+
if name in actions && actions[name] == SE.ENCRYPT_AND_SIGN {
19361940
return Some(name);
19371941
}
19381942
}
@@ -1942,12 +1946,12 @@ module DynamoDBFilterExpr {
19421946

19431947
method TestParsedExpr(
19441948
expr : seq<Token>,
1945-
encrypted : set<string>,
1949+
actions : AttributeActions,
19461950
names : Option<DDB.ExpressionAttributeNameMap>
19471951
)
19481952
returns (output : Outcome<Error>)
19491953
{
1950-
var hasEncField := UsesEncryptedField(expr, encrypted, names);
1954+
var hasEncField := UsesEncryptedField(expr, actions, names);
19511955
if hasEncField.Some? {
19521956
return Fail(E("Query is using encrypted field : " + hasEncField.value + "."));
19531957
}
@@ -1963,14 +1967,76 @@ module DynamoDBFilterExpr {
19631967
)
19641968
returns (output : Result<bool, Error>)
19651969
{
1966-
var encrypted := set k <- actions | actions[k] == SE.ENCRYPT_AND_SIGN :: k;
19671970
if keyExpr.Some? {
1968-
:- TestParsedExpr(ParseExpr(keyExpr.value), encrypted, names);
1971+
:- TestParsedExpr(ParseExpr(keyExpr.value), actions, names);
19691972
}
19701973
if filterExpr.Some? {
1971-
:- TestParsedExpr(ParseExpr(filterExpr.value), encrypted, names);
1974+
:- TestParsedExpr(ParseExpr(filterExpr.value), actions, names);
19721975
}
19731976
return Success(true);
19741977
}
19751978

1979+
// return an list of encrypted attributes used in expression
1980+
method GetEncryptedAttrs (
1981+
actions : AttributeActions,
1982+
expr : string,
1983+
names : Option<DDB.ExpressionAttributeNameMap>
1984+
)
1985+
returns (output : seq<string>)
1986+
{
1987+
var pExpr := ParseExpr(expr);
1988+
var result : seq<string> := [];
1989+
for i := 0 to |pExpr| {
1990+
if pExpr[i].Attr? {
1991+
var name := GetAttrName(pExpr[i], names);
1992+
if name in actions && actions[name] == SE.ENCRYPT_AND_SIGN {
1993+
result := result + [GetAttrName(pExpr[i], names)];
1994+
}
1995+
}
1996+
}
1997+
return result;
1998+
}
1999+
2000+
// return the number of queries necessary for these encrypted attributes
2001+
method GetNumQueriesForAttrs(attrs : seq<string>, b : SI.BeaconVersion) returns (output : Result<BucketCount, Error>)
2002+
ensures output.Success? ==> 0 < output.value
2003+
{
2004+
if |attrs| == 0 {
2005+
return Success(1);
2006+
} else if |attrs| == 1 {
2007+
var attr := attrs[0];
2008+
if attr !in b.beacons {
2009+
return Failure(E("No beacon for " + attr));
2010+
}
2011+
return Success(b.beacons[attr].getNumQueries(b.numBuckets));
2012+
} else if |attrs| == 2 {
2013+
var attr1 := attrs[0];
2014+
if attr1 !in b.beacons {
2015+
return Failure(E("No beacon for " + attr1));
2016+
}
2017+
var attr2 := attrs[1];
2018+
if attr2 !in b.beacons {
2019+
return Failure(E("No beacon for " + attr2));
2020+
}
2021+
return Success(lcmBucket(b.beacons[attr1].getNumQueries(b.numBuckets), b.beacons[attr2].getNumQueries(b.numBuckets), b.numBuckets));
2022+
} else {
2023+
return Failure(E("More than two attributes not implemented yet"));
2024+
}
2025+
}
2026+
2027+
method GetNumQueries (
2028+
actions : AttributeActions,
2029+
expr : Option<string>,
2030+
names : Option<DDB.ExpressionAttributeNameMap>,
2031+
b : SI.BeaconVersion
2032+
)
2033+
returns (output : Result<BucketCount, Error>)
2034+
{
2035+
if expr.None? {
2036+
return Success(1);
2037+
}
2038+
var attrs := GetEncryptedAttrs(actions, expr.value, names);
2039+
output := GetNumQueriesForAttrs(attrs, b);
2040+
}
2041+
19762042
}

DynamoDbEncryption/dafny/DynamoDbEncryption/src/SearchInfo.dfy

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,13 @@ module SearchableEncryptionInfo {
544544
| Standard(std : BaseBeacon.ValidStandardBeacon)
545545
| Compound(cmp : CompoundBeacon.ValidCompoundBeacon)
546546
{
547+
function method getNumQueries(globalMax : BucketCount) : BucketCount
548+
{
549+
if Standard? then
550+
std.numberOfBuckets
551+
else
552+
cmp.getNumQueries(globalMax)
553+
}
547554
predicate method isEncrypted()
548555
{
549556
if Standard? then

DynamoDbEncryption/dafny/DynamoDbEncryption/src/Util.dfy

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ module DynamoDbEncryptionUtil {
101101
[x as uint8]
102102
}
103103

104+
// Java is broken, None becomes Some(0)
105+
predicate method BucketCountNone(x : Option<BucketCount>)
106+
{
107+
x.None? || x.value == 0
108+
}
109+
104110
predicate method Valid_BucketBytes(x : seq<uint8>)
105111
{
106112
&& |x| <= 1
@@ -113,4 +119,68 @@ module DynamoDbEncryptionUtil {
113119
print x,"\n";
114120
return ();
115121
}
122+
function printFromFunction2<T, U>(x: T, y : U): () {
123+
()
124+
} by method {
125+
print x, " ", y, "\n";
126+
return ();
127+
}
128+
function printFromFunction3<T, U, V>(x: T, y : U, z : V): () {
129+
()
130+
} by method {
131+
print x, " ", y, " ", z, "\n";
132+
return ();
133+
}
134+
135+
function method gcd(a : nat, b : nat) : nat
136+
requires 0 < a || 0 < b
137+
ensures 0 < gcd(a, b)
138+
ensures 0 < b ==> gcd(a, b) <= b
139+
decreases b
140+
{
141+
if b == 0 then
142+
a
143+
else
144+
gcd(b, a % b)
145+
}
146+
147+
function method lcm(a : nat, b : nat) : nat
148+
requires 0 < a && 0 < b
149+
ensures 0 < lcm(a, b)
150+
{
151+
(a / gcd(a, b)) * b
152+
}
153+
154+
function method lcmBucket(a : BucketCount, b : BucketCount, max : BucketCount) : BucketCount
155+
requires 0 < a && 0 < b
156+
ensures 0 < lcmBucket(a, b, max)
157+
{
158+
if a == 1 || b == max then
159+
b
160+
else if b == 1 || a == max then
161+
a
162+
else
163+
var result := lcm(a as nat, b as nat);
164+
if result < max as nat then
165+
result as BucketCount
166+
else
167+
max
168+
}
169+
170+
function method lcmSeq(values : seq<BucketCount>, max : BucketCount) : BucketCount
171+
// requires forall i <- values :: i <= max
172+
decreases |values|
173+
{
174+
if |values| == 0 then
175+
1
176+
else if |values| == 1 then
177+
values[0]
178+
else
179+
var res := lcmBucket(values[0], values[1], max);
180+
if |values| == 2 then
181+
res as BucketCount
182+
else
183+
lcmSeq([res as BucketCount] + values[2..], max)
184+
}
185+
116186
}

0 commit comments

Comments
 (0)