Skip to content
Snippets Groups Projects
Commit fc897bd1 authored by akwizgran's avatar akwizgran
Browse files

Use XSalsa20-Poly1305 instead of AES-GCM. #111

parent 6fab0e87
No related branches found
No related tags found
No related merge requests found
...@@ -19,7 +19,7 @@ public interface TransportConstants { ...@@ -19,7 +19,7 @@ public interface TransportConstants {
+ MAC_LENGTH; + MAC_LENGTH;
/** The length of the frame initalisation vector (IV) in bytes. */ /** The length of the frame initalisation vector (IV) in bytes. */
int FRAME_IV_LENGTH = 12; int FRAME_IV_LENGTH = 24;
/** The length of the frame header in bytes. */ /** The length of the frame header in bytes. */
int FRAME_HEADER_LENGTH = 4 + MAC_LENGTH; int FRAME_HEADER_LENGTH = 4 + MAC_LENGTH;
......
package org.briarproject.crypto;
import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH;
import java.security.GeneralSecurityException;
import org.briarproject.api.crypto.SecretKey;
import org.spongycastle.crypto.DataLengthException;
import org.spongycastle.crypto.InvalidCipherTextException;
import org.spongycastle.crypto.engines.AESLightEngine;
import org.spongycastle.crypto.modes.AEADBlockCipher;
import org.spongycastle.crypto.modes.GCMBlockCipher;
import org.spongycastle.crypto.modes.gcm.BasicGCMMultiplier;
import org.spongycastle.crypto.params.AEADParameters;
import org.spongycastle.crypto.params.KeyParameter;
class AuthenticatedCipherImpl implements AuthenticatedCipher {
private final AEADBlockCipher cipher;
AuthenticatedCipherImpl() {
cipher = new GCMBlockCipher(new AESLightEngine(),
new BasicGCMMultiplier());
}
public int process(byte[] input, int inputOff, int len, byte[] output,
int outputOff) throws GeneralSecurityException {
int processed = 0;
if (len != 0) {
processed = cipher.processBytes(input, inputOff, len, output,
outputOff);
}
try {
return processed + cipher.doFinal(output, outputOff + processed);
} catch (DataLengthException e) {
throw new GeneralSecurityException(e.getMessage());
} catch (InvalidCipherTextException e) {
throw new GeneralSecurityException(e.getMessage());
}
}
public void init(boolean encrypt, SecretKey key, byte[] iv)
throws GeneralSecurityException {
KeyParameter k = new KeyParameter(key.getBytes());
// Authenticate the IV by passing it as additional authenticated data
AEADParameters params = new AEADParameters(k, MAC_LENGTH * 8, iv, iv);
try {
cipher.init(encrypt, params);
} catch (IllegalArgumentException e) {
throw new GeneralSecurityException(e.getMessage());
}
}
public int getMacBytes() {
return MAC_LENGTH;
}
}
...@@ -55,8 +55,8 @@ class CryptoComponentImpl implements CryptoComponent { ...@@ -55,8 +55,8 @@ class CryptoComponentImpl implements CryptoComponent {
private static final int AGREEMENT_KEY_PAIR_BITS = 256; private static final int AGREEMENT_KEY_PAIR_BITS = 256;
private static final int SIGNATURE_KEY_PAIR_BITS = 256; private static final int SIGNATURE_KEY_PAIR_BITS = 256;
private static final int STORAGE_IV_BYTES = 16; // 128 bits private static final int STORAGE_IV_BYTES = 24; // 196 bits
private static final int PBKDF_SALT_BYTES = 16; // 128 bits private static final int PBKDF_SALT_BYTES = 32; // 256 bits
private static final int PBKDF_TARGET_MILLIS = 500; private static final int PBKDF_TARGET_MILLIS = 500;
private static final int PBKDF_SAMPLES = 30; private static final int PBKDF_SAMPLES = 30;
...@@ -325,7 +325,7 @@ class CryptoComponentImpl implements CryptoComponent { ...@@ -325,7 +325,7 @@ class CryptoComponentImpl implements CryptoComponent {
} }
public byte[] encryptWithPassword(byte[] input, String password) { public byte[] encryptWithPassword(byte[] input, String password) {
AuthenticatedCipher cipher = new AuthenticatedCipherImpl(); AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
int macBytes = cipher.getMacBytes(); int macBytes = cipher.getMacBytes();
// Generate a random salt // Generate a random salt
byte[] salt = new byte[PBKDF_SALT_BYTES]; byte[] salt = new byte[PBKDF_SALT_BYTES];
...@@ -355,7 +355,7 @@ class CryptoComponentImpl implements CryptoComponent { ...@@ -355,7 +355,7 @@ class CryptoComponentImpl implements CryptoComponent {
} }
public byte[] decryptWithPassword(byte[] input, String password) { public byte[] decryptWithPassword(byte[] input, String password) {
AuthenticatedCipher cipher = new AuthenticatedCipherImpl(); AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
int macBytes = cipher.getMacBytes(); int macBytes = cipher.getMacBytes();
// The input contains the salt, iterations, IV, ciphertext and MAC // The input contains the salt, iterations, IV, ciphertext and MAC
if (input.length < PBKDF_SALT_BYTES + 4 + STORAGE_IV_BYTES + macBytes) if (input.length < PBKDF_SALT_BYTES + 4 + STORAGE_IV_BYTES + macBytes)
......
package org.briarproject.crypto; package org.briarproject.crypto;
import static java.util.concurrent.TimeUnit.SECONDS; import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.CryptoExecutor;
import org.briarproject.api.crypto.PasswordStrengthEstimator;
import org.briarproject.api.crypto.StreamDecrypterFactory;
import org.briarproject.api.crypto.StreamEncrypterFactory;
import org.briarproject.api.lifecycle.LifecycleManager;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
...@@ -11,15 +19,7 @@ import java.util.concurrent.ThreadPoolExecutor; ...@@ -11,15 +19,7 @@ import java.util.concurrent.ThreadPoolExecutor;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.briarproject.api.crypto.CryptoComponent; import static java.util.concurrent.TimeUnit.SECONDS;
import org.briarproject.api.crypto.CryptoExecutor;
import org.briarproject.api.crypto.PasswordStrengthEstimator;
import org.briarproject.api.crypto.StreamDecrypterFactory;
import org.briarproject.api.crypto.StreamEncrypterFactory;
import org.briarproject.api.lifecycle.LifecycleManager;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
public class CryptoModule extends AbstractModule { public class CryptoModule extends AbstractModule {
...@@ -42,7 +42,8 @@ public class CryptoModule extends AbstractModule { ...@@ -42,7 +42,8 @@ public class CryptoModule extends AbstractModule {
@Override @Override
protected void configure() { protected void configure() {
bind(AuthenticatedCipher.class).to(AuthenticatedCipherImpl.class); bind(AuthenticatedCipher.class).to(
XSalsa20Poly1305AuthenticatedCipher.class);
bind(CryptoComponent.class).to( bind(CryptoComponent.class).to(
CryptoComponentImpl.class).in(Singleton.class); CryptoComponentImpl.class).in(Singleton.class);
bind(PasswordStrengthEstimator.class).to( bind(PasswordStrengthEstimator.class).to(
......
...@@ -24,7 +24,8 @@ import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; ...@@ -24,7 +24,8 @@ import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH;
* <li>http://cr.yp.to/highspeed/naclcrypto-20090310.pdf</li> * <li>http://cr.yp.to/highspeed/naclcrypto-20090310.pdf</li>
* </ul> * </ul>
*/ */
public class XSalsa20Poly1305AC implements AuthenticatedCipher { public class XSalsa20Poly1305AuthenticatedCipher
implements AuthenticatedCipher {
/** Length of the padding to be used to generate the Poly1305 key */ /** Length of the padding to be used to generate the Poly1305 key */
private static final int SUBKEY_LENGTH = 32; private static final int SUBKEY_LENGTH = 32;
...@@ -34,13 +35,14 @@ public class XSalsa20Poly1305AC implements AuthenticatedCipher { ...@@ -34,13 +35,14 @@ public class XSalsa20Poly1305AC implements AuthenticatedCipher {
private boolean encrypting; private boolean encrypting;
XSalsa20Poly1305AC() { XSalsa20Poly1305AuthenticatedCipher() {
xSalsa20Engine = new XSalsa20Engine(); xSalsa20Engine = new XSalsa20Engine();
poly1305 = new Poly1305(); poly1305 = new Poly1305();
} }
@Override @Override
public void init(boolean encrypt, SecretKey key, byte[] iv) throws GeneralSecurityException { public void init(boolean encrypt, SecretKey key, byte[] iv)
throws GeneralSecurityException {
encrypting = encrypt; encrypting = encrypt;
KeyParameter k = new KeyParameter(key.getBytes()); KeyParameter k = new KeyParameter(key.getBytes());
ParametersWithIV params = new ParametersWithIV(k, iv); ParametersWithIV params = new ParametersWithIV(k, iv);
...@@ -52,12 +54,10 @@ public class XSalsa20Poly1305AC implements AuthenticatedCipher { ...@@ -52,12 +54,10 @@ public class XSalsa20Poly1305AC implements AuthenticatedCipher {
} }
@Override @Override
public int process(byte[] input, int inputOff, int len, byte[] output, int outputOff) throws GeneralSecurityException { public int process(byte[] input, int inputOff, int len, byte[] output,
if (len == 0) int outputOff) throws GeneralSecurityException {
return 0; if (!encrypting && len < MAC_LENGTH)
else if (!encrypting && len < MAC_LENGTH)
throw new GeneralSecurityException("Invalid MAC"); throw new GeneralSecurityException("Invalid MAC");
try { try {
// Generate the Poly1305 subkey from an empty array // Generate the Poly1305 subkey from an empty array
byte[] zero = new byte[SUBKEY_LENGTH]; byte[] zero = new byte[SUBKEY_LENGTH];
...@@ -100,7 +100,7 @@ public class XSalsa20Poly1305AC implements AuthenticatedCipher { ...@@ -100,7 +100,7 @@ public class XSalsa20Poly1305AC implements AuthenticatedCipher {
throw new GeneralSecurityException("Invalid MAC"); throw new GeneralSecurityException("Invalid MAC");
} }
// Invert the stream encryption // Apply or invert the stream encryption
int processed = xSalsa20Engine.processBytes( int processed = xSalsa20Engine.processBytes(
input, encrypting ? inputOff : inputOff + MAC_LENGTH, input, encrypting ? inputOff : inputOff + MAC_LENGTH,
encrypting ? len : len - MAC_LENGTH, encrypting ? len : len - MAC_LENGTH,
......
...@@ -6,10 +6,11 @@ import org.briarproject.util.StringUtils; ...@@ -6,10 +6,11 @@ import org.briarproject.util.StringUtils;
import org.junit.Test; import org.junit.Test;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Random;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
public class XSalsa20Poly1305ACTest extends BriarTestCase { public class XSalsa20Poly1305AuthenticatedCipherTest extends BriarTestCase {
// Test vectors from the NaCl paper // Test vectors from the NaCl paper
// http://cr.yp.to/highspeed/naclcrypto-20090310.pdf // http://cr.yp.to/highspeed/naclcrypto-20090310.pdf
...@@ -47,9 +48,9 @@ public class XSalsa20Poly1305ACTest extends BriarTestCase { ...@@ -47,9 +48,9 @@ public class XSalsa20Poly1305ACTest extends BriarTestCase {
@Test @Test
public void testEncrypt() throws Exception { public void testEncrypt() throws Exception {
SecretKey k = new SecretKey(TEST_KEY); SecretKey k = new SecretKey(TEST_KEY);
AuthenticatedCipher cipher = new XSalsa20Poly1305AC(); AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
cipher.init(true, k, TEST_IV); cipher.init(true, k, TEST_IV);
byte[] output = new byte[TEST_PLAINTEXT.length + cipher.getMacBytes()]; byte[] output = new byte[TEST_CIPHERTEXT.length];
cipher.process(TEST_PLAINTEXT, 0, TEST_PLAINTEXT.length, output, 0); cipher.process(TEST_PLAINTEXT, 0, TEST_PLAINTEXT.length, output, 0);
assertArrayEquals(TEST_CIPHERTEXT, output); assertArrayEquals(TEST_CIPHERTEXT, output);
} }
...@@ -57,9 +58,9 @@ public class XSalsa20Poly1305ACTest extends BriarTestCase { ...@@ -57,9 +58,9 @@ public class XSalsa20Poly1305ACTest extends BriarTestCase {
@Test @Test
public void testDecrypt() throws Exception { public void testDecrypt() throws Exception {
SecretKey k = new SecretKey(TEST_KEY); SecretKey k = new SecretKey(TEST_KEY);
AuthenticatedCipher cipher = new XSalsa20Poly1305AC(); AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
cipher.init(false, k, TEST_IV); cipher.init(false, k, TEST_IV);
byte[] output = new byte[TEST_CIPHERTEXT.length - cipher.getMacBytes()]; byte[] output = new byte[TEST_PLAINTEXT.length];
cipher.process(TEST_CIPHERTEXT, 0, TEST_CIPHERTEXT.length, output, 0); cipher.process(TEST_CIPHERTEXT, 0, TEST_CIPHERTEXT.length, output, 0);
assertArrayEquals(TEST_PLAINTEXT, output); assertArrayEquals(TEST_PLAINTEXT, output);
} }
...@@ -67,23 +68,23 @@ public class XSalsa20Poly1305ACTest extends BriarTestCase { ...@@ -67,23 +68,23 @@ public class XSalsa20Poly1305ACTest extends BriarTestCase {
@Test(expected = GeneralSecurityException.class) @Test(expected = GeneralSecurityException.class)
public void testDecryptFailsWithShortInput() throws Exception { public void testDecryptFailsWithShortInput() throws Exception {
SecretKey k = new SecretKey(TEST_KEY); SecretKey k = new SecretKey(TEST_KEY);
AuthenticatedCipher cipher = new XSalsa20Poly1305AC(); AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
cipher.init(false, k, TEST_IV); cipher.init(false, k, TEST_IV);
byte[] input = new byte[8]; byte[] input = new byte[cipher.getMacBytes() - 1];
System.arraycopy(TEST_CIPHERTEXT, 0, input, 0, 8); System.arraycopy(TEST_CIPHERTEXT, 0, input, 0, input.length);
byte[] output = new byte[TEST_CIPHERTEXT.length - cipher.getMacBytes()]; byte[] output = new byte[TEST_PLAINTEXT.length];
cipher.process(input, 0, input.length, output, 0); cipher.process(input, 0, input.length, output, 0);
} }
@Test(expected = GeneralSecurityException.class) @Test(expected = GeneralSecurityException.class)
public void testDecryptFailsWithAlteredCiphertext() throws Exception { public void testDecryptFailsWithAlteredCiphertext() throws Exception {
SecretKey k = new SecretKey(TEST_KEY); SecretKey k = new SecretKey(TEST_KEY);
AuthenticatedCipher cipher = new XSalsa20Poly1305AC(); AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
cipher.init(false, k, TEST_IV); cipher.init(false, k, TEST_IV);
byte[] input = new byte[TEST_CIPHERTEXT.length]; byte[] input = new byte[TEST_CIPHERTEXT.length];
System.arraycopy(TEST_CIPHERTEXT, 0, input, 0, TEST_CIPHERTEXT.length); System.arraycopy(TEST_CIPHERTEXT, 0, input, 0, TEST_CIPHERTEXT.length);
input[TEST_CIPHERTEXT.length - cipher.getMacBytes()] = 42; input[new Random().nextInt(TEST_CIPHERTEXT.length)] ^= 0xFF;
byte[] output = new byte[TEST_CIPHERTEXT.length - cipher.getMacBytes()]; byte[] output = new byte[TEST_PLAINTEXT.length];
cipher.process(input, 0, input.length, output, 0); cipher.process(input, 0, input.length, output, 0);
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment