[OTR-dev] OTR, keyservers, MITM, etc.
chris-tuchs at hushmail.com
chris-tuchs at hushmail.com
Tue Aug 18 06:35:21 EDT 2009
Here's version 4.
The basic plan is to use multiple servers as a secondary channel
to detect MITM attacks. The kernel of the protocol is just
"Alice and Bob post the fingerprints of both their DSA keys to
public servers, check that the fingerprints match, and that
there are no conflicting claims."
In response to a message like <N, R, E, M>, a keyserver responds
with all the messages <N', R', E', M'> it received in the last
256 seconds where N' == N; and with a signature <T, Q, S>
Where
N = HMAC(KDF1(npn), npn)
R = a 168 bit nonce chosen at random
E = AESC(KDF2(npn, R), HMAC(KDF3(npn, R), kfp))
M = HMAC(KDF4(npn, R), E)
npn = user1-name || protocol-name || user2-name
where user1-name <= user2-name using a
not-yet-specified ordering function.
kfp = key1-fingerprint || key2-fingerprint
fingerprints are human readable utf8 strings
produced by otrl_privkey_hash_to_human()
KDF1(x) = KDF(x, salt)
KDF2(x,r) = HMAC(r, "Encryption key" || KFD1(x) || "KDF2")
KDF3(x,r) = HMAC(r, "Fingerprints" || KFD1(x) || "KDF3")
KDF4(x,r) = HMAC(r, "HMAC key" || KFD1(x) || "KDF4")
HMAC(k,x) = TRUNC(168, HMAC-SHA256(k, x))
AESC(k,x) = The encryption of x by AES in counter mode with
128? 256? bit key k and initial count 0
TRUNC(L,n) = the L left most bits of n
salt = SHA256(time)
time = number of seconds since 1970 UTC right shifted 7
bits expressed as a utf8 encoded string, i.e. "123"
not 0x7b. 2^7 seconds, 128 seconds, is a little
bit more than 2 minutes.
KDF(x,salt) = either PBKDF2(SHA256, x, salt, 1000, 64)
as defined in http://tools.ietf.org/html/rfc2898
or SCRYPT(???) http://www.tarsnap.com/scrypt/
|| = the concatenation of strings. "x" || "y" is "xy".
T = time
Q = a 168 bit nonce chosen at random
S = sign(HMAC(Q || R, posts || T))
posts = all the <N', R', E', M'> where N' == N posted to
the keyserver in the last 256 seconds. Expressed
as URL query strings, one per line.
The keyservers are http servers which respond to urls like
http://host.tld/otrkey?v=1&N=<b64>&R=<b64>&E=<b64>&M=<b64>
with lines, reffered to as "posts", like
N=<b64>&R=<b64>&E=<b64>&M=<b64>
N=<b64>&R=<b64>&E=<b64>&M=<b64>
N=<b64>&R=<b64>&E=<b64>&M=<b64>
and a final signature line like
T=<b64>&N=<b64>&S=<b64>
Where <b64> is a URL safe variant of a base 64 encoded number.
Base 64 encoding is the reason I picked 168 bits; it is evenly
divisible by 24, which is the base 64 "blocksize", and thus
eliminates the need for any padding algorithm.
--- possible changes
I am considering replacing E with an encryption of the actual
fingerprints or just a hash of them:
E = AESC(KDF2(npn, R), kfp) or
E = HMAC(KDF3(npn, R), kfp)
I am considering specifying that Alice makes some kind of false
posts periodically so that posts cannot be used to confirm the
existence of an OTR conversation.
--- change log / notes
Since I use time in the protocol, people who's local computer
reports a time very different from the current one will have
problems. I will make the keyservers tell what time it is in
their response and make an internal adjustment.
I changed the hashes to be dependant on a KDF, like PBKDF2 or
SCRYPT. This greatly increases the time needed to "reverse" the
name hash by trial hashing.
I replaced the single hash of the fingerprints with two hashes,
the second being a hmac of the first. I think this will make it
quick to detect "forgery" posts by agents which don't actually
know npn.
I made the clients know the public keys of the key servers to
help prevent MITM attacks on the user to keyserver channels. I
considered using ssl/https instead. I don't like this part of
the protocol.
I added R as part of the key used to encrypt E. This prevents a
kind of replay attack. In particular, if <N, R, E, M> is a
valid post, then <N, R', E, M> is very unlikely to be a valid
post unless R' == R.
Chris
Postscript: Please tell me someone has already done this better,
let me use their protocol.
More information about the OTR-dev
mailing list