From d9808c48f04a1c4072e534351326532fae23e353 Mon Sep 17 00:00:00 2001
From: str4d <str4d@mail.i2p>
Date: Tue, 15 Dec 2015 14:19:57 +0000
Subject: [PATCH] Implement XSalsa20/Poly1305

---
 .../crypto/XSalsa20Poly1305AC.java            | 125 ++++++++++++++++++
 1 file changed, 125 insertions(+)
 create mode 100644 briar-core/src/org/briarproject/crypto/XSalsa20Poly1305AC.java

diff --git a/briar-core/src/org/briarproject/crypto/XSalsa20Poly1305AC.java b/briar-core/src/org/briarproject/crypto/XSalsa20Poly1305AC.java
new file mode 100644
index 0000000000..7a4a39d90b
--- /dev/null
+++ b/briar-core/src/org/briarproject/crypto/XSalsa20Poly1305AC.java
@@ -0,0 +1,125 @@
+package org.briarproject.crypto;
+
+import org.briarproject.api.crypto.SecretKey;
+import org.spongycastle.crypto.DataLengthException;
+import org.spongycastle.crypto.engines.XSalsa20Engine;
+import org.spongycastle.crypto.generators.Poly1305KeyGenerator;
+import org.spongycastle.crypto.macs.Poly1305;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.params.ParametersWithIV;
+
+import java.security.GeneralSecurityException;
+
+import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH;
+
+/**
+ * An authenticated cipher that uses XSalsa20 for encryption and Poly1305 for
+ * authentication. It is equivalent to the C++ implementation of
+ * crypto_secretbox in NaCl, and to the C implementations of crypto_secretbox
+ * in NaCl and libsodium once the zero-padding has been removed.
+ * <p/>
+ * References:
+ * <ul>
+ *     <li>http://nacl.cr.yp.to/secretbox.html</li>
+ *     <li>http://cr.yp.to/highspeed/naclcrypto-20090310.pdf</li>
+ * </ul>
+ */
+public class XSalsa20Poly1305AC implements AuthenticatedCipher {
+
+	/** Length of the padding to be used to generate the Poly1305 key */
+	private static final int SUBKEY_LENGTH = 32;
+
+	private final XSalsa20Engine xSalsa20Engine;
+	private final Poly1305 poly1305;
+
+	private boolean encrypting;
+
+	XSalsa20Poly1305AC() {
+		xSalsa20Engine = new XSalsa20Engine();
+		poly1305 = new Poly1305();
+	}
+
+	@Override
+	public void init(boolean encrypt, SecretKey key, byte[] iv) throws GeneralSecurityException {
+		encrypting = encrypt;
+		KeyParameter k = new KeyParameter(key.getBytes());
+		ParametersWithIV params = new ParametersWithIV(k, iv);
+		try {
+			xSalsa20Engine.init(encrypt, params);
+		} catch (IllegalArgumentException e) {
+			throw new GeneralSecurityException(e.getMessage());
+		}
+	}
+
+	@Override
+	public int process(byte[] input, int inputOff, int len, byte[] output, int outputOff) throws GeneralSecurityException {
+		if (len == 0)
+			return 0;
+		else if (!encrypting && len < MAC_LENGTH)
+			throw new GeneralSecurityException("Invalid MAC");
+
+		try {
+			// Generate the Poly1305 subkey from an empty array
+			byte[] zero = new byte[SUBKEY_LENGTH];
+			byte[] subKey = new byte[SUBKEY_LENGTH];
+			xSalsa20Engine.processBytes(zero, 0, SUBKEY_LENGTH, subKey, 0);
+
+			// Reverse the order of the Poly130 subkey
+			//
+			// NaCl and libsodium use the first 32 bytes of XSalsa20 as the
+			// subkey for crypto_onetimeauth_poly1305, which interprets it
+			// as r[0] ... r[15], k[0] ... k[15]. See section 9 of the NaCl
+			// paper (http://cr.yp.to/highspeed/naclcrypto-20090310.pdf),
+			// where the XSalsa20 output is defined as (r, s, t, ...).
+			//
+			// BC's Poly1305 implementation interprets the subkey as
+			// k[0] ... k[15], r[0] ... r[15] (per poly1305_aes_clamp in
+			// the reference implementation).
+			//
+			// To be NaCl-compatible, we reverse the subkey.
+			System.arraycopy(subKey, 0, zero, 0, SUBKEY_LENGTH / 2);
+			System.arraycopy(subKey, SUBKEY_LENGTH / 2, subKey, 0, SUBKEY_LENGTH / 2);
+			System.arraycopy(zero, 0, subKey, SUBKEY_LENGTH / 2, SUBKEY_LENGTH / 2);
+			// Now we can clamp the correct part of the subkey
+			Poly1305KeyGenerator.clamp(subKey);
+
+			// Initialize Poly1305 with the subkey
+			KeyParameter k = new KeyParameter(subKey);
+			poly1305.init(k);
+
+			// If we are decrypting, verify the MAC
+			if (!encrypting) {
+				byte[] mac = new byte[MAC_LENGTH];
+				poly1305.update(input, inputOff + MAC_LENGTH, len - MAC_LENGTH);
+				poly1305.doFinal(mac, 0);
+				// Constant-time comparison
+				int cmp = 0;
+				for (int i = 0; i < MAC_LENGTH; i++)
+					cmp |= mac[i] ^ input[inputOff + i];
+				if (cmp != 0)
+					throw new GeneralSecurityException("Invalid MAC");
+			}
+
+			// Invert the stream encryption
+			int processed = xSalsa20Engine.processBytes(
+					input, encrypting ? inputOff : inputOff + MAC_LENGTH,
+					encrypting ? len : len - MAC_LENGTH,
+					output, encrypting ? outputOff + MAC_LENGTH : outputOff);
+
+			// If we are encrypting, generate the MAC
+			if (encrypting) {
+				poly1305.update(output, outputOff + MAC_LENGTH, len);
+				poly1305.doFinal(output, outputOff);
+			}
+
+			return processed;
+		} catch (DataLengthException e) {
+			throw new GeneralSecurityException(e.getMessage());
+		}
+	}
+
+	@Override
+	public int getMacBytes() {
+		return MAC_LENGTH;
+	}
+}
-- 
GitLab