-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Hi Michael,

What you're trying to do is very similar to ECIES. You should probably
use ECIES, which has received more review than your design. It's
implemented in BouncyCastle, and there was recently a thread on the
BouncyCastle mailing list about how to use it.

The cost of deriving the shared secret via ECDH will vastly outweigh
the cost of symmetric MAC and encryption operations, so there's little
point in cutting corners.

If you decide to stick with your own design, here are a couple of
comments:

On 19/03/14 16:01, Michael Powers wrote:
> (3) To ensure you can decrypt your own message, you encode one of
> the keys for yourself.  Might your private key be compromised when
> used with your own public key moreso than someone else's public
> key?

Good question - I don't know if it's safe to derive an ECDH shared
secret from a private key and the corresponding public key. An
alternative would be to save a random symmetric key alongside your
public/private key pair, and use that instead of a shared secret for
deriving the encryption and authentication keys for your own copy of
the message.

> * Takes the specified 32 bytes, appends its sha-256 digest, and
> xor * encrypts those 64 bytes with the sha-512 hash of the ECDH
> shared secret * and the entry id.

Are entry IDs supposed to be unique across all messages?

What happens if a sender accidentally reuses an entry ID? What happens
if Alice sends a message to Bob with entry ID n, and Bob sends a
message to Alice with entry ID n? The messages have the same shared
secret and entry ID, therefore the same shared hash.

The adversary can XOR the encrypted keys to get the XOR of the
unencrypted keys. Does it help the adversary in some way to know that
message 1 is encrypted with unknown key k1, message 2 is encrypted
with unknown key k2, and the XOR of k1 and k2 is x? I don't know, I'm
not a cryptanalyst. ;-) But it makes me uneasy that the adversary has
*any* knowledge about the keys.

> KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH",
> "BC"); keyAgreement.init(privateKey); 
> keyAgreement.doPhase(publicKey, true); byte[] sharedSecret =
> keyAgreement.generateSecret();

Make sure you validate the public key before using it. See SEC 1
section 3.2.2.1, and Java code here:

http://code.briarproject.org/akwizgran/briar/blob/master/briar-core/src/org/briarproject/crypto/Sec1KeyParser.java

> // generate 512 bits using shared secret and entry id MessageDigest
> sha512 = MessageDigest.getInstance("SHA-512"); 
> sha512.update(sharedSecret); 
> sha512.update(ByteBuffer.allocate(8).putLong(entryId)); byte[]
> sharedHash = sha512.digest();

This homebrew key derivation function is a reinvention of the
concatenation KDF from NIST SP 800-56A section 5.8. You should think
about why the NIST KDF includes extra inputs, such as the identities
of the parties in the key exchange and the output length, and why it's
so careful about delimiting the inputs.

> * Takes the specified 64 byte encoded input and xor decrypts it
> with the * sha-512 hash of the ECDH shared secret and the entry id.
> Then checks to * see if the last 32 bytes is the sha-256 hash of
> the first 32 bytes.

You're reinventing a MAC. Please just use a MAC.

> KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH",
> "BC"); keyAgreement.init(privateKey); 
> keyAgreement.doPhase(publicKey, true); byte[] sharedSecret =
> keyAgreement.generateSecret();

Validate the public key.

> // verify that the digest of first 32 bytes matches last 32 bytes 
> for (i = 0; i < 32; i++) { if (digest[i] != decoded[i + 32]) { //
> incorrectly decoded: we're not the intended recipient return null; 
> } }

You should use a constant-time comparison here to avoid timing
attacks. Something like:

boolean matches = true;
for (i = 0; i < 32; i++) {
        matches &= (digest[i] == decoded[i + 32]);
}
if (!matches) {
        // incorrectly decoded: we're not the intended recipient
        return null;
}
        
> int maximumOutputLength = bufferedBlockCipher 
> .getOutputSize(inputLength); byte[] output = new
> byte[maximumOutputLength];

Now that you're using PKCS padding, this is simply the output length -
no need to truncate.

But anyway this is irrelevant - use ECIES!

Cheers,
Michael

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)

iQEcBAEBCAAGBQJTKupbAAoJEBEET9GfxSfMqjEH/jtUZ4krXgyyIisNtynmQbYO
Ona2renHcbZr7ajBZgEMFGeuFK2dmH9v0aqrsTrnke3RLA2YN2gxe6pzvdJE6z6/
CoIwcPIAqe1bE2iZN/HiFrrLoamQqX2YiTRQbooCc2+FulmWJVs3tzaTFltwlS6U
MfADfNzbzPIgN1pZ3oZ75AMAzXvlTS8RF7zjiuE7WGWB0YkhMHe9BrbSL5n+7HSh
krrKAUscw1cXtlC+/g3KzR0wOtHd1Io5Y55GTJEj+X+q+enZ5AiJ+rJNHM/bwckw
EtY4vWwOu0NQjypJRpLweqeTD80lHFerqOT1nzOrXa4vnV8SnRR9mOL37UTUSAw=
=yytB
-----END PGP SIGNATURE-----
-- 
Liberationtech is public & archives are searchable on Google. Violations of 
list guidelines will get you moderated: 
https://mailman.stanford.edu/mailman/listinfo/liberationtech. Unsubscribe, 
change to digest, or change password by emailing moderator at 
compa...@stanford.edu.

Reply via email to