diff --git a/components/net/sf/briar/crypto/SharedSecret.java b/components/net/sf/briar/crypto/SharedSecret.java index 4e9eb747e3df15ea548ed387764f4828028e6f08..5c144dcc56e61ffeb2baa84c11887a8384441e23 100644 --- a/components/net/sf/briar/crypto/SharedSecret.java +++ b/components/net/sf/briar/crypto/SharedSecret.java @@ -4,6 +4,16 @@ import java.util.Arrays; import javax.crypto.spec.IvParameterSpec; +/** + * An encrypted shared secret from which authentication and encryption keys can + * be derived. The encrypted secret carries an IV for encrypting and decrypting + * it and a flag indicating whether Alice's keys or Bob's keys should be + * derived from the secret. + * <p> + * When two parties agree on a shared secret, they must determine which of them + * will derive Alice's keys and which Bob's. Each party then encrypts the + * secret with an independent key and IV. + */ class SharedSecret { private static final int IV_BYTES = 16; @@ -28,15 +38,42 @@ class SharedSecret { ciphertext = Arrays.copyOfRange(secret, IV_BYTES + 1, secret.length); } + SharedSecret(IvParameterSpec iv, boolean alice, byte[] ciphertext) { + if(iv.getIV().length != IV_BYTES) throw new IllegalArgumentException(); + this.iv = iv; + this.alice = alice; + this.ciphertext = ciphertext; + } + + /** Returns the IV used for encrypting and decrypting the secret. */ IvParameterSpec getIv() { return iv; } + /** + * 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 encrypted shared secret. */ byte[] getCiphertext() { return ciphertext; } + + /** + * Returns a raw representation of the encrypted shared secret, suitable + * for storing in the database. + */ + byte[] getBytes() { + byte[] b = new byte[IV_BYTES + 1 + ciphertext.length]; + byte[] ivBytes = iv.getIV(); + assert ivBytes.length == IV_BYTES; + System.arraycopy(ivBytes, 0, b, 0, IV_BYTES); + if(alice) b[IV_BYTES] = (byte) 1; + System.arraycopy(ciphertext, 0, b, IV_BYTES + 1, ciphertext.length); + return b; + } } diff --git a/test/net/sf/briar/crypto/SharedSecretTest.java b/test/net/sf/briar/crypto/SharedSecretTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ebaa879976deaeaeac323ff78ab2968718456e45 --- /dev/null +++ b/test/net/sf/briar/crypto/SharedSecretTest.java @@ -0,0 +1,38 @@ +package net.sf.briar.crypto; + +import java.util.Arrays; +import java.util.Random; + +import junit.framework.TestCase; + +import org.junit.Test; + +public class SharedSecretTest extends TestCase { + + @Test + public void testDecodeAndEncode() { + Random random = new Random(); + byte[] secret = new byte[40]; + random.nextBytes(secret); + secret[16] = (byte) 0; + SharedSecret s = new SharedSecret(secret); + assertTrue(Arrays.equals(secret, s.getBytes())); + secret[16] = (byte) 1; + s = new SharedSecret(secret); + assertTrue(Arrays.equals(secret, s.getBytes())); + // The Alice flag must be either 0 or 1 + secret[16] = (byte) 2; + try { + s = new SharedSecret(secret); + fail(); + } catch(IllegalArgumentException expected) {} + // The secret must be at least 18 bytes long + secret = new byte[17]; + random.nextBytes(secret); + secret[16] = (byte) 0; + try { + s = new SharedSecret(secret); + fail(); + } catch(IllegalArgumentException expected) {} + } +}