Skip to content

Commit f046e5c

Browse files
deduplicated code
1 parent 2e723af commit f046e5c

File tree

2 files changed

+82
-43
lines changed

2 files changed

+82
-43
lines changed

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

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -114,33 +114,16 @@ public byte[] encrypt(SecretKey ctrKey, SecretKey macKey, byte[] plaintext, byte
114114
* @throws IllegalArgumentException if the either of the two keys is of invalid length for the used {@link BlockCipher}.
115115
*/
116116
public byte[] encrypt(byte[] ctrKey, byte[] macKey, byte[] plaintext, byte[]... associatedData) {
117-
final byte[] iv = s2v(macKey, plaintext, associatedData);
118-
119117
// Check if plaintext length will cause overflows
120118
if (plaintext.length > (Integer.MAX_VALUE - 16)) {
121119
throw new IllegalArgumentException("Plaintext is too long");
122120
}
123121

122+
assert plaintext.length + 15 < Integer.MAX_VALUE;
124123
final int numBlocks = (plaintext.length + 15) / 16;
125-
126-
// clear out the 31st and 63rd (rightmost) bit:
127-
final byte[] ctr = Arrays.copyOf(iv, 16);
128-
ctr[8] = (byte) (ctr[8] & 0x7F);
129-
ctr[12] = (byte) (ctr[12] & 0x7F);
130-
final ByteBuffer ctrBuf = ByteBuffer.wrap(ctr);
131-
final long initialCtrVal = ctrBuf.getLong(8);
132-
133-
final byte[] x = new byte[numBlocks * 16];
134-
final BlockCipher cipher = threadLocalCipher.get();
135-
cipher.init(true, new KeyParameter(ctrKey));
136-
for (int i = 0; i < numBlocks; i++) {
137-
final long ctrVal = initialCtrVal + i;
138-
ctrBuf.putLong(8, ctrVal);
139-
cipher.processBlock(ctrBuf.array(), 0, x, i * 16);
140-
cipher.reset();
141-
}
142-
143-
final byte[] ciphertext = xor(plaintext, x);
124+
final byte[] iv = s2v(macKey, plaintext, associatedData);
125+
final byte[] keystream = generateKeyStream(ctrKey, iv, numBlocks);
126+
final byte[] ciphertext = xor(plaintext, keystream);
144127

145128
// concat IV + ciphertext:
146129
final byte[] result = new byte[iv.length + ciphertext.length];
@@ -195,28 +178,11 @@ public byte[] decrypt(byte[] ctrKey, byte[] macKey, byte[] ciphertext, byte[]...
195178
final byte[] iv = Arrays.copyOf(ciphertext, 16);
196179
final byte[] actualCiphertext = Arrays.copyOfRange(ciphertext, 16, ciphertext.length);
197180

198-
// will not overflow because actualCiphertext.length == (ciphertext.length - 16)
181+
assert actualCiphertext.length == ciphertext.length - 16;
182+
assert actualCiphertext.length + 15 < Integer.MAX_VALUE;
199183
final int numBlocks = (actualCiphertext.length + 15) / 16;
200-
201-
// clear out the 31st and 63rd (rightmost) bit:
202-
final byte[] ctr = Arrays.copyOf(iv, 16);
203-
ctr[8] = (byte) (ctr[8] & 0x7F);
204-
ctr[12] = (byte) (ctr[12] & 0x7F);
205-
final ByteBuffer ctrBuf = ByteBuffer.wrap(ctr);
206-
final long initialCtrVal = ctrBuf.getLong(8);
207-
208-
final byte[] x = new byte[numBlocks * 16];
209-
final BlockCipher cipher = threadLocalCipher.get();
210-
cipher.init(true, new KeyParameter(ctrKey));
211-
for (int i = 0; i < numBlocks; i++) {
212-
final long ctrVal = initialCtrVal + i;
213-
ctrBuf.putLong(8, ctrVal);
214-
cipher.processBlock(ctrBuf.array(), 0, x, i * 16);
215-
cipher.reset();
216-
}
217-
218-
final byte[] plaintext = xor(actualCiphertext, x);
219-
184+
final byte[] keystream = generateKeyStream(ctrKey, iv, numBlocks);
185+
final byte[] plaintext = xor(actualCiphertext, keystream);
220186
final byte[] control = s2v(macKey, plaintext, associatedData);
221187

222188
// time-constant comparison (taken from MessageDigest.isEqual in JDK8)
@@ -233,6 +199,27 @@ public byte[] decrypt(byte[] ctrKey, byte[] macKey, byte[] ciphertext, byte[]...
233199
}
234200
}
235201

202+
byte[] generateKeyStream(byte[] ctrKey, byte[] iv, int numBlocks) {
203+
final byte[] keystream = new byte[numBlocks * 16];
204+
205+
// clear out the 31st and 63rd (rightmost) bit:
206+
final byte[] ctr = Arrays.copyOf(iv, 16);
207+
ctr[8] = (byte) (ctr[8] & 0x7F);
208+
ctr[12] = (byte) (ctr[12] & 0x7F);
209+
final ByteBuffer ctrBuf = ByteBuffer.wrap(ctr);
210+
final long initialCtrVal = ctrBuf.getLong(8);
211+
212+
final BlockCipher cipher = threadLocalCipher.get();
213+
cipher.init(true, new KeyParameter(ctrKey));
214+
for (int i = 0; i < numBlocks; i++) {
215+
ctrBuf.putLong(8, initialCtrVal + i);
216+
cipher.processBlock(ctr, 0, keystream, i * 16);
217+
cipher.reset();
218+
}
219+
220+
return keystream;
221+
}
222+
236223
// Visible for testing, throws IllegalArgumentException if key is not accepted by CMac#init(CipherParameters)
237224
byte[] s2v(byte[] macKey, byte[] plaintext, byte[]... associatedData) {
238225
// Maximum permitted AD length is the block size in bits - 2

src/test/java/org/cryptomator/siv/SivModeTest.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import org.mockito.Mockito;
2424

2525
/**
26-
* Official RFC 5297 test vector taken from https://tools.ietf.org/html/rfc5297#appendix-A.1
26+
* Official RFC 5297 test vector taken from https://tools.ietf.org/html/rfc5297#appendix-A.1 and https://tools.ietf.org/html/rfc5297#appendix-A.2
2727
*/
2828
public class SivModeTest {
2929

@@ -121,6 +121,58 @@ public void testDecryptAssociatedDataLimit() throws UnauthenticCiphertextExcepti
121121
new SivMode().decrypt(ctrKey, macKey, plaintext, new byte[127][0]);
122122
}
123123

124+
// CTR-AES https://tools.ietf.org/html/rfc5297#appendix-A.1
125+
@Test
126+
public void testGenerateKeyStream1() {
127+
final byte[] ctrKey = {(byte) 0xf0, (byte) 0xf1, (byte) 0xf2, (byte) 0xf3, //
128+
(byte) 0xf4, (byte) 0xf5, (byte) 0xf6, (byte) 0xf7, //
129+
(byte) 0xf8, (byte) 0xf9, (byte) 0xfa, (byte) 0xfb, //
130+
(byte) 0xfc, (byte) 0xfd, (byte) 0xfe, (byte) 0xff};
131+
132+
final byte[] ctr = {(byte) 0x85, (byte) 0x63, (byte) 0x2d, (byte) 0x07, //
133+
(byte) 0xc6, (byte) 0xe8, (byte) 0xf3, (byte) 0x7f, //
134+
(byte) 0x15, (byte) 0x0a, (byte) 0xcd, (byte) 0x32, //
135+
(byte) 0x0a, (byte) 0x2e, (byte) 0xcc, (byte) 0x93};
136+
137+
final byte[] expected = {(byte) 0x51, (byte) 0xe2, (byte) 0x18, (byte) 0xd2, //
138+
(byte) 0xc5, (byte) 0xa2, (byte) 0xab, (byte) 0x8c, //
139+
(byte) 0x43, (byte) 0x45, (byte) 0xc4, (byte) 0xa6, //
140+
(byte) 0x23, (byte) 0xb2, (byte) 0xf0, (byte) 0x8f};
141+
142+
final byte[] result = new SivMode().generateKeyStream(ctrKey, ctr, 1);
143+
Assert.assertArrayEquals(expected, result);
144+
}
145+
146+
// CTR-AES https://tools.ietf.org/html/rfc5297#appendix-A.2
147+
@Test
148+
public void testGenerateKeyStream2() {
149+
final byte[] ctrKey = {(byte) 0x40, (byte) 0x41, (byte) 0x42, (byte) 0x43, //
150+
(byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47, //
151+
(byte) 0x48, (byte) 0x49, (byte) 0x4a, (byte) 0x4b, //
152+
(byte) 0x4c, (byte) 0x4d, (byte) 0x4e, (byte) 0x4f};
153+
154+
final byte[] ctr = {(byte) 0x7b, (byte) 0xdb, (byte) 0x6e, (byte) 0x3b, //
155+
(byte) 0x43, (byte) 0x26, (byte) 0x67, (byte) 0xeb, //
156+
(byte) 06, (byte) 0xf4, (byte) 0xd1, (byte) 0x4b, //
157+
(byte) 0x7f, (byte) 0x2f, (byte) 0xbd, (byte) 0x0f};
158+
159+
final byte[] expected = {(byte) 0xbf, (byte) 0xf8, (byte) 0x66, (byte) 0x5c, //
160+
(byte) 0xfd, (byte) 0xd7, (byte) 0x33, (byte) 0x63, //
161+
(byte) 0x55, (byte) 0x0f, (byte) 0x74, (byte) 0x00, //
162+
(byte) 0xe8, (byte) 0xf9, (byte) 0xd3, (byte) 0x76, //
163+
(byte) 0xb2, (byte) 0xc9, (byte) 0x08, (byte) 0x8e, //
164+
(byte) 0x71, (byte) 0x3b, (byte) 0x86, (byte) 0x17, //
165+
(byte) 0xd8, (byte) 0x83, (byte) 0x92, (byte) 0x26, //
166+
(byte) 0xd9, (byte) 0xf8, (byte) 0x81, (byte) 0x59, //
167+
(byte) 0x9e, (byte) 0x44, (byte) 0xd8, (byte) 0x27, //
168+
(byte) 0x23, (byte) 0x49, (byte) 0x49, (byte) 0xbc, //
169+
(byte) 0x1b, (byte) 0x12, (byte) 0x34, (byte) 0x8e, //
170+
(byte) 0xbc, (byte) 0x19, (byte) 0x5e, (byte) 0xc7};
171+
172+
final byte[] result = new SivMode().generateKeyStream(ctrKey, ctr, 3);
173+
Assert.assertArrayEquals(expected, result);
174+
}
175+
124176
@Test
125177
public void testS2v() {
126178
final byte[] macKey = {(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc, //

0 commit comments

Comments
 (0)