Skip to content

Commit 4da3f20

Browse files
deprecate 1.x SivMode API
1 parent 6edd8c5 commit 4da3f20

File tree

3 files changed

+216
-0
lines changed

3 files changed

+216
-0
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package org.cryptomator.siv;
2+
3+
import javax.crypto.AEADBadTagException;
4+
import javax.crypto.IllegalBlockSizeException;
5+
import javax.crypto.SecretKey;
6+
import java.util.Arrays;
7+
8+
/**
9+
* Implements the RFC 5297 SIV mode.
10+
*/
11+
@Deprecated
12+
public final class SivMode {
13+
14+
/**
15+
* Creates an AES-SIV instance using JCE's cipher implementation, which should normally be the best choice.<br>
16+
*/
17+
public SivMode() {
18+
}
19+
20+
/**
21+
* Convenience method using a single 256, 384, or 512 bits key. This is just a wrapper for {@link #encrypt(byte[], byte[], byte[], byte[]...)}.
22+
* @param key Combined key, which is split in half.
23+
* @param plaintext Your plaintext, which shall be encrypted.
24+
* @param associatedData Optional associated data, which gets authenticated but not encrypted.
25+
* @return IV + Ciphertext as a concatenated byte array.
26+
*/
27+
public byte[] encrypt(SecretKey key, byte[] plaintext, byte[]... associatedData) {
28+
final byte[] keyBytes = key.getEncoded();
29+
if (keyBytes == null) {
30+
throw new IllegalArgumentException("Can't get bytes of given key.");
31+
}
32+
try {
33+
return new SivEngine(keyBytes).encrypt(plaintext, associatedData);
34+
} finally {
35+
Arrays.fill(keyBytes, (byte) 0);
36+
}
37+
}
38+
39+
/**
40+
* Convenience method, if you are using the javax.crypto API. This is just a wrapper for {@link #encrypt(byte[], byte[], byte[], byte[]...)}.
41+
*
42+
* @param ctrKey SIV mode requires two separate keys. You can use one long key, which is split in half. See <a href="https://tools.ietf.org/html/rfc5297#section-2.2">RFC 5297 Section 2.2</a>
43+
* @param macKey SIV mode requires two separate keys. You can use one long key, which is split in half. See <a href="https://tools.ietf.org/html/rfc5297#section-2.2">RFC 5297 Section 2.2</a>
44+
* @param plaintext Your plaintext, which shall be encrypted.
45+
* @param associatedData Optional associated data, which gets authenticated but not encrypted.
46+
* @return IV + Ciphertext as a concatenated byte array.
47+
* @throws IllegalArgumentException if keys are invalid or {@link SecretKey#getEncoded()} is not supported.
48+
*/
49+
public byte[] encrypt(SecretKey ctrKey, SecretKey macKey, byte[] plaintext, byte[]... associatedData) {
50+
final byte[] ctrKeyBytes = ctrKey.getEncoded();
51+
final byte[] macKeyBytes = macKey.getEncoded();
52+
if (ctrKeyBytes == null || macKeyBytes == null) {
53+
throw new IllegalArgumentException("Can't get bytes of given key.");
54+
}
55+
try {
56+
return encrypt(ctrKeyBytes, macKeyBytes, plaintext, associatedData);
57+
} finally {
58+
Arrays.fill(ctrKeyBytes, (byte) 0);
59+
Arrays.fill(macKeyBytes, (byte) 0);
60+
}
61+
}
62+
63+
/**
64+
* Encrypts plaintext using SIV mode. A block cipher defined by the constructor is being used.<br>
65+
*
66+
* @param ctrKey SIV mode requires two separate keys. You can use one long key, which is split in half. See <a href="https://tools.ietf.org/html/rfc5297#section-2.2">RFC 5297 Section 2.2</a>
67+
* @param macKey SIV mode requires two separate keys. You can use one long key, which is split in half. See <a href="https://tools.ietf.org/html/rfc5297#section-2.2">RFC 5297 Section 2.2</a>
68+
* @param plaintext Your plaintext, which shall be encrypted.
69+
* @param associatedData Optional associated data, which gets authenticated but not encrypted.
70+
* @return IV + Ciphertext as a concatenated byte array.
71+
* @throws IllegalArgumentException if the either of the two keys is of invalid length for the used {@link BlockCipher}.
72+
*/
73+
public byte[] encrypt(byte[] ctrKey, byte[] macKey, byte[] plaintext, byte[]... associatedData) {
74+
byte[] combinedKey = new byte[ctrKey.length + macKey.length];
75+
try {
76+
System.arraycopy(macKey, 0, combinedKey, 0, macKey.length);
77+
System.arraycopy(ctrKey, 0, combinedKey, macKey.length, ctrKey.length);
78+
return new SivEngine(combinedKey).encrypt(plaintext, associatedData);
79+
} finally {
80+
Arrays.fill(combinedKey, (byte) 0);
81+
}
82+
}
83+
84+
/**
85+
* Convenience method using a single 256, 384, or 512 bits key. This is just a wrapper for {@link #decrypt(byte[], byte[], byte[], byte[]...)}.
86+
* @param key Combined key, which is split in half.
87+
* @param ciphertext Your cipehrtext, which shall be decrypted.
88+
* @param associatedData Optional associated data, which gets authenticated but not encrypted.
89+
* @return Plaintext byte array.
90+
* @throws IllegalArgumentException If keys are invalid.
91+
* @throws UnauthenticCiphertextException If the authentication failed, e.g. because ciphertext and/or associatedData are corrupted.
92+
* @throws IllegalBlockSizeException If the provided ciphertext is of invalid length.
93+
*/
94+
public byte[] decrypt(SecretKey key, byte[] ciphertext, byte[]... associatedData) throws UnauthenticCiphertextException, IllegalBlockSizeException {
95+
final byte[] keyBytes = key.getEncoded();
96+
if (keyBytes == null) {
97+
throw new IllegalArgumentException("Can't get bytes of given key.");
98+
}
99+
try {
100+
return new SivEngine(keyBytes).decrypt(ciphertext, associatedData);
101+
} catch (AEADBadTagException e) {
102+
throw new UnauthenticCiphertextException("authentication in SIV decryption failed");
103+
} finally {
104+
Arrays.fill(keyBytes, (byte) 0);
105+
}
106+
}
107+
108+
/**
109+
* Convenience method, if you are using the javax.crypto API. This is just a wrapper for {@link #decrypt(byte[], byte[], byte[], byte[]...)}.
110+
*
111+
* @param ctrKey SIV mode requires two separate keys. You can use one long key, which is split in half. See <a href="https://tools.ietf.org/html/rfc5297#section-2.2">RFC 5297 Section 2.2</a>
112+
* @param macKey SIV mode requires two separate keys. You can use one long key, which is split in half. See <a href="https://tools.ietf.org/html/rfc5297#section-2.2">RFC 5297 Section 2.2</a>
113+
* @param ciphertext Your cipehrtext, which shall be decrypted.
114+
* @param associatedData Optional associated data, which needs to be authenticated during decryption.
115+
* @return Plaintext byte array.
116+
* @throws IllegalArgumentException If keys are invalid or {@link SecretKey#getEncoded()} is not supported.
117+
* @throws UnauthenticCiphertextException If the authentication failed, e.g. because ciphertext and/or associatedData are corrupted.
118+
* @throws IllegalBlockSizeException If the provided ciphertext is of invalid length.
119+
*/
120+
public byte[] decrypt(SecretKey ctrKey, SecretKey macKey, byte[] ciphertext, byte[]... associatedData) throws UnauthenticCiphertextException, IllegalBlockSizeException {
121+
final byte[] ctrKeyBytes = ctrKey.getEncoded();
122+
final byte[] macKeyBytes = macKey.getEncoded();
123+
if (ctrKeyBytes == null || macKeyBytes == null) {
124+
throw new IllegalArgumentException("Can't get bytes of given key.");
125+
}
126+
try {
127+
return decrypt(ctrKeyBytes, macKeyBytes, ciphertext, associatedData);
128+
} finally {
129+
Arrays.fill(ctrKeyBytes, (byte) 0);
130+
Arrays.fill(macKeyBytes, (byte) 0);
131+
}
132+
}
133+
134+
/**
135+
* Decrypts ciphertext using SIV mode. A block cipher defined by the constructor is being used.<br>
136+
*
137+
* @param ctrKey SIV mode requires two separate keys. You can use one long key, which is split in half. See <a href="https://tools.ietf.org/html/rfc5297#section-2.2">RFC 5297 Section 2.2</a>
138+
* @param macKey SIV mode requires two separate keys. You can use one long key, which is split in half. See <a href="https://tools.ietf.org/html/rfc5297#section-2.2">RFC 5297 Section 2.2</a>
139+
* @param ciphertext Your ciphertext, which shall be encrypted.
140+
* @param associatedData Optional associated data, which needs to be authenticated during decryption.
141+
* @return Plaintext byte array.
142+
* @throws IllegalArgumentException If the either of the two keys is of invalid length.
143+
* @throws UnauthenticCiphertextException If the authentication failed, e.g. because ciphertext and/or associatedData are corrupted.
144+
* @throws IllegalBlockSizeException If the provided ciphertext is of invalid length.
145+
*/
146+
public byte[] decrypt(byte[] ctrKey, byte[] macKey, byte[] ciphertext, byte[]... associatedData) throws UnauthenticCiphertextException, IllegalBlockSizeException {
147+
byte[] combinedKey = new byte[ctrKey.length + macKey.length];
148+
try {
149+
System.arraycopy(macKey, 0, combinedKey, 0, macKey.length);
150+
System.arraycopy(ctrKey, 0, combinedKey, macKey.length, ctrKey.length);
151+
return new SivEngine(combinedKey).decrypt(ciphertext, associatedData);
152+
} catch (AEADBadTagException e) {
153+
throw new UnauthenticCiphertextException("authentication in SIV decryption failed");
154+
} finally {
155+
Arrays.fill(combinedKey, (byte) 0);
156+
}
157+
}
158+
159+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.cryptomator.siv;
2+
3+
import javax.crypto.BadPaddingException;
4+
5+
/**
6+
* Drop-in replacement for {@link javax.crypto.AEADBadTagException}, which is not available on some older Android systems.
7+
*/
8+
@Deprecated
9+
public class UnauthenticCiphertextException extends BadPaddingException {
10+
11+
/**
12+
* Constructs a UnauthenticCiphertextException with the specified
13+
* detail message.
14+
*
15+
* @param message the detail message.
16+
*/
17+
public UnauthenticCiphertextException(String message) {
18+
super(message);
19+
}
20+
21+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.cryptomator.siv;
2+
3+
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.DisplayName;
5+
import org.junit.jupiter.api.Test;
6+
7+
import javax.crypto.IllegalBlockSizeException;
8+
import javax.crypto.spec.SecretKeySpec;
9+
10+
@Deprecated
11+
@DisplayName("test deprecated SivMode API")
12+
class SivModeTest {
13+
14+
@Test
15+
@DisplayName("encrypt/decrypt with 512 bit key")
16+
public void testEncryptAndDecryptWithSingleKey() throws UnauthenticCiphertextException, IllegalBlockSizeException {
17+
byte[] key = new byte[64];
18+
byte[] plaintext = "Hello, World!".getBytes();
19+
byte[] aad = "AdditionalData".getBytes();
20+
byte[] encrypted = new SivMode().encrypt(new SecretKeySpec(key, "AES"), plaintext, aad);
21+
byte[] decrypted = new SivMode().decrypt(new SecretKeySpec(key, "AES"), encrypted, aad);
22+
Assertions.assertArrayEquals(plaintext, decrypted);
23+
}
24+
25+
@Test
26+
@DisplayName("encrypt/decrypt with two 256 bit keys")
27+
public void testEncryptAndDecryptWithSeparateKeys() throws UnauthenticCiphertextException, IllegalBlockSizeException {
28+
byte[] key = new byte[32];
29+
byte[] plaintext = "Hello, World!".getBytes();
30+
byte[] aad = "AdditionalData".getBytes();
31+
byte[] encrypted = new SivMode().encrypt(new SecretKeySpec(key, "AES"), new SecretKeySpec(key, "AES"), plaintext, aad);
32+
byte[] decrypted = new SivMode().decrypt(new SecretKeySpec(key, "AES"), new SecretKeySpec(key, "AES"), encrypted, aad);
33+
Assertions.assertArrayEquals(plaintext, decrypted);
34+
}
35+
36+
}

0 commit comments

Comments
 (0)