Skip to content

Commit b3196ad

Browse files
committed
Improve test coverage for TransformedRecordSerializer*.java
1 parent 36b7869 commit b3196ad

File tree

1 file changed

+152
-4
lines changed

1 file changed

+152
-4
lines changed

fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/common/TransformedRecordSerializerTest.java

Lines changed: 152 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.apple.foundationdb.record.logging.LogMessageKeys;
3030
import com.apple.foundationdb.record.metadata.RecordType;
3131
import com.apple.foundationdb.tuple.Tuple;
32+
import com.apple.test.BooleanSource;
3233
import com.google.common.base.Strings;
3334
import com.google.common.primitives.Bytes;
3435
import com.google.protobuf.Message;
@@ -38,6 +39,7 @@
3839
import org.junit.jupiter.params.ParameterizedTest;
3940
import org.junit.jupiter.params.provider.Arguments;
4041
import org.junit.jupiter.params.provider.MethodSource;
42+
import org.junit.jupiter.params.provider.ValueSource;
4143
import org.slf4j.Logger;
4244
import org.slf4j.LoggerFactory;
4345

@@ -47,6 +49,7 @@
4749
import javax.crypto.SecretKey;
4850
import java.nio.ByteBuffer;
4951
import java.nio.ByteOrder;
52+
import java.security.InvalidKeyException;
5053
import java.security.Key;
5154
import java.security.NoSuchAlgorithmException;
5255
import java.security.SecureRandom;
@@ -369,21 +372,26 @@ public void unrecognizedEncoding() {
369372
assertEquals(15L, e.getLogInfo().get("encoding"));
370373
}
371374

372-
@Test
373-
public void encryptWhenSerializing() throws Exception {
375+
@ParameterizedTest
376+
@BooleanSource
377+
public void encryptWhenSerializing(boolean compressToo) throws Exception {
374378
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
375379
keyGen.init(128);
376380
SecretKey key = keyGen.generateKey();
377381
TransformedRecordSerializer<Message> serializer = TransformedRecordSerializerJCE.newDefaultBuilder()
378382
.setEncryptWhenSerializing(true)
379383
.setEncryptionKey(key)
384+
.setCompressWhenSerializing(compressToo)
385+
.setCompressionLevel(9)
380386
.setWriteValidationRatio(1.0)
381387
.build();
382388

383389
MySimpleRecord mediumRecord = MySimpleRecord.newBuilder().setRecNo(1066L).setStrValueIndexed(SONNET_108).build();
384390
assertTrue(Bytes.indexOf(mediumRecord.toByteArray(), "brain".getBytes()) >= 0, "should contain clear text");
385391
byte[] serialized = serialize(serializer, mediumRecord);
386-
assertEquals(TransformedRecordSerializerPrefix.PREFIX_ENCRYPTED, serialized[0]);
392+
assertEquals(compressToo ? TransformedRecordSerializerPrefix.PREFIX_COMPRESSED_THEN_ENCRYPTED
393+
: TransformedRecordSerializerPrefix.PREFIX_ENCRYPTED,
394+
serialized[0]);
387395
assertFalse(Bytes.indexOf(serialized, "brain".getBytes()) >= 0, "should not contain clear text");
388396
Message deserialized = deserialize(serializer, Tuple.from(1066L), serialized);
389397
assertEquals(mediumRecord, deserialized);
@@ -457,8 +465,33 @@ public void corruptAnyBit() {
457465
}
458466
}
459467

468+
@ParameterizedTest
469+
@ValueSource(ints = {6, 10})
470+
public void malformedVarintEncoding(int length) {
471+
RecordSerializationException e = assertThrows(RecordSerializationException.class, () -> {
472+
TransformedRecordSerializer<Message> serializer = TransformedRecordSerializer.newDefaultBuilder().build();
473+
byte[] serialized = new byte[length];
474+
Arrays.fill(serialized, (byte)0xFF);
475+
deserialize(serializer, Tuple.from(1066L), serialized);
476+
});
477+
assertThat(e.getMessage(), containsString(length > 64 / 7 ? "transformation prefix too long"
478+
: "transformation prefix malformed"));
479+
}
480+
460481
@Test
461-
public void encryptDifferentKeys() throws Exception {
482+
public void invalidKeyNumberEncoding() {
483+
RecordSerializationException e = assertThrows(RecordSerializationException.class, () -> {
484+
TransformedRecordSerializer<Message> serializer = TransformedRecordSerializer.newDefaultBuilder().build();
485+
byte[] serialized = new byte[10];
486+
TransformedRecordSerializerPrefix.writeVarint(serialized,
487+
TransformedRecordSerializerPrefix.PREFIX_ENCRYPTED + ((long)Integer.MAX_VALUE + 1 << 3));
488+
deserialize(serializer, Tuple.from(1066L), serialized);
489+
});
490+
assertThat(e.getMessage(), containsString("unrecognized transformation encoding"));
491+
}
492+
493+
@Test
494+
public void encryptRollingKeys() throws Exception {
462495
RollingKeyManager keyManager = new RollingKeyManager();
463496
TransformedRecordSerializer<Message> serializer = TransformedRecordSerializerJCE.newDefaultBuilder()
464497
.setEncryptWhenSerializing(true)
@@ -490,6 +523,121 @@ public void encryptDifferentKeys() throws Exception {
490523
assertEquals(records, deserialized);
491524
}
492525

526+
@Test
527+
public void cannotDecryptUnknownKey() throws Exception {
528+
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
529+
keyGen.init(128);
530+
SecretKey key = keyGen.generateKey();
531+
SecureRandom random = new SecureRandom();
532+
TransformedRecordSerializer<Message> serializer = TransformedRecordSerializerJCE.newDefaultBuilder()
533+
.setEncryptWhenSerializing(true)
534+
.setKeyManager(new TransformedRecordSerializerKeyManager() {
535+
@Override
536+
public int getSerializationKey() {
537+
return 2;
538+
}
539+
540+
@Override
541+
public Key getKey(final int keyNumber) {
542+
return key;
543+
}
544+
545+
@Override
546+
public String getCipher(final int keyNumber) {
547+
return CipherPool.DEFAULT_CIPHER;
548+
}
549+
550+
@Override
551+
public Random getRandom(final int keyNumber) {
552+
return random;
553+
}
554+
})
555+
.setWriteValidationRatio(1.0)
556+
.build();
557+
558+
MySimpleRecord simpleRecord = MySimpleRecord.newBuilder().setRecNo(1066L).setStrValueIndexed("Hello").build();
559+
RecordTypeUnion unionRecord = RecordTypeUnion.newBuilder().setMySimpleRecord(simpleRecord).build();
560+
byte[] serialized = serialize(serializer, simpleRecord);
561+
TransformedRecordSerializer<Message> deserializer = TransformedRecordSerializerJCE.newDefaultBuilder()
562+
.setEncryptionKey(key)
563+
.build();
564+
RecordSerializationException e = assertThrows(RecordSerializationException.class,
565+
() -> deserialize(deserializer, Tuple.from(1066L), serialized));
566+
assertThat(e.getMessage(), containsString("only provide key number 0"));
567+
}
568+
569+
@ParameterizedTest
570+
@BooleanSource
571+
public void cannotDecryptWithoutKey(boolean jce) throws Exception {
572+
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
573+
keyGen.init(128);
574+
TransformedRecordSerializer<Message> serializer = TransformedRecordSerializerJCE.newDefaultBuilder()
575+
.setEncryptWhenSerializing(true)
576+
.setEncryptionKey(keyGen.generateKey())
577+
.setWriteValidationRatio(1.0)
578+
.build();
579+
MySimpleRecord simpleRecord = MySimpleRecord.newBuilder().setRecNo(1066L).setStrValueIndexed("Hello").build();
580+
RecordTypeUnion unionRecord = RecordTypeUnion.newBuilder().setMySimpleRecord(simpleRecord).build();
581+
byte[] serialized = serialize(serializer, simpleRecord);
582+
TransformedRecordSerializer<Message> deserializer;
583+
if (jce) {
584+
deserializer = TransformedRecordSerializerJCE.newDefaultBuilder()
585+
.setWriteValidationRatio(1.0)
586+
.build();
587+
} else {
588+
deserializer = TransformedRecordSerializer.newDefaultBuilder()
589+
.setWriteValidationRatio(1.0)
590+
.build();
591+
}
592+
RecordSerializationException e = assertThrows(RecordSerializationException.class,
593+
() -> deserialize(deserializer, Tuple.from(1066L), serialized));
594+
assertThat(e.getMessage(), containsString(jce ? "missing encryption key or provider during decryption"
595+
: "this serializer cannot decrypt"));
596+
}
597+
598+
@Test
599+
public void keyDoesNotMatchAlgorithm() throws Exception {
600+
KeyGenerator keyGen = KeyGenerator.getInstance("DES");
601+
keyGen.init(56);
602+
TransformedRecordSerializer<Message> serializer = TransformedRecordSerializerJCE.newDefaultBuilder()
603+
.setEncryptWhenSerializing(true)
604+
.setEncryptionKey(keyGen.generateKey())
605+
.setWriteValidationRatio(1.0)
606+
.build();
607+
MySimpleRecord simpleRecord = MySimpleRecord.newBuilder().setRecNo(1066L).setStrValueIndexed("Hello").build();
608+
RecordTypeUnion unionRecord = RecordTypeUnion.newBuilder().setMySimpleRecord(simpleRecord).build();
609+
RecordSerializationException e = assertThrows(RecordSerializationException.class,
610+
() -> serialize(serializer, simpleRecord));
611+
assertThat(e.getMessage(), containsString("encryption error"));
612+
assertThat(e.getCause(), instanceOf(InvalidKeyException.class));
613+
assertThat(e.getCause().getMessage(), containsString("Wrong algorithm"));
614+
}
615+
616+
@Test
617+
public void changeAlgorithm() throws Exception {
618+
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
619+
keyGen.init(128);
620+
TransformedRecordSerializer<Message> serializer = TransformedRecordSerializerJCE.newDefaultBuilder()
621+
.setEncryptWhenSerializing(true)
622+
.setEncryptionKey(keyGen.generateKey())
623+
.setWriteValidationRatio(1.0)
624+
.build();
625+
MySimpleRecord simpleRecord = MySimpleRecord.newBuilder().setRecNo(1066L).setStrValueIndexed("Hello").build();
626+
RecordTypeUnion unionRecord = RecordTypeUnion.newBuilder().setMySimpleRecord(simpleRecord).build();
627+
byte[] serialized = serialize(serializer, simpleRecord);
628+
KeyGenerator keyGen2 = KeyGenerator.getInstance("DES");
629+
keyGen2.init(56);
630+
TransformedRecordSerializer<Message> deserializer = TransformedRecordSerializerJCE.newDefaultBuilder()
631+
.setEncryptWhenSerializing(true)
632+
.setCipherName("DES")
633+
.setEncryptionKey(keyGen2.generateKey())
634+
.setWriteValidationRatio(1.0)
635+
.build();
636+
RecordSerializationException e = assertThrows(RecordSerializationException.class,
637+
() -> deserialize(deserializer, Tuple.from(1066L), serialized));
638+
assertThat(e.getMessage(), containsString("decryption error"));
639+
}
640+
493641
private boolean isCompressed(byte[] serialized) {
494642
byte headerByte = serialized[0];
495643
return headerByte == TransformedRecordSerializerPrefix.PREFIX_COMPRESSED ||

0 commit comments

Comments
 (0)