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:
akwizgran
2016-08-22 16:12:57 +01:00
parent d24e18a5d9
commit c6c62cab6c
3 changed files with 71 additions and 45 deletions

View File

@@ -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);
} }

View File

@@ -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) {

View File

@@ -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();
} }
} }
} }