Skip to content

Commit a6d6309

Browse files
fix: validate entire ciphertext has been processed before returning (#191)
* fix: validate entire ciphertext has been processed before returning * Updating changelog
1 parent a85f61a commit a6d6309

File tree

8 files changed

+147
-17
lines changed

8 files changed

+147
-17
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# Changelog
22

3-
## 1.6.2 -- unreleased
3+
## 1.6.2 -- 2020-05-26
44

55
### Patches
66
* Validate final frame length does not exceed the frame size in the message header [PR #166](https://github.com/aws/aws-encryption-sdk-java/pull/166)
7+
* Validate entire ciphertext has been processed before returning [PR #191](https://github.com/aws/aws-encryption-sdk-java/pull/191)
78

89
### Maintenance
910
* Update AWS Java SDK version from 1.11.561 to 1.11.704. [PR #186](https://github.com/aws/aws-encryption-sdk-java/pull/186)

src/main/java/com/amazonaws/encryptionsdk/internal/BlockDecryptionHandler.java

+12-5
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,15 @@
1313

1414
package com.amazonaws.encryptionsdk.internal;
1515

16-
import java.util.Arrays;
17-
18-
import javax.crypto.Cipher;
19-
import javax.crypto.SecretKey;
20-
2116
import com.amazonaws.encryptionsdk.CryptoAlgorithm;
2217
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
2318
import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
2419
import com.amazonaws.encryptionsdk.model.CipherBlockHeaders;
2520

21+
import javax.crypto.Cipher;
22+
import javax.crypto.SecretKey;
23+
import java.util.Arrays;
24+
2625
/**
2726
* The block decryption handler is an implementation of CryptoHandler that
2827
* provides methods to decrypt content encrypted and stored in a single block.
@@ -97,6 +96,11 @@ public BlockDecryptionHandler(final SecretKey decryptionKey, final short nonceLe
9796
synchronized public ProcessingSummary processBytes(final byte[] in, final int off, final int len,
9897
final byte[] out,
9998
final int outOff) throws AwsCryptoException {
99+
100+
if (complete_) {
101+
throw new AwsCryptoException("Ciphertext has already been processed.");
102+
}
103+
100104
final byte[] bytesToParse = new byte[unparsedBytes_.length + len];
101105
// If there were previously unparsed bytes, add them as the first
102106
// set of bytes to be parsed in this call.
@@ -166,6 +170,9 @@ synchronized public ProcessingSummary processBytes(final byte[] in, final int of
166170
*/
167171
@Override
168172
synchronized public int doFinal(final byte[] out, final int outOff) throws BadCiphertextException {
173+
if (!complete_) {
174+
throw new BadCiphertextException("Unable to process entire ciphertext.");
175+
}
169176
return 0;
170177
}
171178

src/main/java/com/amazonaws/encryptionsdk/internal/DecryptionHandler.java

+4
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@ public int doFinal(final byte[] out, final int outOff) throws BadCiphertextExcep
326326
} else {
327327
int result = contentCryptoHandler_.doFinal(out, outOff);
328328

329+
if (!ciphertextHeaders_.isComplete() || !contentCryptoHandler_.isComplete()) {
330+
throw new BadCiphertextException("Unable to process entire ciphertext.");
331+
}
332+
329333
return result;
330334
}
331335
}

src/main/java/com/amazonaws/encryptionsdk/internal/FrameDecryptionHandler.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,16 @@ public FrameDecryptionHandler(final SecretKey decryptionKey, final short nonceLe
7878
*
7979
* @param in
8080
* the input byte array.
81-
* @param inOff
81+
* @param off
8282
* the offset into the in array where the data to be decrypted starts.
83-
* @param inLen
83+
* @param len
8484
* the number of bytes to be decrypted.
8585
* @param out
8686
* the output buffer the decrypted plaintext bytes go into.
8787
* @param outOff
8888
* the offset into the output byte array the decrypted data starts at.
8989
* @return the number of bytes written to out and processed
90-
* @throws InvalidCiphertextException
90+
* @throws BadCiphertextException
9191
* if frame number is invalid/out-of-order or if the bytes do not decrypt correctly.
9292
* @throws AwsCryptoException
9393
* if the content type found in the headers is not of frame type.
@@ -96,6 +96,11 @@ public FrameDecryptionHandler(final SecretKey decryptionKey, final short nonceLe
9696
public ProcessingSummary processBytes(final byte[] in, final int off, final int len, final byte[] out,
9797
final int outOff)
9898
throws BadCiphertextException, AwsCryptoException {
99+
100+
if (complete_) {
101+
throw new AwsCryptoException("Ciphertext has already been processed.");
102+
}
103+
99104
final long totalBytesToParse = unparsedBytes_.length + (long) len;
100105
if (totalBytesToParse > Integer.MAX_VALUE) {
101106
throw new AwsCryptoException(
@@ -200,6 +205,10 @@ public ProcessingSummary processBytes(final byte[] in, final int off, final int
200205
*/
201206
@Override
202207
public int doFinal(final byte[] out, final int outOff) {
208+
if (!complete_) {
209+
throw new BadCiphertextException("Unable to process entire ciphertext.");
210+
}
211+
203212
return 0;
204213
}
205214

src/test/java/com/amazonaws/encryptionsdk/AwsCryptoTest.java

+51
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.io.InputStream;
3333
import java.io.OutputStream;
3434
import java.nio.charset.StandardCharsets;
35+
import java.util.Arrays;
3536
import java.util.EnumSet;
3637
import java.util.HashMap;
3738
import java.util.Map;
@@ -112,6 +113,31 @@ private void doTamperedEncryptDecrypt(final CryptoAlgorithm cryptoAlg, final int
112113
}
113114
}
114115

116+
private void doTruncatedEncryptDecrypt(final CryptoAlgorithm cryptoAlg, final int byteSize, final int frameSize) {
117+
final byte[] plaintextBytes = new byte[byteSize];
118+
119+
final Map<String, String> encryptionContext = new HashMap<>(1);
120+
encryptionContext.put("ENC1", "Encrypt-decrypt test with %d" + byteSize);
121+
122+
encryptionClient_.setEncryptionAlgorithm(cryptoAlg);
123+
encryptionClient_.setEncryptionFrameSize(frameSize);
124+
125+
final byte[] cipherText = encryptionClient_.encryptData(
126+
masterKeyProvider,
127+
plaintextBytes,
128+
encryptionContext).getResult();
129+
final byte[] truncatedCipherText = Arrays.copyOf(cipherText, cipherText.length - 1);
130+
try {
131+
encryptionClient_.decryptData(
132+
masterKeyProvider,
133+
truncatedCipherText
134+
).getResult();
135+
Assert.fail("Expected BadCiphertextException");
136+
} catch (final BadCiphertextException ex) {
137+
// Expected exception
138+
}
139+
}
140+
115141
private void doEncryptDecryptWithParsedCiphertext(final int byteSize, final int frameSize) {
116142
final byte[] plaintextBytes = new byte[byteSize];
117143

@@ -195,6 +221,31 @@ public void encryptDecryptWithBadSignature() {
195221
}
196222
}
197223

224+
@Test
225+
public void encryptDecryptWithTruncatedCiphertext() {
226+
for (final CryptoAlgorithm cryptoAlg : EnumSet.allOf(CryptoAlgorithm.class)) {
227+
final int[] frameSizeToTest = TestUtils.getFrameSizesToTest(cryptoAlg);
228+
229+
for (int i = 0; i < frameSizeToTest.length; i++) {
230+
final int frameSize = frameSizeToTest[i];
231+
int[] bytesToTest = { 0, 1, frameSize - 1, frameSize, frameSize + 1, (int) (frameSize * 1.5),
232+
frameSize * 2, 1000000 };
233+
234+
for (int j = 0; j < bytesToTest.length; j++) {
235+
final int byteSize = bytesToTest[j];
236+
237+
if (byteSize > 500_000) {
238+
continue;
239+
}
240+
241+
if (byteSize >= 0) {
242+
doTruncatedEncryptDecrypt(cryptoAlg, byteSize, frameSize);
243+
}
244+
}
245+
}
246+
}
247+
}
248+
198249
@Test
199250
public void encryptDecryptWithParsedCiphertext() {
200251
for (final CryptoAlgorithm cryptoAlg : EnumSet.allOf(CryptoAlgorithm.class)) {

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

+24-7
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
package com.amazonaws.encryptionsdk.internal;
1515

16-
import static org.junit.Assert.assertEquals;
1716
import static org.junit.Assert.assertTrue;
1817

1918
import java.nio.ByteBuffer;
@@ -22,6 +21,7 @@
2221
import javax.crypto.SecretKey;
2322
import javax.crypto.spec.SecretKeySpec;
2423

24+
import com.amazonaws.encryptionsdk.exception.BadCiphertextException;
2525
import org.junit.Before;
2626
import org.junit.Test;
2727

@@ -58,11 +58,9 @@ public void estimateOutputSize() {
5858
assertTrue(outSize >= inLen);
5959
}
6060

61-
@Test
62-
public void decryptWithoutHeaders() {
63-
final byte[] out = new byte[1];
64-
final int returnedLen = blockDecryptionHandler_.doFinal(out, 0);
65-
assertEquals(0, returnedLen);
61+
@Test(expected= BadCiphertextException.class)
62+
public void doFinalCalledWhileNotComplete() {
63+
blockDecryptionHandler_.doFinal(new byte[1], 0);
6664
}
6765

6866
@Test(expected = AwsCryptoException.class)
@@ -90,4 +88,23 @@ public void decryptMaxContentLength() {
9088
final byte[] decryptedOut = new byte[decryptedOutLen];
9189
blockDecryptionHandler_.processBytes(outBuff.array(), 0, outBuff.array().length, decryptedOut, 0);
9290
}
93-
}
91+
92+
@Test(expected = AwsCryptoException.class)
93+
public void processBytesCalledWhileComplete() {
94+
final BlockEncryptionHandler blockEncryptionHandler = new BlockEncryptionHandler(
95+
dataKey_,
96+
nonceLen_,
97+
cryptoAlgorithm_,
98+
messageId_);
99+
final byte[] in = new byte[0];
100+
final int outLen = blockEncryptionHandler.estimateOutputSize(in.length);
101+
final byte[] out = new byte[outLen];
102+
103+
blockEncryptionHandler.processBytes(in, 0, in.length, out, 0);
104+
blockEncryptionHandler.doFinal(out, 0);
105+
106+
final byte[] decryptedOut = new byte[outLen];
107+
blockDecryptionHandler_.processBytes(out, 0, outLen, decryptedOut, 0);
108+
blockDecryptionHandler_.processBytes(out, 0, outLen, decryptedOut, 0);
109+
}
110+
}

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

+16-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.util.Collections;
1717
import java.util.Map;
1818

19+
import com.amazonaws.encryptionsdk.model.CiphertextHeaders;
1920
import org.junit.Before;
2021
import org.junit.Test;
2122

@@ -140,4 +141,18 @@ public void invalidOffsetProcessBytes() {
140141
final byte[] out = new byte[1];
141142
decryptionHandler.processBytes(in, -1, in.length, out, 0);
142143
}
143-
}
144+
145+
@Test(expected = BadCiphertextException.class)
146+
public void incompleteCiphertext() {
147+
byte[] ciphertext = getTestHeaders();
148+
149+
CiphertextHeaders h = new CiphertextHeaders();
150+
h.deserialize(ciphertext, 0);
151+
152+
final DecryptionHandler<StaticMasterKey> decryptionHandler = DecryptionHandler.create(masterKeyProvider_);
153+
final byte[] out = new byte[1];
154+
155+
decryptionHandler.processBytes(ciphertext, 0, ciphertext.length - 1, out, 0);
156+
decryptionHandler.doFinal(out, 0);
157+
}
158+
}

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

+26
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,30 @@ public void finalFrameLengthTooLarge() {
8989

9090
frameDecryptionHandler_.processBytes(in, 0, in.length, out, 0);
9191
}
92+
93+
@Test(expected = BadCiphertextException.class)
94+
public void doFinalCalledWhileNotComplete() {
95+
frameDecryptionHandler_.doFinal(new byte[1], 0);
96+
}
97+
98+
@Test(expected = AwsCryptoException.class)
99+
public void processBytesCalledWhileComplete() {
100+
final FrameEncryptionHandler frameEncryptionHandler = new FrameEncryptionHandler(
101+
dataKey_,
102+
nonceLen_,
103+
cryptoAlgorithm_,
104+
messageId_,
105+
frameSize_);
106+
final byte[] in = new byte[0];
107+
final int outLen = frameEncryptionHandler.estimateOutputSize(in.length);
108+
final byte[] out = new byte[outLen];
109+
110+
frameEncryptionHandler.processBytes(in, 0, in.length, out, 0);
111+
frameEncryptionHandler.doFinal(out, 0);
112+
113+
final byte[] decryptedOut = new byte[outLen];
114+
115+
frameDecryptionHandler_.processBytes(out, 0, out.length, decryptedOut, 0);
116+
frameDecryptionHandler_.processBytes(out, 0, out.length, decryptedOut, 0);
117+
}
92118
}

0 commit comments

Comments
 (0)