Skip to content

Commit 08cf1a3

Browse files
authored
chore: add a test for offset against BlockEncryptionHandler (#2115)
1 parent adb0a42 commit 08cf1a3

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed

src/test/java/com/amazonaws/encryptionsdk/internal/BlockEncryptionHandlerTest.java

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,29 @@
1616
import static org.junit.Assert.assertArrayEquals;
1717
import static org.junit.Assert.assertEquals;
1818

19+
import com.amazonaws.encryptionsdk.AwsCrypto;
1920
import com.amazonaws.encryptionsdk.CryptoAlgorithm;
21+
import com.amazonaws.encryptionsdk.CryptoInputStream;
22+
import com.amazonaws.encryptionsdk.CryptoOutputStream;
23+
import com.amazonaws.encryptionsdk.CryptoResult;
2024
import com.amazonaws.encryptionsdk.TestUtils;
2125
import com.amazonaws.encryptionsdk.model.CipherBlockHeaders;
26+
import java.io.ByteArrayInputStream;
27+
import java.io.ByteArrayOutputStream;
28+
import java.nio.ByteBuffer;
29+
import java.nio.charset.StandardCharsets;
30+
import java.security.SecureRandom;
31+
import java.util.Collections;
2232
import javax.crypto.SecretKey;
2333
import javax.crypto.spec.SecretKeySpec;
2434
import org.junit.Before;
2535
import org.junit.Test;
36+
import software.amazon.awssdk.utils.StringUtils;
37+
import software.amazon.cryptography.materialproviders.IKeyring;
38+
import software.amazon.cryptography.materialproviders.MaterialProviders;
39+
import software.amazon.cryptography.materialproviders.model.AesWrappingAlg;
40+
import software.amazon.cryptography.materialproviders.model.CreateRawAesKeyringInput;
41+
import software.amazon.cryptography.materialproviders.model.MaterialProvidersConfig;
2642

2743
public class BlockEncryptionHandlerTest {
2844
private final CryptoAlgorithm cryptoAlgorithm_ = TestUtils.DEFAULT_TEST_CRYPTO_ALG;
@@ -67,4 +83,88 @@ public void correctIVGenerated() throws Exception {
6783
},
6884
headers.getNonce());
6985
}
86+
87+
/**
88+
* This isn't a unit test, but it reproduces a bug found in the FrameEncryptionHandler where the
89+
* stream would be truncated when the offset is >0. For the sake of robustness, the same test is
90+
* included against the BlockEncryptionHandler.
91+
*
92+
* @throws Exception
93+
*/
94+
@Test
95+
public void testStreamTruncation() throws Exception {
96+
// Initialize AES key and keyring
97+
SecureRandom rnd = new SecureRandom();
98+
byte[] rawKey = new byte[16];
99+
rnd.nextBytes(rawKey);
100+
SecretKeySpec cryptoKey = new SecretKeySpec(rawKey, "AES");
101+
MaterialProviders materialProviders =
102+
MaterialProviders.builder()
103+
.MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
104+
.build();
105+
CreateRawAesKeyringInput keyringInput =
106+
CreateRawAesKeyringInput.builder()
107+
.wrappingKey(ByteBuffer.wrap(cryptoKey.getEncoded()))
108+
.keyNamespace("Example")
109+
.keyName("RandomKey")
110+
.wrappingAlg(AesWrappingAlg.ALG_AES128_GCM_IV12_TAG16)
111+
.build();
112+
IKeyring keyring = materialProviders.CreateRawAesKeyring(keyringInput);
113+
// Use unframed (block) encryption
114+
AwsCrypto crypto = AwsCrypto.builder().withEncryptionFrameSize(0).build();
115+
116+
String testDataString = StringUtils.repeat("Hello, World! ", 5_000);
117+
118+
int startOffset = 100; // The data will start from this offset
119+
byte[] inputDataWithOffset = new byte[10_000];
120+
// the length of the actual data
121+
int dataLength = inputDataWithOffset.length - startOffset;
122+
// copy some data, starting at the startOffset
123+
// so the first |startOffset| bytes are 0s
124+
System.arraycopy(
125+
testDataString.getBytes(StandardCharsets.UTF_8),
126+
0,
127+
inputDataWithOffset,
128+
startOffset,
129+
dataLength);
130+
// decryptData (non-streaming) doesn't know about the offset
131+
// it will strip out the original 0s
132+
byte[] expectedOutput = new byte[10_000 - startOffset];
133+
System.arraycopy(
134+
testDataString.getBytes(StandardCharsets.UTF_8), 0, expectedOutput, 0, dataLength);
135+
136+
// Encrypt the data
137+
byte[] encryptedData;
138+
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
139+
try (CryptoOutputStream cryptoOutput =
140+
crypto.createEncryptingStream(keyring, os, Collections.emptyMap())) {
141+
cryptoOutput.write(inputDataWithOffset, startOffset, dataLength);
142+
}
143+
encryptedData = os.toByteArray();
144+
}
145+
146+
// Check non-streaming decrypt
147+
CryptoResult<byte[], ?> nonStreamDecrypt = crypto.decryptData(keyring, encryptedData);
148+
assertEquals(dataLength, nonStreamDecrypt.getResult().length);
149+
assertArrayEquals(expectedOutput, nonStreamDecrypt.getResult());
150+
151+
// Check streaming decrypt
152+
int decryptedLength = 0;
153+
byte[] decryptedData = new byte[inputDataWithOffset.length];
154+
try (ByteArrayInputStream is = new ByteArrayInputStream(encryptedData);
155+
CryptoInputStream cryptoInput = crypto.createDecryptingStream(keyring, is)) {
156+
int offset = startOffset;
157+
do {
158+
int bytesRead = cryptoInput.read(decryptedData, offset, decryptedData.length - offset);
159+
if (bytesRead <= 0) {
160+
break; // End of stream
161+
}
162+
offset += bytesRead;
163+
decryptedLength += bytesRead;
164+
} while (true);
165+
}
166+
assertEquals(dataLength, decryptedLength);
167+
// These arrays will be offset, i.e. the first |startOffset| bytes are 0s
168+
assertArrayEquals(inputDataWithOffset, decryptedData);
169+
}
70170
}

0 commit comments

Comments
 (0)