shared secret alternative to DSA

Brad Fitzpatrick brad at danga.com
Fri Jun 3 21:08:23 PDT 2005


Paul,

As requested, I'm appointing you security dictator over the OpenID spec.
You've shown that you're reasonably flexible towards practicality
(accepting the phishing problems as out of scope) and you've impressed me
a lot in the past.

So, to kick-start the DSA vs. shared secret debate, let me paste here my
notes on how I'd see a shared secret approach working, based on what
you've described and LiveJournal's LJ::get_secret().  Let me know if I
have anything wrong and/or if the general strategy is right.

DEFINITIONS:

                  time ::= current time (w3c or unixtime, id-server's choice)

 latest-secret-time(t) ::= floor(t / 86400) * 86400     -- if rotating daily

         public-random ::= random string, generated once per secret asked

      server-secret(t) ::= the server-only secret generated at time t, where t exists in
                           set of all possible values of latest-secret-time.  value
                           is some random string.  if server-secret(t) doesn't already exist,
                           it's created and stored.

         secret_handle ::= <time> "-" <public-random>

                           (consumer shouldn't parse:  should be treated as opaque ASCII string
                            with bounded length of perhaps 100 characters)

  secret_value(handle) ::= HMAC_SHA1_hex( <public-random(handle)> + server-secret(latest-secret-time(<time(handle)>)) )

                           (also bounded length opaque ASCII string)


FLOW:

** Getting a secret (once per day/week/month, depending on expirys)

   consumer -> idserver:

          openid.mode=generate_secret

          (perhaps also the consumer site's trust_root, so the returned secret handle/value
           are tied to one trust_root in case the value is intercepted)

   idserver -> consumer:

          secret_handle = 1117855428-random_string_19283746
          secret_expiry = 86400
          secret_value = 2e47994eec0b1fa16ac62180005fa5651f5e0e14

** 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
      hmac_sha1 = HMACSHA1( secret_value(secret_handle) + plain )
                = HMACSHA1( "2e47994eec0b1fa16ac62180005fa5651f5e0e14" +
                            "yyyy-mm-ddThh:mm:ssZ assert_identity http://bradfitz.com/ http://return.to.com");



Is that about right?

Pros and cons:

 PRO:  easier for implentors.  SHA1 more available than DSA.  (the whole
       reason I'm even considering this instead of DSA)
  |
  +->  Counter arguments:

       -- DSA isn't /that/ hard.  We already have Perl, Java, and PHP
         implementations.

 CON:  consumers /must/ cache the secret_handle -> secret_value, at least
       for 30 seconds or so.  at least with DSA mode, a completely stateless
       design was possible and caching just helped:  it wasn't required.

 CON:  secret value goes from idserver to consumer in the clear.
  |
  +->  Counter arguments:

       -- identity server can choose to run on SSL if they care

       -- it's very difficult to sniff core internet connections,
          as opposed to ease of sniffing user-agent's connections (Wifi, Etc)

       -- Diffie-Hellman /could/ be used for secret exchange, maybe.


What pros/cons and counter arguments am I missing?

- Brad


More information about the yadis mailing list