diff --git a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java index dedcafbff7896a3a1b8c957c3e73643d2eef04c6..924afbde7c620767b6aa2342117cbf090e38e39c 100644 --- a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java +++ b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java @@ -3,7 +3,6 @@ package org.briarproject.api.crypto; import org.briarproject.api.TransportId; import org.briarproject.api.transport.TransportKeys; -import java.io.IOException; import java.security.GeneralSecurityException; import java.security.SecureRandom; @@ -163,5 +162,11 @@ public interface CryptoComponent { /** * 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); } diff --git a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java index 2411e51989c57386115b767025eec02f3e4068b3..459ee6b9280eee3e976e42d2e31f6d546ddad7ca 100644 --- a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java +++ b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java @@ -120,20 +120,24 @@ class CryptoComponentImpl implements CryptoComponent { messageEncrypter = new MessageEncrypter(secureRandom); } + @Override public SecretKey generateSecretKey() { byte[] b = new byte[SecretKey.LENGTH]; secureRandom.nextBytes(b); return new SecretKey(b); } + @Override public MessageDigest getMessageDigest() { return new DigestWrapper(new Blake2sDigest()); } + @Override public PseudoRandom getPseudoRandom(int seed1, int seed2) { return new PseudoRandomImpl(seed1, seed2); } + @Override public SecureRandom getSecureRandom() { return secureRandom; } @@ -157,10 +161,12 @@ class CryptoComponentImpl implements CryptoComponent { return secret; } + @Override public Signature getSignature() { return new SignatureImpl(secureRandom); } + @Override public KeyPair generateAgreementKeyPair() { AsymmetricCipherKeyPair keyPair = agreementKeyPairGenerator.generateKeyPair(); @@ -176,10 +182,12 @@ class CryptoComponentImpl implements CryptoComponent { return new KeyPair(publicKey, privateKey); } + @Override public KeyParser getAgreementKeyParser() { return agreementKeyParser; } + @Override public KeyPair generateSignatureKeyPair() { AsymmetricCipherKeyPair keyPair = signatureKeyPairGenerator.generateKeyPair(); @@ -195,14 +203,17 @@ class CryptoComponentImpl implements CryptoComponent { return new KeyPair(publicKey, privateKey); } + @Override public KeyParser getSignatureKeyParser() { return signatureKeyParser; } + @Override public KeyParser getMessageKeyParser() { return messageEncrypter.getKeyParser(); } + @Override public int generateBTInvitationCode() { int codeBytes = (CODE_BITS + 7) / 8; byte[] random = new byte[codeBytes]; @@ -210,21 +221,25 @@ class CryptoComponentImpl implements CryptoComponent { return ByteUtils.readUint(random, CODE_BITS); } + @Override public int deriveBTConfirmationCode(SecretKey master, boolean alice) { byte[] b = macKdf(master, alice ? BT_A_CONFIRM : BT_B_CONFIRM); return ByteUtils.readUint(b, CODE_BITS); } + @Override public SecretKey deriveHeaderKey(SecretKey master, boolean alice) { return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE)); } + @Override public byte[] deriveSignatureNonce(SecretKey master, boolean alice) { return macKdf(master, alice ? A_SIG_NONCE : B_SIG_NONCE); } + @Override public byte[] deriveKeyCommitment(byte[] publicKey) { byte[] hash = hash(COMMIT, publicKey); // The output is the first COMMIT_LENGTH bytes of the hash @@ -233,6 +248,7 @@ class CryptoComponentImpl implements CryptoComponent { return commitment; } + @Override public SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException { PrivateKey ourPriv = ourKeyPair.getPrivate(); @@ -249,6 +265,7 @@ class CryptoComponentImpl implements CryptoComponent { return new SecretKey(hash(SHARED_SECRET, raw, alicePub, bobPub)); } + @Override public byte[] deriveConfirmationRecord(SecretKey sharedSecret, byte[] theirPayload, byte[] ourPayload, byte[] theirPublicKey, KeyPair ourKeyPair, boolean alice, boolean aliceRecord) { @@ -271,16 +288,19 @@ class CryptoComponentImpl implements CryptoComponent { return macKdf(ck, bobPayload, bobPub, alicePayload, alicePub); } + @Override public SecretKey deriveMasterSecret(SecretKey sharedSecret) { return new SecretKey(macKdf(sharedSecret, MASTER_KEY)); } + @Override public SecretKey deriveMasterSecret(byte[] theirPublicKey, KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException { return deriveMasterSecret(deriveSharedSecret( theirPublicKey,ourKeyPair, alice)); } + @Override public TransportKeys deriveTransportKeys(TransportId t, SecretKey master, long rotationPeriod, boolean alice) { // 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); } + @Override public TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod) { 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)); } + @Override public void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber) { if (tag.length < TAG_LENGTH) throw new IllegalArgumentException(); if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED) @@ -369,6 +391,7 @@ class CryptoComponentImpl implements CryptoComponent { System.arraycopy(mac, 0, tag, 0, TAG_LENGTH); } + @Override public byte[] hash(byte[]... inputs) { MessageDigest digest = getMessageDigest(); byte[] length = new byte[INT_32_BYTES]; @@ -380,6 +403,7 @@ class CryptoComponentImpl implements CryptoComponent { return digest.digest(); } + @Override public byte[] encryptWithPassword(byte[] input, String password) { AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher(); int macBytes = cipher.getMacBytes(); @@ -411,6 +435,7 @@ class CryptoComponentImpl implements CryptoComponent { } } + @Override public byte[] decryptWithPassword(byte[] input, String password) { AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher(); 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 { - byte[] ciphertext = messageEncrypter.encrypt(publicKey, plaintext); - return AsciiArmour.wrap(ciphertext, 70); + return messageEncrypter.encrypt(publicKey, plaintext); } catch (CryptoException 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 // NIST SP 800-108, section 5.1 private byte[] macKdf(SecretKey key, byte[]... inputs) { diff --git a/briar-core/src/org/briarproject/reporting/DevReporterImpl.java b/briar-core/src/org/briarproject/reporting/DevReporterImpl.java index 6d5c49f73a7a6917d385c10d8e535fa81a65f1a4..52bab15338ca3ba91652169bfb63c244afecbbb4 100644 --- a/briar-core/src/org/briarproject/reporting/DevReporterImpl.java +++ b/briar-core/src/org/briarproject/reporting/DevReporterImpl.java @@ -1,7 +1,5 @@ package org.briarproject.reporting; -import com.google.common.io.Files; - import net.sourceforge.jsocks.socks.Socks5Proxy; import net.sourceforge.jsocks.socks.SocksException; 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.DevReporter; import org.briarproject.util.StringUtils; +import org.h2.util.IOUtils; +import java.io.Closeable; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; -import java.nio.charset.Charset; -import java.util.List; import java.util.logging.Logger; import static java.util.logging.Level.WARNING; @@ -33,7 +33,7 @@ class DevReporterImpl implements DevReporter { Logger.getLogger(DevReporterImpl.class.getName()); 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 DevConfig devConfig; @@ -55,16 +55,17 @@ class DevReporterImpl implements DevReporter { @Override public void encryptReportToFile(File reportDir, String filename, String report) throws FileNotFoundException { - String encryptedReport = - crypto.encryptToKey(devConfig.getDevPublicKey(), - StringUtils.toUtf8(report)); + byte[] plaintext = StringUtils.toUtf8(report); + byte[] ciphertext = crypto.encryptToKey(devConfig.getDevPublicKey(), + plaintext); + String armoured = crypto.asciiArmour(ciphertext, LINE_LENGTH); File f = new File(reportDir, filename); PrintWriter writer = null; try { writer = new PrintWriter( new OutputStreamWriter(new FileOutputStream(f))); - writer.append(encryptedReport); + writer.append(armoured); writer.flush(); } finally { if (writer != null) @@ -78,41 +79,31 @@ class DevReporterImpl implements DevReporter { if (reports == null || reports.length == 0) 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"); - OutputStream output; - PrintWriter writer = null; - try { - output = s.getOutputStream(); - writer = new PrintWriter( - new OutputStreamWriter(output, "UTF-8"), true); - for (File f : reports) { - List<String> encryptedReport = Files.readLines(f, - Charset.forName("UTF-8")); - writer.append(f.getName()).append(CRLF); - for (String line : encryptedReport) { - writer.append(line).append(CRLF); - } - writer.append(CRLF); - writer.flush(); + for (File f : reports) { + OutputStream out = null; + InputStream in = null; + try { + Socket s = connectToDevelopers(socksPort); + out = s.getOutputStream(); + in = new FileInputStream(f); + IOUtils.copy(in, out); 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) { - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, "Connection to developers failed", e); - } finally { - if (writer != null) - writer.close(); + if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } } }