Getting the timing right
Paul Crowley
paul at ciphergoth.org
Sun Jun 5 07:50:52 PDT 2005
An update on how I see the protocol looking with the lifespan stuff in
place, taking into account Martin Atkin's warnings about NTP. Note that
most of this would stay the same in a DSA based protocol; it's not extra
complexity resulting from the use of HMAC.
Brad, some but not all of your fields start with "openid." - when should
this be used?
** Getting a secret, no DH (once per day/week/month per consumer per
server, depending on expiries)
consumer --> idserver
openid.mode = generate_hmac_sha1_secret
idserver --> consumer
hmacsha1_secret = <secret>
secret_handle = 1117855428-random_string_19283746
secret_expiry = <expiry UTC date>
server_time = <UTC date on server>
** Alternative: Getting a secret with DH
consumer --> idserver
openid.mode = generate_hmac_sha1_secret_dh
openid.dh.p = <modulus>
openid.dh.q = <order>
openid.dh.g = <generator>
openid.dh.gx = <g ^ x mod p>
idserver --> consumer
openid.dh.gy = <g ^ y mod p>
openid.encrypted_hmacsha1_secret = SHA1(gx ^ y mod p) XOR <secret>
secret_handle = 1117855428-random_string_19283746
secret_expiry = <expiry UTC date>
secret_value = 2e47994eec0b1fa16ac62180005fa5651f5e0e14
server_time = <UTC date on server>
<secret> must be 160 random bits.
** Checking identity
consumer redirect -> UA -> idserver:
openid.mode = checkid_immediate
openid.is_identity = http://bradfitz.com/
openid.return_to = http://return.to.com
openid.secret_handle = 1117855428-random_string_19283746
idserver redirect -> UA -> consumer:
openid.mode = id_res
openid.assert_identity = http://bradfitz.com/
secret_handle = 1117855428-random_string_19283746
valid_from = <UTC date>
valid_to = <UTC date>
hmac_sha1 = HMACSHA1( secret_value(secret_handle), token_contents )
token_contents are
assert_identity
<valid_from>
<valid_to>
<assert_identity>
<return_to>
It occurs to me that newline is probably a perfectly safe character to
use to separate these fields before signing.
OK, now here's the cunning bit. When you get a secret, you must
remember not only the handle, the secret and the expiry time - you must
also note, and offset, the difference between the time I *initiated* the
request for the secret, and the server_time in the reply. I can use
this to compensate for the difference in our clocks. I'll apply this
offset when using the valid_to date in the token.
If this offset makes the valid_from date appear to be in the future at
the time I receive the token, I'll add the difference to the offset, so
it appears to be in the present.
That way a token can never outlive the valid_to date on the server, and
never live longer than the difference between the valid from and
valid_to dates.
In slapdash pseudocode:
# Get secret
now = Date()
server_secret = get_secret(server)
server_secret.offset = now - server_time
# Identify
it = get_identity_token(server, server_secret)
# A token can't live longer than its server secret
if it.expiry > server_secret.expiry:
it.expiry = server_secret.expiry
# A token can't start life in the future
now = Date()
if it.valid_from + server_secret.offset > now:
server_secret.offset -= now - it.valid_from
# When does it expire by our clocks?
it.true_expiry = it.valid_to + server_secret.offset
--
__
\/ o\ Paul Crowley, paul at ciphergoth.org
/\__/ http://www.ciphergoth.org/
More information about the yadis
mailing list