Skip to content

Commit 3274627

Browse files
committed
Enforce 2MiB max document size and 6 MB max message size for encrypted
commands JAVA-3311
1 parent eadbc5c commit 3274627

File tree

2 files changed

+93
-28
lines changed

2 files changed

+93
-28
lines changed

driver-sync/src/main/com/mongodb/client/internal/CryptConnection.java

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
@SuppressWarnings("deprecation")
6060
class CryptConnection implements Connection {
6161
private static final CodecRegistry REGISTRY = fromProviders(new BsonValueCodecProvider());
62+
private static final int MAX_MESSAGE_SIZE = 6000000;
63+
private static final int MAX_DOCUMENT_SIZE = 2097152;
6264

6365
private final Connection wrapped;
6466
private final Crypt crypt;
@@ -100,7 +102,7 @@ public <T> T command(final String database, final BsonDocument command, final Fi
100102
}
101103

102104
BasicOutputBuffer bsonOutput = new BasicOutputBuffer();
103-
BsonBinaryWriter bsonBinaryWriter = new BsonBinaryWriter(createBsonWriterSettings(), createBsonBinaryWriterSettings(),
105+
BsonBinaryWriter bsonBinaryWriter = new BsonBinaryWriter(new BsonWriterSettings(), new BsonBinaryWriterSettings(MAX_DOCUMENT_SIZE),
104106
bsonOutput, getFieldNameValidator(payload, commandFieldNameValidator, payloadFieldNameValidator));
105107
BsonWriter writer = payload == null
106108
? bsonBinaryWriter
@@ -144,25 +146,11 @@ private FieldNameValidator getFieldNameValidator(final SplittablePayload payload
144146
return new MappedFieldNameValidator(commandFieldNameValidator, rootMap);
145147
}
146148

147-
private BsonWriterSettings createBsonWriterSettings() {
148-
return new BsonWriterSettings();
149-
}
150-
151-
// TODO
152-
// Currently these settings will allow a split point that is potentially too large after encryption occurs. Need to find a way
153-
// to allow at least one document to be included in the split even if it is equal in size to maxDocumentSize, but still split at a
154-
// much smaller document size for the command document itself.
155-
private BsonBinaryWriterSettings createBsonBinaryWriterSettings() {
156-
ConnectionDescription connectionDescription = wrapped.getDescription();
157-
return new BsonBinaryWriterSettings(connectionDescription.getMaxDocumentSize());
158-
}
159-
160149
private MessageSettings createSplittablePayloadMessageSettings() {
161-
ConnectionDescription connectionDescription = wrapped.getDescription();
162150
return MessageSettings.builder()
163-
.maxBatchCount(connectionDescription.getMaxBatchCount())
164-
.maxMessageSize(connectionDescription.getMaxDocumentSize())
165-
.maxDocumentSize(connectionDescription.getMaxDocumentSize())
151+
.maxBatchCount(wrapped.getDescription().getMaxBatchCount())
152+
.maxMessageSize(MAX_MESSAGE_SIZE)
153+
.maxDocumentSize(MAX_DOCUMENT_SIZE)
166154
.build();
167155
}
168156

driver-sync/src/test/unit/com/mongodb/client/internal/CryptConnectionSpecification.groovy

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.mongodb.client.internal
1818

19+
1920
import com.mongodb.ReadPreference
2021
import com.mongodb.ServerAddress
2122
import com.mongodb.connection.ClusterId
@@ -58,9 +59,6 @@ class CryptConnectionSpecification extends Specification {
5859
def crypt = Mock(Crypt)
5960
def cryptConnection = new CryptConnection(wrappedConnection, crypt)
6061
def codec = new DocumentCodec()
61-
def maxBatchCount = 4
62-
def maxDocumentSize = 1024 * 16_000
63-
def maxMessageSize = 1024 * 16_000
6462
def encryptedCommand = toRaw(new BsonDocument('find', new BsonString('test'))
6563
.append('ssid', new BsonBinary(6 as byte, new byte[10])))
6664

@@ -85,7 +83,7 @@ class CryptConnectionSpecification extends Specification {
8583
then:
8684
_ * wrappedConnection.getDescription() >> {
8785
new ConnectionDescription(new ConnectionId(new ServerId(new ClusterId(), new ServerAddress())), 8, STANDALONE,
88-
maxBatchCount, maxDocumentSize, maxMessageSize, [])
86+
1000, 1024 * 16_000, 1024 * 48_000, [])
8987
}
9088
1 * crypt.encrypt('db', toRaw(new BsonDocument('find', new BsonString('test'))
9189
.append('filter', new BsonDocument('ssid', new BsonString('555-55-5555'))))) >> {
@@ -101,16 +99,13 @@ class CryptConnectionSpecification extends Specification {
10199
response == rawToDocument(decryptedResponse)
102100
}
103101

104-
def 'should encrypt and decrypt a command with a splittable payload'() {
102+
def 'should split at 2 MiB'() {
105103
given:
106104
def wrappedConnection = Mock(Connection)
107105
def crypt = Mock(Crypt)
108106
def cryptConnection = new CryptConnection(wrappedConnection, crypt)
109107
def codec = new DocumentCodec()
110-
def maxBatchCount = 4
111-
def maxDocumentSize = 1024 * 16_000
112-
def maxMessageSize = 1024 * 16_000
113-
def bytes = new byte[1024 * 10_000]
108+
def bytes = new byte[2097152 - 85]
114109
def payload = new SplittablePayload(INSERT, [
115110
new BsonDocumentWrapper(new Document('_id', 1).append('ssid', '555-55-5555').append('b', bytes), codec),
116111
new BsonDocumentWrapper(new Document('_id', 2).append('ssid', '666-66-6666').append('b', bytes), codec)
@@ -124,6 +119,7 @@ class CryptConnectionSpecification extends Specification {
124119

125120
def encryptedResponse = toRaw(new BsonDocument('ok', new BsonInt32(1)))
126121
def decryptedResponse = encryptedResponse
122+
127123
when:
128124
def response = cryptConnection.command('db',
129125
new BsonDocumentWrapper(new Document('insert', 'test'), codec),
@@ -135,7 +131,7 @@ class CryptConnectionSpecification extends Specification {
135131
then:
136132
_ * wrappedConnection.getDescription() >> {
137133
new ConnectionDescription(new ConnectionId(new ServerId(new ClusterId(), new ServerAddress())), 8, STANDALONE,
138-
maxBatchCount, maxDocumentSize, maxMessageSize, [])
134+
1000, 1024 * 16_000, 1024 * 48_000, [])
139135
}
140136
1 * crypt.encrypt('db',
141137
toRaw(new BsonDocument('insert', new BsonString('test')).append('documents',
@@ -157,6 +153,87 @@ class CryptConnectionSpecification extends Specification {
157153
payload.getPosition() == 1
158154
}
159155

156+
def 'should split at maxBatchCount'() {
157+
given:
158+
def wrappedConnection = Mock(Connection)
159+
def crypt = Mock(Crypt)
160+
def cryptConnection = new CryptConnection(wrappedConnection, crypt)
161+
def codec = new DocumentCodec()
162+
def maxBatchCount = 2
163+
def payload = new SplittablePayload(INSERT, [
164+
new BsonDocumentWrapper(new Document('_id', 1), codec),
165+
new BsonDocumentWrapper(new Document('_id', 2), codec),
166+
new BsonDocumentWrapper(new Document('_id', 3), codec)
167+
])
168+
def encryptedCommand = toRaw(new BsonDocument('insert', new BsonString('test')).append('documents', new BsonArray(
169+
[
170+
new BsonDocument('_id', new BsonInt32(1)),
171+
new BsonDocument('_id', new BsonInt32(2)),
172+
173+
])))
174+
175+
def encryptedResponse = toRaw(new BsonDocument('ok', new BsonInt32(1)))
176+
def decryptedResponse = encryptedResponse
177+
178+
when:
179+
def response = cryptConnection.command('db',
180+
new BsonDocumentWrapper(new Document('insert', 'test'), codec),
181+
new NoOpFieldNameValidator(), ReadPreference.primary(), new BsonDocumentCodec(),
182+
NoOpSessionContext.INSTANCE, true,
183+
payload,
184+
new NoOpFieldNameValidator())
185+
186+
then:
187+
_ * wrappedConnection.getDescription() >> {
188+
new ConnectionDescription(new ConnectionId(new ServerId(new ClusterId(), new ServerAddress())), 8, STANDALONE,
189+
maxBatchCount, 1024 * 16_000, 1024 * 48_000, [])
190+
}
191+
1 * crypt.encrypt('db',
192+
toRaw(new BsonDocument('insert', new BsonString('test')).append('documents',
193+
new BsonArray([
194+
new BsonDocument('_id', new BsonInt32(1)),
195+
new BsonDocument('_id', new BsonInt32(2))
196+
])))) >> {
197+
encryptedCommand
198+
}
199+
1 * wrappedConnection.command('db', encryptedCommand, _ as NoOpFieldNameValidator, ReadPreference.primary(),
200+
_ as RawBsonDocumentCodec, NoOpSessionContext.INSTANCE, true, null, null) >> {
201+
encryptedResponse
202+
}
203+
1 * crypt.decrypt(encryptedResponse) >> {
204+
decryptedResponse
205+
}
206+
response == rawToBsonDocument(decryptedResponse)
207+
payload.getPosition() == 2
208+
}
209+
210+
def 'should throw if command document is large than 2 MiB'() {
211+
given:
212+
def wrappedConnection = Mock(Connection)
213+
def crypt = Mock(Crypt)
214+
def cryptConnection = new CryptConnection(wrappedConnection, crypt)
215+
def codec = new DocumentCodec()
216+
def bytes = new byte[2097152 - 84]
217+
def payload = new SplittablePayload(INSERT, [
218+
new BsonDocumentWrapper(new Document('_id', 1).append('ssid', '555-55-5555').append('b', bytes), codec),
219+
])
220+
221+
when:
222+
cryptConnection.command('db',
223+
new BsonDocumentWrapper(new Document('insert', 'test'), codec),
224+
new NoOpFieldNameValidator(), ReadPreference.primary(), new BsonDocumentCodec(),
225+
NoOpSessionContext.INSTANCE, true,
226+
payload,
227+
new NoOpFieldNameValidator())
228+
229+
then:
230+
_ * wrappedConnection.getDescription() >> {
231+
new ConnectionDescription(new ConnectionId(new ServerId(new ClusterId(), new ServerAddress())), 8, STANDALONE,
232+
1000, 1024 * 16_000, 1024 * 48_000, [])
233+
}
234+
thrown(BsonMaximumSizeExceededException)
235+
}
236+
160237
RawBsonDocument toRaw(BsonDocument document) {
161238
def buffer = new BasicOutputBuffer()
162239
def writer = new BsonBinaryWriter(buffer)

0 commit comments

Comments
 (0)