mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 14:19:53 +01:00
Use one connection per dev report.
This allows simpler server-side code, and a failure part-way through sending won't require restarting from the beginning next time.
This commit is contained in:
@@ -3,7 +3,6 @@ package org.briarproject.api.crypto;
|
|||||||
import org.briarproject.api.TransportId;
|
import org.briarproject.api.TransportId;
|
||||||
import org.briarproject.api.transport.TransportKeys;
|
import org.briarproject.api.transport.TransportKeys;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
@@ -163,5 +162,11 @@ public interface CryptoComponent {
|
|||||||
/**
|
/**
|
||||||
* Encrypts the given plaintext to the given public key.
|
* Encrypts the given plaintext to the given public key.
|
||||||
*/
|
*/
|
||||||
String encryptToKey(PublicKey publicKey, byte[] plaintext);
|
byte[] encryptToKey(PublicKey publicKey, byte[] plaintext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes the given data as a hex string divided into lines of the given
|
||||||
|
* length. The line terminator is CRLF.
|
||||||
|
*/
|
||||||
|
String asciiArmour(byte[] b, int lineLength);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,20 +120,24 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
messageEncrypter = new MessageEncrypter(secureRandom);
|
messageEncrypter = new MessageEncrypter(secureRandom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public SecretKey generateSecretKey() {
|
public SecretKey generateSecretKey() {
|
||||||
byte[] b = new byte[SecretKey.LENGTH];
|
byte[] b = new byte[SecretKey.LENGTH];
|
||||||
secureRandom.nextBytes(b);
|
secureRandom.nextBytes(b);
|
||||||
return new SecretKey(b);
|
return new SecretKey(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public MessageDigest getMessageDigest() {
|
public MessageDigest getMessageDigest() {
|
||||||
return new DigestWrapper(new Blake2sDigest());
|
return new DigestWrapper(new Blake2sDigest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public PseudoRandom getPseudoRandom(int seed1, int seed2) {
|
public PseudoRandom getPseudoRandom(int seed1, int seed2) {
|
||||||
return new PseudoRandomImpl(seed1, seed2);
|
return new PseudoRandomImpl(seed1, seed2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public SecureRandom getSecureRandom() {
|
public SecureRandom getSecureRandom() {
|
||||||
return secureRandom;
|
return secureRandom;
|
||||||
}
|
}
|
||||||
@@ -157,10 +161,12 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Signature getSignature() {
|
public Signature getSignature() {
|
||||||
return new SignatureImpl(secureRandom);
|
return new SignatureImpl(secureRandom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public KeyPair generateAgreementKeyPair() {
|
public KeyPair generateAgreementKeyPair() {
|
||||||
AsymmetricCipherKeyPair keyPair =
|
AsymmetricCipherKeyPair keyPair =
|
||||||
agreementKeyPairGenerator.generateKeyPair();
|
agreementKeyPairGenerator.generateKeyPair();
|
||||||
@@ -176,10 +182,12 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return new KeyPair(publicKey, privateKey);
|
return new KeyPair(publicKey, privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public KeyParser getAgreementKeyParser() {
|
public KeyParser getAgreementKeyParser() {
|
||||||
return agreementKeyParser;
|
return agreementKeyParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public KeyPair generateSignatureKeyPair() {
|
public KeyPair generateSignatureKeyPair() {
|
||||||
AsymmetricCipherKeyPair keyPair =
|
AsymmetricCipherKeyPair keyPair =
|
||||||
signatureKeyPairGenerator.generateKeyPair();
|
signatureKeyPairGenerator.generateKeyPair();
|
||||||
@@ -195,14 +203,17 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return new KeyPair(publicKey, privateKey);
|
return new KeyPair(publicKey, privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public KeyParser getSignatureKeyParser() {
|
public KeyParser getSignatureKeyParser() {
|
||||||
return signatureKeyParser;
|
return signatureKeyParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public KeyParser getMessageKeyParser() {
|
public KeyParser getMessageKeyParser() {
|
||||||
return messageEncrypter.getKeyParser();
|
return messageEncrypter.getKeyParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int generateBTInvitationCode() {
|
public int generateBTInvitationCode() {
|
||||||
int codeBytes = (CODE_BITS + 7) / 8;
|
int codeBytes = (CODE_BITS + 7) / 8;
|
||||||
byte[] random = new byte[codeBytes];
|
byte[] random = new byte[codeBytes];
|
||||||
@@ -210,21 +221,25 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return ByteUtils.readUint(random, CODE_BITS);
|
return ByteUtils.readUint(random, CODE_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int deriveBTConfirmationCode(SecretKey master, boolean alice) {
|
public int deriveBTConfirmationCode(SecretKey master, boolean alice) {
|
||||||
byte[] b = macKdf(master, alice ? BT_A_CONFIRM : BT_B_CONFIRM);
|
byte[] b = macKdf(master, alice ? BT_A_CONFIRM : BT_B_CONFIRM);
|
||||||
return ByteUtils.readUint(b, CODE_BITS);
|
return ByteUtils.readUint(b, CODE_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public SecretKey deriveHeaderKey(SecretKey master,
|
public SecretKey deriveHeaderKey(SecretKey master,
|
||||||
boolean alice) {
|
boolean alice) {
|
||||||
return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE));
|
return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public byte[] deriveSignatureNonce(SecretKey master,
|
public byte[] deriveSignatureNonce(SecretKey master,
|
||||||
boolean alice) {
|
boolean alice) {
|
||||||
return macKdf(master, alice ? A_SIG_NONCE : B_SIG_NONCE);
|
return macKdf(master, alice ? A_SIG_NONCE : B_SIG_NONCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public byte[] deriveKeyCommitment(byte[] publicKey) {
|
public byte[] deriveKeyCommitment(byte[] publicKey) {
|
||||||
byte[] hash = hash(COMMIT, publicKey);
|
byte[] hash = hash(COMMIT, publicKey);
|
||||||
// The output is the first COMMIT_LENGTH bytes of the hash
|
// The output is the first COMMIT_LENGTH bytes of the hash
|
||||||
@@ -233,6 +248,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return commitment;
|
return commitment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public SecretKey deriveSharedSecret(byte[] theirPublicKey,
|
public SecretKey deriveSharedSecret(byte[] theirPublicKey,
|
||||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
||||||
PrivateKey ourPriv = ourKeyPair.getPrivate();
|
PrivateKey ourPriv = ourKeyPair.getPrivate();
|
||||||
@@ -249,6 +265,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return new SecretKey(hash(SHARED_SECRET, raw, alicePub, bobPub));
|
return new SecretKey(hash(SHARED_SECRET, raw, alicePub, bobPub));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
||||||
byte[] theirPayload, byte[] ourPayload, byte[] theirPublicKey,
|
byte[] theirPayload, byte[] ourPayload, byte[] theirPublicKey,
|
||||||
KeyPair ourKeyPair, boolean alice, boolean aliceRecord) {
|
KeyPair ourKeyPair, boolean alice, boolean aliceRecord) {
|
||||||
@@ -271,16 +288,19 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return macKdf(ck, bobPayload, bobPub, alicePayload, alicePub);
|
return macKdf(ck, bobPayload, bobPub, alicePayload, alicePub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public SecretKey deriveMasterSecret(SecretKey sharedSecret) {
|
public SecretKey deriveMasterSecret(SecretKey sharedSecret) {
|
||||||
return new SecretKey(macKdf(sharedSecret, MASTER_KEY));
|
return new SecretKey(macKdf(sharedSecret, MASTER_KEY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
|
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
|
||||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
||||||
return deriveMasterSecret(deriveSharedSecret(
|
return deriveMasterSecret(deriveSharedSecret(
|
||||||
theirPublicKey,ourKeyPair, alice));
|
theirPublicKey,ourKeyPair, alice));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public TransportKeys deriveTransportKeys(TransportId t,
|
public TransportKeys deriveTransportKeys(TransportId t,
|
||||||
SecretKey master, long rotationPeriod, boolean alice) {
|
SecretKey master, long rotationPeriod, boolean alice) {
|
||||||
// Keys for the previous period are derived from the master secret
|
// Keys for the previous period are derived from the master secret
|
||||||
@@ -308,6 +328,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public TransportKeys rotateTransportKeys(TransportKeys k,
|
public TransportKeys rotateTransportKeys(TransportKeys k,
|
||||||
long rotationPeriod) {
|
long rotationPeriod) {
|
||||||
if (k.getRotationPeriod() >= rotationPeriod) return k;
|
if (k.getRotationPeriod() >= rotationPeriod) return k;
|
||||||
@@ -350,6 +371,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return new SecretKey(macKdf(master, alice ? A_HEADER : B_HEADER, id));
|
return new SecretKey(macKdf(master, alice ? A_HEADER : B_HEADER, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber) {
|
public void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber) {
|
||||||
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
||||||
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
||||||
@@ -369,6 +391,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
|
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public byte[] hash(byte[]... inputs) {
|
public byte[] hash(byte[]... inputs) {
|
||||||
MessageDigest digest = getMessageDigest();
|
MessageDigest digest = getMessageDigest();
|
||||||
byte[] length = new byte[INT_32_BYTES];
|
byte[] length = new byte[INT_32_BYTES];
|
||||||
@@ -380,6 +403,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return digest.digest();
|
return digest.digest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public byte[] encryptWithPassword(byte[] input, String password) {
|
public byte[] encryptWithPassword(byte[] input, String password) {
|
||||||
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
||||||
int macBytes = cipher.getMacBytes();
|
int macBytes = cipher.getMacBytes();
|
||||||
@@ -411,6 +435,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public byte[] decryptWithPassword(byte[] input, String password) {
|
public byte[] decryptWithPassword(byte[] input, String password) {
|
||||||
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
||||||
int macBytes = cipher.getMacBytes();
|
int macBytes = cipher.getMacBytes();
|
||||||
@@ -445,15 +470,20 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String encryptToKey(PublicKey publicKey, byte[] plaintext) {
|
@Override
|
||||||
|
public byte[] encryptToKey(PublicKey publicKey, byte[] plaintext) {
|
||||||
try {
|
try {
|
||||||
byte[] ciphertext = messageEncrypter.encrypt(publicKey, plaintext);
|
return messageEncrypter.encrypt(publicKey, plaintext);
|
||||||
return AsciiArmour.wrap(ciphertext, 70);
|
|
||||||
} catch (CryptoException e) {
|
} catch (CryptoException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String asciiArmour(byte[] b, int lineLength) {
|
||||||
|
return AsciiArmour.wrap(b, lineLength);
|
||||||
|
}
|
||||||
|
|
||||||
// Key derivation function based on a pseudo-random function - see
|
// Key derivation function based on a pseudo-random function - see
|
||||||
// NIST SP 800-108, section 5.1
|
// NIST SP 800-108, section 5.1
|
||||||
private byte[] macKdf(SecretKey key, byte[]... inputs) {
|
private byte[] macKdf(SecretKey key, byte[]... inputs) {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package org.briarproject.reporting;
|
package org.briarproject.reporting;
|
||||||
|
|
||||||
import com.google.common.io.Files;
|
|
||||||
|
|
||||||
import net.sourceforge.jsocks.socks.Socks5Proxy;
|
import net.sourceforge.jsocks.socks.Socks5Proxy;
|
||||||
import net.sourceforge.jsocks.socks.SocksException;
|
import net.sourceforge.jsocks.socks.SocksException;
|
||||||
import net.sourceforge.jsocks.socks.SocksSocket;
|
import net.sourceforge.jsocks.socks.SocksSocket;
|
||||||
@@ -10,19 +8,21 @@ import org.briarproject.api.crypto.CryptoComponent;
|
|||||||
import org.briarproject.api.reporting.DevConfig;
|
import org.briarproject.api.reporting.DevConfig;
|
||||||
import org.briarproject.api.reporting.DevReporter;
|
import org.briarproject.api.reporting.DevReporter;
|
||||||
import org.briarproject.util.StringUtils;
|
import org.briarproject.util.StringUtils;
|
||||||
|
import org.h2.util.IOUtils;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
@@ -33,7 +33,7 @@ class DevReporterImpl implements DevReporter {
|
|||||||
Logger.getLogger(DevReporterImpl.class.getName());
|
Logger.getLogger(DevReporterImpl.class.getName());
|
||||||
|
|
||||||
private static final int SOCKET_TIMEOUT = 30 * 1000; // 30 seconds
|
private static final int SOCKET_TIMEOUT = 30 * 1000; // 30 seconds
|
||||||
private static final String CRLF = "\r\n";
|
private static final int LINE_LENGTH = 70;
|
||||||
|
|
||||||
private CryptoComponent crypto;
|
private CryptoComponent crypto;
|
||||||
private DevConfig devConfig;
|
private DevConfig devConfig;
|
||||||
@@ -55,16 +55,17 @@ class DevReporterImpl implements DevReporter {
|
|||||||
@Override
|
@Override
|
||||||
public void encryptReportToFile(File reportDir, String filename,
|
public void encryptReportToFile(File reportDir, String filename,
|
||||||
String report) throws FileNotFoundException {
|
String report) throws FileNotFoundException {
|
||||||
String encryptedReport =
|
byte[] plaintext = StringUtils.toUtf8(report);
|
||||||
crypto.encryptToKey(devConfig.getDevPublicKey(),
|
byte[] ciphertext = crypto.encryptToKey(devConfig.getDevPublicKey(),
|
||||||
StringUtils.toUtf8(report));
|
plaintext);
|
||||||
|
String armoured = crypto.asciiArmour(ciphertext, LINE_LENGTH);
|
||||||
|
|
||||||
File f = new File(reportDir, filename);
|
File f = new File(reportDir, filename);
|
||||||
PrintWriter writer = null;
|
PrintWriter writer = null;
|
||||||
try {
|
try {
|
||||||
writer = new PrintWriter(
|
writer = new PrintWriter(
|
||||||
new OutputStreamWriter(new FileOutputStream(f)));
|
new OutputStreamWriter(new FileOutputStream(f)));
|
||||||
writer.append(encryptedReport);
|
writer.append(armoured);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
} finally {
|
} finally {
|
||||||
if (writer != null)
|
if (writer != null)
|
||||||
@@ -78,41 +79,31 @@ class DevReporterImpl implements DevReporter {
|
|||||||
if (reports == null || reports.length == 0)
|
if (reports == null || reports.length == 0)
|
||||||
return; // No reports to send
|
return; // No reports to send
|
||||||
|
|
||||||
LOG.info("Connecting to developers");
|
|
||||||
Socket s;
|
|
||||||
try {
|
|
||||||
s = connectToDevelopers(socksPort);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, "Could not connect to developers", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.info("Sending reports to developers");
|
LOG.info("Sending reports to developers");
|
||||||
OutputStream output;
|
for (File f : reports) {
|
||||||
PrintWriter writer = null;
|
OutputStream out = null;
|
||||||
try {
|
InputStream in = null;
|
||||||
output = s.getOutputStream();
|
try {
|
||||||
writer = new PrintWriter(
|
Socket s = connectToDevelopers(socksPort);
|
||||||
new OutputStreamWriter(output, "UTF-8"), true);
|
out = s.getOutputStream();
|
||||||
for (File f : reports) {
|
in = new FileInputStream(f);
|
||||||
List<String> encryptedReport = Files.readLines(f,
|
IOUtils.copy(in, out);
|
||||||
Charset.forName("UTF-8"));
|
|
||||||
writer.append(f.getName()).append(CRLF);
|
|
||||||
for (String line : encryptedReport) {
|
|
||||||
writer.append(line).append(CRLF);
|
|
||||||
}
|
|
||||||
writer.append(CRLF);
|
|
||||||
writer.flush();
|
|
||||||
f.delete();
|
f.delete();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.log(WARNING, "Failed to send reports", e);
|
||||||
|
tryToClose(out);
|
||||||
|
tryToClose(in);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
LOG.info("Reports sent");
|
}
|
||||||
|
LOG.info("Reports sent");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryToClose(Closeable c) {
|
||||||
|
try {
|
||||||
|
if (c != null) c.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
LOG.log(WARNING, "Connection to developers failed", e);
|
|
||||||
} finally {
|
|
||||||
if (writer != null)
|
|
||||||
writer.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user