Skip to content

Commit f71ac4d

Browse files
Merge pull request #11 from patrickfav/feature/10-custom-provider
Allow for providing a JCE provider in JceAesBlockCipher fixes #10
2 parents aa21286 + fb75190 commit f71ac4d

File tree

4 files changed

+88
-23
lines changed

4 files changed

+88
-23
lines changed

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

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
* Copyright (c) 2016 Sebastian Stenzel
44
* This file is licensed under the terms of the MIT license.
55
* See the LICENSE.txt file for more info.
6-
*
6+
*
77
* Contributors:
88
* Sebastian Stenzel - initial API and implementation
99
******************************************************************************/
1010

1111
import java.security.InvalidKeyException;
1212
import java.security.Key;
1313
import java.security.NoSuchAlgorithmException;
14+
import java.security.Provider;
1415

1516
import javax.crypto.Cipher;
1617
import javax.crypto.NoSuchPaddingException;
@@ -24,12 +25,12 @@
2425

2526
/**
2627
* Adapter class between BouncyCastle's {@link BlockCipher} and JCE's {@link Cipher} API.
27-
*
28+
*
2829
* <p>
2930
* As per contract of {@link BlockCipher#processBlock(byte[], int, byte[], int)}, this class is designed to encrypt or decrypt just <b>one single block</b> at a time.
3031
* JCE doesn't allow us to retrieve the plain cipher without a mode, so we explicitly request {@value #SINGLE_BLOCK_PLAIN_AES_JCE_CIPHER_NAME}.
3132
* This is by design, because we want the plain cipher for a single 128 bit block without any mode. We're not actually using ECB mode.
32-
*
33+
*
3334
* <p>
3435
* This is a package-private class only used to encrypt the 128 bit counter during SIV mode.
3536
*/
@@ -43,9 +44,17 @@ final class JceAesBlockCipher implements BlockCipher {
4344
private Key key;
4445
private int opmode;
4546

46-
public JceAesBlockCipher() {
47+
JceAesBlockCipher() {
48+
this(null);
49+
}
50+
51+
JceAesBlockCipher(Provider provider) {
4752
try {
48-
this.cipher = Cipher.getInstance(SINGLE_BLOCK_PLAIN_AES_JCE_CIPHER_NAME); // defaults to SunJCE but allows to configure different providers
53+
if(provider != null) {
54+
this.cipher = Cipher.getInstance(SINGLE_BLOCK_PLAIN_AES_JCE_CIPHER_NAME, provider);
55+
} else {
56+
this.cipher = Cipher.getInstance(SINGLE_BLOCK_PLAIN_AES_JCE_CIPHER_NAME); // defaults to SunJCE
57+
}
4958
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
5059
throw new IllegalStateException("Every implementation of the Java platform is required to support AES/ECB/NoPadding.");
5160
}

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
******************************************************************************/
1010

1111
import java.nio.ByteBuffer;
12+
import java.security.Provider;
1213
import java.util.Arrays;
1314

1415
import javax.crypto.IllegalBlockSizeException;
@@ -40,11 +41,24 @@ public final class SivMode {
4041
* @see #SivMode(BlockCipherFactory)
4142
*/
4243
public SivMode() {
44+
this((Provider) null);
45+
}
46+
47+
/**
48+
* Creates an AES-SIV instance using a custom JCE's security provider<br>
49+
*
50+
* For embedded systems, you might want to consider using {@link #SivMode(BlockCipherFactory)} with {@link AESLightEngine} instead.
51+
*
52+
* @param jceSecurityProvider to use to create the internal {@link javax.crypto.Cipher} instance
53+
*
54+
* @see #SivMode(BlockCipherFactory)
55+
*/
56+
public SivMode(final Provider jceSecurityProvider) {
4357
this(new BlockCipherFactory() {
4458

4559
@Override
4660
public BlockCipher create() {
47-
return new JceAesBlockCipher();
61+
return new JceAesBlockCipher(jceSecurityProvider);
4862
}
4963

5064
});

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

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Copyright (c) 2016 Sebastian Stenzel
44
* This file is licensed under the terms of the MIT license.
55
* See the LICENSE.txt file for more info.
6-
*
6+
*
77
* Contributors:
88
* Sebastian Stenzel - initial API and implementation
99
******************************************************************************/
@@ -16,6 +16,9 @@
1616
import org.junit.Test;
1717
import org.junit.rules.ExpectedException;
1818

19+
import java.security.Provider;
20+
import java.security.Security;
21+
1922
public class JceAesBlockCipherTest {
2023

2124
@Rule
@@ -51,13 +54,31 @@ public void testInitForEncryption() {
5154
cipher.init(true, new KeyParameter(new byte[16]));
5255
}
5356

57+
@Test
58+
public void testInitForEncryptionWithProvider() {
59+
JceAesBlockCipher cipher = new JceAesBlockCipher(getSunJceProvider());
60+
cipher.init(true, new KeyParameter(new byte[16]));
61+
}
62+
5463
@Test
5564
public void testInitForDecryption() {
5665
JceAesBlockCipher cipher = new JceAesBlockCipher();
5766
cipher.init(false, new KeyParameter(new byte[16]));
5867
}
5968

6069
@Test
70+
public void testInitForDecryptionWithProvider() {
71+
JceAesBlockCipher cipher = new JceAesBlockCipher(getSunJceProvider());
72+
cipher.init(false, new KeyParameter(new byte[16]));
73+
}
74+
75+
private Provider getSunJceProvider() {
76+
Provider provider = Security.getProvider("SunJCE");
77+
Assert.assertNotNull(provider);
78+
return provider;
79+
}
80+
81+
@Test
6182
public void testGetAlgorithmName() {
6283
JceAesBlockCipher cipher = new JceAesBlockCipher();
6384
Assert.assertEquals("AES", cipher.getAlgorithmName());
@@ -77,7 +98,7 @@ public void testProcessBlockWithUninitializedCipher() {
7798
}
7899

79100
@Test
80-
public void testProcessBlockWithUnsufficientInput() {
101+
public void testProcessBlockWithInsufficientInput() {
81102
JceAesBlockCipher cipher = new JceAesBlockCipher();
82103
cipher.init(true, new KeyParameter(new byte[16]));
83104
thrown.expect(DataLengthException.class);
@@ -86,7 +107,7 @@ public void testProcessBlockWithUnsufficientInput() {
86107
}
87108

88109
@Test
89-
public void testProcessBlockWithUnsufficientOutput() {
110+
public void testProcessBlockWithInsufficientOutput() {
90111
JceAesBlockCipher cipher = new JceAesBlockCipher();
91112
cipher.init(true, new KeyParameter(new byte[16]));
92113
thrown.expect(DataLengthException.class);
@@ -96,20 +117,28 @@ public void testProcessBlockWithUnsufficientOutput() {
96117

97118
@Test
98119
public void testProcessBlock() {
99-
JceAesBlockCipher cipher = new JceAesBlockCipher();
100-
cipher.init(true, new KeyParameter(new byte[16]));
101-
byte[] ciphertext = new byte[16];
102-
int encrypted = cipher.processBlock(new byte[20], 0, ciphertext, 0);
103-
Assert.assertEquals(16, encrypted);
104-
105-
cipher.init(false, new KeyParameter(new byte[16]));
106-
byte[] cleartext = new byte[16];
107-
int decrypted = cipher.processBlock(ciphertext, 0, cleartext, 0);
108-
Assert.assertEquals(16, decrypted);
109-
Assert.assertArrayEquals(new byte[16], cleartext);
110-
}
111-
112-
@Test
120+
testProcessBlock(new JceAesBlockCipher());
121+
}
122+
123+
@Test
124+
public void testProcessBlockWithProvider() {
125+
testProcessBlock(new JceAesBlockCipher(getSunJceProvider()));
126+
}
127+
128+
private void testProcessBlock(JceAesBlockCipher cipher) {
129+
cipher.init(true, new KeyParameter(new byte[16]));
130+
byte[] ciphertext = new byte[16];
131+
int encrypted = cipher.processBlock(new byte[20], 0, ciphertext, 0);
132+
Assert.assertEquals(16, encrypted);
133+
134+
cipher.init(false, new KeyParameter(new byte[16]));
135+
byte[] cleartext = new byte[16];
136+
int decrypted = cipher.processBlock(ciphertext, 0, cleartext, 0);
137+
Assert.assertEquals(16, decrypted);
138+
Assert.assertArrayEquals(new byte[16], cleartext);
139+
}
140+
141+
@Test
113142
public void testResetBeforeInitDoesNotThrowExceptions() {
114143
JceAesBlockCipher cipher = new JceAesBlockCipher();
115144
cipher.reset();

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
import java.io.IOException;
1212
import java.security.InvalidKeyException;
13+
import java.security.Provider;
14+
import java.security.Security;
1315

1416
import javax.crypto.IllegalBlockSizeException;
1517
import javax.crypto.SecretKey;
@@ -141,6 +143,9 @@ public void testGenerateKeyStream1() {
141143

142144
final byte[] result = new SivMode().generateKeyStream(ctrKey, ctr, 1);
143145
Assert.assertArrayEquals(expected, result);
146+
147+
final byte[] resultProvider = new SivMode(getSunJceProvider()).generateKeyStream(ctrKey, ctr, 1);
148+
Assert.assertArrayEquals(expected, resultProvider);
144149
}
145150

146151
// CTR-AES https://tools.ietf.org/html/rfc5297#appendix-A.2
@@ -199,6 +204,9 @@ public void testS2v() {
199204

200205
final byte[] result = new SivMode().s2v(macKey, plaintext, ad);
201206
Assert.assertArrayEquals(expected, result);
207+
208+
final byte[] resultProvider = new SivMode(getSunJceProvider()).s2v(macKey, plaintext, ad);
209+
Assert.assertArrayEquals(expected, resultProvider);
202210
}
203211

204212
@Test
@@ -584,4 +592,9 @@ public void testGeneratedTestCases() throws IOException, UnauthenticCiphertextEx
584592
}
585593
}
586594

595+
private Provider getSunJceProvider() {
596+
Provider provider = Security.getProvider("SunJCE");
597+
Assert.assertNotNull(provider);
598+
return provider;
599+
}
587600
}

0 commit comments

Comments
 (0)