mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 11:49:04 +01:00
Store the incoming and outgoing secrets separately.
This commit is contained in:
@@ -53,17 +53,22 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
}
|
||||
|
||||
public ErasableKey deriveIncomingFrameKey(byte[] secret) {
|
||||
SharedSecret s = new SharedSecret(secret);
|
||||
return deriveFrameKey(s, !s.getAlice());
|
||||
public ErasableKey deriveFrameKey(byte[] source, boolean initiator) {
|
||||
if(initiator) return deriveKey("FRAME_I", source);
|
||||
else return deriveKey("FRAME_R", source);
|
||||
}
|
||||
|
||||
private ErasableKey deriveFrameKey(SharedSecret s, boolean alice) {
|
||||
if(alice) return deriveKey("F_A", s.getSecret());
|
||||
else return deriveKey("F_B", s.getSecret());
|
||||
public ErasableKey deriveIvKey(byte[] source, boolean initiator) {
|
||||
if(initiator) return deriveKey("IV_I", source);
|
||||
else return deriveKey("IV_R", source);
|
||||
}
|
||||
|
||||
private ErasableKey deriveKey(String name, byte[] secret) {
|
||||
public ErasableKey deriveMacKey(byte[] source, boolean initiator) {
|
||||
if(initiator) return deriveKey("MAC_I", source);
|
||||
else return deriveKey("MAC_R", source);
|
||||
}
|
||||
|
||||
private ErasableKey deriveKey(String name, byte[] source) {
|
||||
MessageDigest digest = getMessageDigest();
|
||||
assert digest.getDigestLength() == SECRET_KEY_BYTES;
|
||||
try {
|
||||
@@ -71,49 +76,20 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
} catch(UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
digest.update(secret);
|
||||
digest.update(source);
|
||||
return new ErasableKeyImpl(digest.digest(), SECRET_KEY_ALGO);
|
||||
}
|
||||
|
||||
public ErasableKey deriveIncomingIvKey(byte[] secret) {
|
||||
SharedSecret s = new SharedSecret(secret);
|
||||
return deriveIvKey(s, !s.getAlice());
|
||||
}
|
||||
|
||||
private ErasableKey deriveIvKey(SharedSecret s, boolean alice) {
|
||||
if(alice) return deriveKey("I_A", s.getSecret());
|
||||
else return deriveKey("I_B", s.getSecret());
|
||||
}
|
||||
|
||||
public ErasableKey deriveIncomingMacKey(byte[] secret) {
|
||||
SharedSecret s = new SharedSecret(secret);
|
||||
return deriveMacKey(s, !s.getAlice());
|
||||
}
|
||||
|
||||
private ErasableKey deriveMacKey(SharedSecret s, boolean alice) {
|
||||
if(alice) return deriveKey("M_A", s.getSecret());
|
||||
else return deriveKey("M_B", s.getSecret());
|
||||
}
|
||||
|
||||
public ErasableKey deriveOutgoingFrameKey(byte[] secret) {
|
||||
SharedSecret s = new SharedSecret(secret);
|
||||
return deriveFrameKey(s, s.getAlice());
|
||||
}
|
||||
|
||||
public ErasableKey deriveOutgoingIvKey(byte[] secret) {
|
||||
SharedSecret s = new SharedSecret(secret);
|
||||
return deriveIvKey(s, s.getAlice());
|
||||
}
|
||||
|
||||
public ErasableKey deriveOutgoingMacKey(byte[] secret) {
|
||||
SharedSecret s = new SharedSecret(secret);
|
||||
return deriveMacKey(s, s.getAlice());
|
||||
}
|
||||
|
||||
public KeyPair generateKeyPair() {
|
||||
return keyPairGenerator.generateKeyPair();
|
||||
}
|
||||
|
||||
public ErasableKey generateTestKey() {
|
||||
byte[] b = new byte[SECRET_KEY_BYTES];
|
||||
getSecureRandom().nextBytes(b);
|
||||
return new ErasableKeyImpl(b, SECRET_KEY_ALGO);
|
||||
}
|
||||
|
||||
public Cipher getFrameCipher() {
|
||||
try {
|
||||
return Cipher.getInstance(FRAME_CIPHER_ALGO, PROVIDER);
|
||||
@@ -177,10 +153,4 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public ErasableKey generateTestKey() {
|
||||
byte[] b = new byte[SECRET_KEY_BYTES];
|
||||
getSecureRandom().nextBytes(b);
|
||||
return new ErasableKeyImpl(b, SECRET_KEY_ALGO);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package net.sf.briar.crypto;
|
||||
|
||||
/**
|
||||
* A shared secret from which authentication and encryption keys can be derived.
|
||||
* The secret carries a flag indicating whether Alice's keys or Bob's keys
|
||||
* should be derived from the secret. When two parties agree on a shared secret,
|
||||
* they must decide which of them will derive Alice's keys and which Bob's.
|
||||
*/
|
||||
class SharedSecret {
|
||||
|
||||
private final boolean alice;
|
||||
private final byte[] secret;
|
||||
|
||||
SharedSecret(byte[] b) {
|
||||
if(b.length < 2) throw new IllegalArgumentException();
|
||||
switch(b[0]) {
|
||||
case 0:
|
||||
alice = false;
|
||||
break;
|
||||
case 1:
|
||||
alice = true;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
secret = new byte[b.length - 1];
|
||||
System.arraycopy(b, 1, secret, 0, secret.length);
|
||||
}
|
||||
|
||||
SharedSecret(boolean alice, byte[] secret) {
|
||||
this.alice = alice;
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we should play the role of Alice in connections using
|
||||
* this secret, or false if we should play the role of Bob.
|
||||
*/
|
||||
boolean getAlice() {
|
||||
return alice;
|
||||
}
|
||||
|
||||
/** Returns the shared secret. */
|
||||
byte[] getSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raw representation of this object, suitable for storing in the
|
||||
* database.
|
||||
*/
|
||||
byte[] getBytes() {
|
||||
byte[] b = new byte[1 + secret.length];
|
||||
if(alice) b[0] = (byte) 1;
|
||||
System.arraycopy(secret, 0, b, 1, secret.length);
|
||||
return b;
|
||||
}
|
||||
}
|
||||
@@ -80,12 +80,13 @@ interface Database<T> {
|
||||
void addBatchToAck(T txn, ContactId c, BatchId b) throws DbException;
|
||||
|
||||
/**
|
||||
* Adds a new contact to the database with the given secret and returns an
|
||||
* Adds a new contact to the database with the given secrets and returns an
|
||||
* ID for the contact.
|
||||
* <p>
|
||||
* Locking: contact write.
|
||||
*/
|
||||
ContactId addContact(T txn, byte[] secret) throws DbException;
|
||||
ContactId addContact(T txn, byte[] incomingSecret, byte[] outgoingSecret)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns false if the given message is already in the database. Otherwise
|
||||
@@ -376,7 +377,8 @@ interface Database<T> {
|
||||
* <p>
|
||||
* Locking: contact read.
|
||||
*/
|
||||
byte[] getSharedSecret(T txn, ContactId c) throws DbException;
|
||||
byte[] getSharedSecret(T txn, ContactId c, boolean incoming)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the given message has been starred.
|
||||
|
||||
@@ -135,14 +135,15 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
public ContactId addContact(byte[] secret) throws DbException {
|
||||
public ContactId addContact(byte[] incomingSecret, byte[] outgoingSecret)
|
||||
throws DbException {
|
||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding contact");
|
||||
ContactId c;
|
||||
contactLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
c = db.addContact(txn, secret);
|
||||
c = db.addContact(txn, incomingSecret, outgoingSecret);
|
||||
db.commitTransaction(txn);
|
||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Added contact " + c);
|
||||
} catch(DbException e) {
|
||||
@@ -905,13 +906,14 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getSharedSecret(ContactId c) throws DbException {
|
||||
public byte[] getSharedSecret(ContactId c, boolean incoming)
|
||||
throws DbException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
byte[] secret = db.getSharedSecret(txn, c);
|
||||
byte[] secret = db.getSharedSecret(txn, c, incoming);
|
||||
db.commitTransaction(txn);
|
||||
return secret;
|
||||
} catch(DbException e) {
|
||||
|
||||
@@ -56,7 +56,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
private static final String CREATE_CONTACTS =
|
||||
"CREATE TABLE contacts"
|
||||
+ " (contactId COUNTER,"
|
||||
+ " secret BINARY NOT NULL,"
|
||||
+ " incomingSecret BINARY NOT NULL,"
|
||||
+ " outgoingSecret BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId))";
|
||||
|
||||
private static final String CREATE_MESSAGES =
|
||||
@@ -509,15 +510,17 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public ContactId addContact(Connection txn, byte[] secret)
|
||||
throws DbException {
|
||||
public ContactId addContact(Connection txn, byte[] incomingSecret,
|
||||
byte[] outgoingSecret) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
// Create a new contact row
|
||||
String sql = "INSERT INTO contacts (secret) VALUES (?)";
|
||||
String sql = "INSERT INTO contacts (incomingSecret, outgoingSecret)"
|
||||
+ " VALUES (?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, secret);
|
||||
ps.setBytes(1, incomingSecret);
|
||||
ps.setBytes(2, outgoingSecret);
|
||||
int affected = ps.executeUpdate();
|
||||
if(affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
@@ -1643,12 +1646,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getSharedSecret(Connection txn, ContactId c)
|
||||
public byte[] getSharedSecret(Connection txn, ContactId c, boolean incoming)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT secret FROM contacts WHERE contactId = ?";
|
||||
String col = incoming ? "incomingSecret" : "outgoingSecret";
|
||||
String sql = "SELECT " + col + " FROM contacts WHERE contactId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
rs = ps.executeQuery();
|
||||
|
||||
@@ -29,7 +29,7 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
|
||||
TransportIndex i, byte[] encryptedIv, byte[] secret) {
|
||||
// Decrypt the IV
|
||||
Cipher ivCipher = crypto.getIvCipher();
|
||||
ErasableKey ivKey = crypto.deriveIncomingIvKey(secret);
|
||||
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
|
||||
byte[] iv;
|
||||
try {
|
||||
ivCipher.init(Cipher.DECRYPT_MODE, ivKey);
|
||||
@@ -57,15 +57,17 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
|
||||
private ConnectionReader createConnectionReader(InputStream in,
|
||||
boolean initiator, TransportIndex i, long connection,
|
||||
byte[] secret) {
|
||||
byte[] iv = IvEncoder.encodeIv(initiator, i, connection);
|
||||
// Derive the keys and erase the secret
|
||||
ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
|
||||
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
|
||||
for(int j = 0; j < secret.length; j++) secret[j] = 0;
|
||||
// Create the decrypter
|
||||
byte[] iv = IvEncoder.encodeIv(initiator, i, connection);
|
||||
Cipher frameCipher = crypto.getFrameCipher();
|
||||
ErasableKey frameKey = crypto.deriveIncomingFrameKey(secret);
|
||||
ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in, iv,
|
||||
frameCipher, frameKey);
|
||||
// Create the reader
|
||||
Mac mac = crypto.getMac();
|
||||
ErasableKey macKey = crypto.deriveIncomingMacKey(secret);
|
||||
return new ConnectionReaderImpl(decrypter, mac, macKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,9 @@ DatabaseListener {
|
||||
}
|
||||
|
||||
private synchronized void calculateIvs(ContactId c) throws DbException {
|
||||
ErasableKey ivKey = crypto.deriveIncomingIvKey(db.getSharedSecret(c));
|
||||
byte[] secret = db.getSharedSecret(c, true);
|
||||
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
|
||||
for(int i = 0; i < secret.length; i++) secret[i] = 0;
|
||||
for(TransportId t : localTransportIds) {
|
||||
TransportIndex i = db.getRemoteIndex(c, t);
|
||||
if(i != null) {
|
||||
@@ -131,7 +133,9 @@ DatabaseListener {
|
||||
TransportIndex i1 = ctx1.getTransportIndex();
|
||||
if(c1.equals(c) && i1.equals(i)) it.remove();
|
||||
}
|
||||
ErasableKey ivKey = crypto.deriveIncomingIvKey(db.getSharedSecret(c));
|
||||
byte[] secret = db.getSharedSecret(c, true);
|
||||
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
|
||||
for(int j = 0; j < secret.length; j++) secret[j] = 0;
|
||||
calculateIvs(c, ctx.getTransportId(), i, ivKey, w);
|
||||
} catch(NoSuchContactException e) {
|
||||
// The contact was removed - clean up when we get the event
|
||||
@@ -181,8 +185,9 @@ DatabaseListener {
|
||||
private synchronized void calculateIvs(TransportId t) throws DbException {
|
||||
for(ContactId c : db.getContacts()) {
|
||||
try {
|
||||
byte[] secret = db.getSharedSecret(c);
|
||||
ErasableKey ivKey = crypto.deriveIncomingIvKey(secret);
|
||||
byte[] secret = db.getSharedSecret(c, true);
|
||||
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
|
||||
for(int i = 0; i < secret.length; i++) secret[i] = 0;
|
||||
TransportIndex i = db.getRemoteIndex(c, t);
|
||||
if(i != null) {
|
||||
ConnectionWindow w = db.getConnectionWindow(c, i);
|
||||
|
||||
@@ -36,7 +36,7 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
|
||||
byte[] secret) {
|
||||
// Decrypt the IV
|
||||
Cipher ivCipher = crypto.getIvCipher();
|
||||
ErasableKey ivKey = crypto.deriveIncomingIvKey(secret);
|
||||
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
|
||||
byte[] iv;
|
||||
try {
|
||||
ivCipher.init(Cipher.DECRYPT_MODE, ivKey);
|
||||
@@ -60,17 +60,19 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
|
||||
private ConnectionWriter createConnectionWriter(OutputStream out,
|
||||
long capacity, boolean initiator, TransportIndex i, long connection,
|
||||
byte[] secret) {
|
||||
// Derive the keys and erase the secret
|
||||
ErasableKey ivKey = crypto.deriveIvKey(secret, initiator);
|
||||
ErasableKey frameKey = crypto.deriveFrameKey(secret, initiator);
|
||||
ErasableKey macKey = crypto.deriveMacKey(secret, initiator);
|
||||
for(int j = 0; j < secret.length; j++) secret[j] = 0;
|
||||
// Create the encrypter
|
||||
Cipher ivCipher = crypto.getIvCipher();
|
||||
Cipher frameCipher = crypto.getFrameCipher();
|
||||
ErasableKey ivKey = crypto.deriveOutgoingIvKey(secret);
|
||||
ErasableKey frameKey = crypto.deriveOutgoingFrameKey(secret);
|
||||
byte[] iv = IvEncoder.encodeIv(initiator, i, connection);
|
||||
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
|
||||
capacity, iv, ivCipher, frameCipher, ivKey, frameKey);
|
||||
// Create the writer
|
||||
Mac mac = crypto.getMac();
|
||||
ErasableKey macKey = crypto.deriveOutgoingMacKey(secret);
|
||||
return new ConnectionWriterImpl(encrypter, mac, macKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class IncomingBatchConnection {
|
||||
|
||||
void read() {
|
||||
try {
|
||||
byte[] secret = db.getSharedSecret(contactId);
|
||||
byte[] secret = db.getSharedSecret(contactId, true);
|
||||
ConnectionReader conn = connFactory.createConnectionReader(
|
||||
reader.getInputStream(), transportIndex, encryptedIv,
|
||||
secret);
|
||||
|
||||
@@ -46,7 +46,7 @@ class OutgoingBatchConnection {
|
||||
|
||||
void write() {
|
||||
try {
|
||||
byte[] secret = db.getSharedSecret(contactId);
|
||||
byte[] secret = db.getSharedSecret(contactId, false);
|
||||
long connection = db.getConnectionNumber(contactId, transportIndex);
|
||||
ConnectionWriter conn = connFactory.createConnectionWriter(
|
||||
writer.getOutputStream(), writer.getCapacity(),
|
||||
|
||||
@@ -33,7 +33,7 @@ public class IncomingStreamConnection extends StreamConnection {
|
||||
@Override
|
||||
protected ConnectionReader createConnectionReader() throws DbException,
|
||||
IOException {
|
||||
byte[] secret = db.getSharedSecret(contactId);
|
||||
byte[] secret = db.getSharedSecret(contactId, true);
|
||||
return connReaderFactory.createConnectionReader(
|
||||
connection.getInputStream(), transportIndex, encryptedIv,
|
||||
secret);
|
||||
@@ -42,7 +42,7 @@ public class IncomingStreamConnection extends StreamConnection {
|
||||
@Override
|
||||
protected ConnectionWriter createConnectionWriter() throws DbException,
|
||||
IOException {
|
||||
byte[] secret = db.getSharedSecret(contactId);
|
||||
byte[] secret = db.getSharedSecret(contactId, false);
|
||||
return connWriterFactory.createConnectionWriter(
|
||||
connection.getOutputStream(), Long.MAX_VALUE, transportIndex,
|
||||
encryptedIv, secret);
|
||||
|
||||
@@ -37,7 +37,7 @@ public class OutgoingStreamConnection extends StreamConnection {
|
||||
transportIndex);
|
||||
}
|
||||
}
|
||||
byte[] secret = db.getSharedSecret(contactId);
|
||||
byte[] secret = db.getSharedSecret(contactId, true);
|
||||
return connReaderFactory.createConnectionReader(
|
||||
connection.getInputStream(), transportIndex, connectionNum,
|
||||
secret);
|
||||
@@ -52,7 +52,7 @@ public class OutgoingStreamConnection extends StreamConnection {
|
||||
transportIndex);
|
||||
}
|
||||
}
|
||||
byte[] secret = db.getSharedSecret(contactId);
|
||||
byte[] secret = db.getSharedSecret(contactId, false);
|
||||
return connWriterFactory.createConnectionWriter(
|
||||
connection.getOutputStream(), Long.MAX_VALUE, transportIndex,
|
||||
connectionNum, secret);
|
||||
|
||||
Reference in New Issue
Block a user