Skip to content

Commit 36bdb8d

Browse files
avoid copying data from input
1 parent b619e23 commit 36bdb8d

File tree

2 files changed

+22
-17
lines changed

2 files changed

+22
-17
lines changed

src/main/java/org/cryptomator/siv/SivCipher.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[]
140140
if (this.opmode == Cipher.ENCRYPT_MODE || this.opmode == Cipher.WRAP_MODE) {
141141
return siv.encrypt(inputBuffer, output, outputOffset, aad);
142142
} else if (this.opmode == Cipher.DECRYPT_MODE || this.opmode == Cipher.UNWRAP_MODE) {
143+
// for security reasons we can't write into output directly before checking integrity:
143144
byte[] plaintext = siv.decrypt(inputBuffer, aad);
144145
System.arraycopy(plaintext, 0, output, outputOffset, plaintext.length);
145146
return plaintext.length;

src/main/java/org/cryptomator/siv/SivEngine.java

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public SivEngine(byte[] key) {
7676
* @throws IllegalArgumentException if either param exceeds the limits for safe use.
7777
*/
7878
public byte[] encrypt(byte[] plaintext, byte[]... associatedData) {
79-
final byte[] ciphertext = new byte[16 + plaintext.length];
79+
final byte[] ciphertext = new byte[IV_LENGTH + plaintext.length];
8080
try {
8181
int encrypted = encrypt(plaintext, ciphertext, 0, associatedData);
8282
assert encrypted == ciphertext.length;
@@ -86,19 +86,19 @@ public byte[] encrypt(byte[] plaintext, byte[]... associatedData) {
8686
return ciphertext;
8787
}
8888

89-
public int encrypt(byte[] input, byte[] output, int outputOffset, byte[]... associatedData) throws ShortBufferException {
89+
public int encrypt(byte[] plaintext, byte[] output, int outputOffset, byte[]... associatedData) throws ShortBufferException {
9090
// Check if plaintext length will cause overflows
91-
if (input.length > (Integer.MAX_VALUE - IV_LENGTH)) {
91+
if (plaintext.length > (Integer.MAX_VALUE - IV_LENGTH)) {
9292
throw new IllegalArgumentException("Plaintext is too long");
9393
}
9494

95-
if (output.length - outputOffset < IV_LENGTH + input.length) {
95+
if (output.length - outputOffset < IV_LENGTH + plaintext.length) {
9696
throw new ShortBufferException();
9797
}
98-
byte[] iv = s2v(input, associatedData);
98+
byte[] iv = s2v(plaintext, associatedData);
9999
assert iv.length == IV_LENGTH;
100100
System.arraycopy(iv, 0, output, 0, IV_LENGTH);
101-
return IV_LENGTH + computeCtr(input, iv, output, IV_LENGTH);
101+
return IV_LENGTH + computeCtr(plaintext, 0, plaintext.length, iv, 0, IV_LENGTH, output, IV_LENGTH);
102102
}
103103

104104
/**
@@ -115,16 +115,20 @@ public byte[] decrypt(byte[] ciphertext, byte[]... associatedData) throws AEADBa
115115
throw new IllegalBlockSizeException("Input length must be greater than or equal 16.");
116116
}
117117

118-
final byte[] iv = Arrays.copyOf(ciphertext, IV_LENGTH);
119-
final byte[] actualCiphertext = Arrays.copyOfRange(ciphertext, IV_LENGTH, ciphertext.length);
120-
final byte[] plaintext = computeCtr(actualCiphertext, iv);
118+
final byte[] plaintext = new byte[ciphertext.length - IV_LENGTH];
119+
try {
120+
int decrypted = computeCtr(ciphertext, IV_LENGTH, ciphertext.length - IV_LENGTH, ciphertext, 0, IV_LENGTH, plaintext, 0);
121+
assert decrypted == plaintext.length;
122+
} catch (ShortBufferException e) {
123+
throw new IllegalStateException(e);
124+
}
121125
final byte[] control = s2v(plaintext, associatedData);
122126

123127
// time-constant comparison (taken from MessageDigest.isEqual in JDK8)
124-
assert iv.length == control.length;
128+
assert control.length == IV_LENGTH;
125129
int diff = 0;
126-
for (int i = 0; i < iv.length; i++) {
127-
diff |= iv[i] ^ control[i];
130+
for (int i = 0; i < IV_LENGTH; i++) {
131+
diff |= ciphertext[i] ^ control[i];
128132
}
129133

130134
if (diff == 0) {
@@ -139,24 +143,24 @@ public byte[] decrypt(byte[] ciphertext, byte[]... associatedData) throws AEADBa
139143
byte[] computeCtr(byte[] input, final byte[] iv) {
140144
byte[] output = new byte[input.length];
141145
try {
142-
int processed = computeCtr(input, iv, output, 0);
146+
int processed = computeCtr(input, 0, input.length, iv, 0, iv.length, output, 0);
143147
assert processed == output.length;
144148
} catch (ShortBufferException e) {
145149
throw new IllegalStateException(e);
146150
}
147151
return output;
148152
}
149153

150-
// visible for testing
151-
int computeCtr(byte[] input, final byte[] iv, byte[] output, int outputOffset) throws ShortBufferException {
154+
private int computeCtr(byte[] input, int inOff, int inLen, final byte[] iv, int ivOff, int ivLen, byte[] output, int outputOffset) throws ShortBufferException {
152155
// clear out the 31st and 63rd (rightmost) bit:
153-
final byte[] adjustedIv = Arrays.copyOf(iv, 16);
156+
assert ivLen == IV_LENGTH;
157+
final byte[] adjustedIv = Arrays.copyOfRange(iv, ivOff, ivOff + ivLen);
154158
adjustedIv[8] = (byte) (adjustedIv[8] & 0x7F);
155159
adjustedIv[12] = (byte) (adjustedIv[12] & 0x7F);
156160

157161
try {
158162
ctrCipher.init(Cipher.ENCRYPT_MODE, ctrKey, new IvParameterSpec(adjustedIv));
159-
return ctrCipher.doFinal(input, 0, input.length, output, outputOffset);
163+
return ctrCipher.doFinal(input, inOff, inLen, output, outputOffset);
160164
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
161165
throw new IllegalArgumentException("Key or IV invalid.");
162166
} catch (BadPaddingException e) {

0 commit comments

Comments
 (0)