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