Subject: comments on draft-leach-digest-sasl-01.txt

From: RL Bob Morgan <[email-masked]>
Date: Mon, 22 Feb 1999 00:17:39 -0800 (PST)

I apologize for submitting these comments long after the closing date in
the Last Call announcement on this document.  I hope that there is still
an opportunity to consider the issues I raise here, since in my opinion
this specification in its current state is not ready for Proposed Standard
status.  The digest security mechanism is intended to have wide use in
many application protocols, so it's important to get it right.

Thanks,

 - RL "Bob" Morgan
   Stanford

---

Here are a number of comments on draft-leach-digest-sasl-01.txt.  I
have divided my comments into three sections: Digest as a SASL
mechanism, the Digest protocol itself, and wording issues.  Each issue
is numbered.

Comments on Digest as a SASL mechanism:

(1) RFC 2222 specifies the transmission by the client of an
authorization identity that may be different from the identity in the
client's authentication credentials.  Digest is deficient in not
supporting this feature.  To fix this the client response format (in
section 2.1.2) must be modified; I suggest:

  digest-response  = 1#( username | realm | authz-id | nonce | cnonce |
                       nonce-count | qop | digest-uri | response |
                       maxbuf | charset | cipher | auth-param )

where

    authz-id         = "authz-id" "=" <"> authz-id-value <">
    authz-id-value   = qdstr-val

and

  authz-id
    The client's requested authorization identity, encoded as UTF-8.
    This is optional; it is only included if the client wishes to
    assert an authorization identity different from the username.

Further, the response-value (in section 2.1.2.1) must be modified to
include this value (so as to protect it from undetected modification):

   A1       = { H( { username-value, ":", realm-value, ":", 
                         authz-id-value, ":", passwd } ),
                  ":", nonce-value, ":", cnonce-value }

Comments on technical aspects of Digest itself:

(2)  The digest-uri-value introduces a 3-part service principal name.
This is an interesting and probably useful scheme but IMHO needs much
more discussion and refinement.  There are several issues.

(2.1) "serv-type" needs more rigorous definition.  I suggest that it
be precisely the SASL/GSS "service name" as described in point 1 of
section 4 of RFC 2222.

(2.2) "host" probably should be DNS host name or IP address.

(2.3) "serv-name" is the tricky part.  ("serv-type" and "host"
together are just the same as the GSS host-based service name, ie a
more or less standard way of naming service principals.)  The reason
to include the "serv-name" component is, as I understand it, that the
DNS name that the client uses to initiate its access to the service
may not be the canonical name of the host; that is, the DNS name
resolution process may involve several lookups using CNAME or SRV or
MX record chains.  This is a security issue when the DNS service used
by the client is not secure (ie, virtually always in today's
Internet); an attacker could cause the client to communicate securely
with an impostor host.  The presence of the "serv-name" component is
intended to protect against this kind of attack.

For this mechanism to do its job the server must check that the "host"
part is its canonical host name (or IP address), and must check that
the "serv-name" part is an acceptable alias for itself.  This check
needs to be written into the spec (if the serv-name feature is to be
retained).

The definition of serv-name in the draft is:

  Indicates the name of the service if it is replicated.

but whether or not the service is replicated is not something that the
client can determine directly.  All the client can know is that its
DNS resolution process started with one name and ended up with
another.  The rule for whether the client includes the serv-name
component in the digest-uri has to be written in these terms.  And
this to me is the biggest problem with this concept.  I don't know how
to write this rule so it is unambiguous.  It could be something like:

  If the final part of the client's service-location process involves
  resolution using standard (i.e., insecure) DNS lookup operations,
  and these operations involve DNS records such as CNAME, SRV, or MX
  that resolve one DNS name into another, the initial name used by the
  client is the "serv-name", and the final name is the "host"
  component.

Presumably it is necessary to exclude any non-DNS operations (and
names) from this process, since a name used earlier in the process
("Bob's Mailbox Server", "XYZ Search Engine", etc) that the client
might resolve via non-DNS lookups, is not appropriate to use as a
serv-name component.

I fear that this attempt to close a modest security hole will require
a more complete, rigorous model to be developed for how clients find
and name services (a model that does not exist now, to my knowledge,
except in some non-standards-track drafts) to give any hope of clients
and servers agreeing in general on the contents of the proposed
serv-name component.  Perhaps I have missed something here, and the
document authors can provide a rule that is unambiguous and simple.  I
think in practice client authors that want to increase the likelihood
of interoperability will just never include the serv-name part; and
server authors similarly will not bother to check it.

(3) Having an adequate nonce/cnonce is, it seems to me, a necessity
for the security of the protocol.  "nonce-value" is specified as
"qdstr-val", which can be of any length, including zero.  It seems
reasonable that, for example, a receiver of a nonce/cnonce would be
justified in aborting the authentication if the nonce/cnonce were
null, or only a single byte.  Shouldn't there a specified minimum
size?  Are there other characteristics that can be mandated or at
least recommended to ensure nonce quality?  Something needs to be
said.

(4) The MAC operations in sections 2.3 and 2.4 use only the first 8
bytes, ie 64 bits, of the HMAC output.  RFC 2104 on HMAC, in section 5
on Truncated Output, says:

   We recommend that
   the output length t be not less than half the length of the hash
   output (to match the birthday attack bound) and not less than 80 bits
   (a suitable lower bound on the number of bits that need to be
   predicted by an attacker).

The use of only 64 bits in the Digest MAC violates this
recommendation.  Is there a reason not to use all 128 bits of MD5 output?

(5) Section 2.3 defines Kic and Kis, which presumably stand for "key
for integrity, client", and "key for integrity, server".  It says:

  The key for integrity protecting messages from client to server is:

  Kic = MD5(H(A1),
      "Digest session key to client-to-server signing key magic constant")

  The key for integrity protecting messages from client to server is:

  Kis = MD5(H(A1),
      "Digest session key to server-to-client signing key magic constant")

The second sentence above is a typo, and should say:

  The key for integrity protecting messages from server to client is:

(6) 2.4 defines Kcc and Kcs, which presumably stand for "key for
confidentiality, client" and "key for confidentiality, server".  The
text below, explaining the SEAL and CMAC constructions, says:

  where CIPHER is the chosen cipher, Ki and Ke are Kic and Kec for
  messages sent by the client and Kis and Kes for those sent by the
  server

I think this is a typo, and it means to say:

  where CIPHER is the chosen cipher, Ki and Ke are Kic and Kcc for
  messages sent by the client and Kis and Kcs for those sent by the
  server.

(7) The MAC and CMAC constructions include "a 4-byte version number
with value 1".  Is this the version number of Digest-MD5?  If it's
important to have a version number, shouldn't this version number be
included in the digest-challenge and digest-response messages too?
What does an implementation do if the version number isn't 1?  Is this
version number necessary?

(8) Sections 2.1.2.1 and 2.1.3 specify values for factor "A2" when qop
is "auth" and "auth-int" but not "auth-conf".  Is this an omission or
is there some reason the auth-conf case is not dealt with here?

(9) If a data stream is protected by auth-int or auth-conf, do the
messages exchanged during a "subsequent authentication"
use that protection or not?

(10) In subsequent auth section 2.2.2, the sense of "stale" seems
reversed: shouldn't "stale=true" mean that the password is stale and
should be reacquired from the user?  More seriously, this directive
assumes an interactive user; what should a non-interactive client do
with this directive?

(11) In section 2.4, the SEAL operation specifies that the message and
MAC are separately encrypted.  Every data protection scheme I'm
familiar with simply encrypts the entire MAC-protected message.  Why
does this SEAL operation do this odd separate encryption?

(12) Is specification of error indications to be left up to the
application protocol?  There are any number of Digest-specific errors:
no nonce, no username, etc; it seems like specifying these so they'd
be consistent, and so apps wouldn't have to reinvent them, would be
appropriate.  Other security schemes do.

(13) In section 2.1.2.1 on the response-value, the value A1 is defined
as:

   A1       = { H( { username-value, ":", realm-value, ":", passwd } ),
                  ":", nonce-value, ":", cnonce-value }

where H(s) is "the 16 octet MD5 hash of the string s".  This
construction calls for concatenation of octets output by the hash
function with character strings.  Is this what is intended?  All the
other concatenations in the spec are between text strings.  Is this
instead supposed to be:

   A1       = { HEX(H( { username-value, ":", realm-value, ":", passwd } )),
                  ":", nonce-value, ":", cnonce-value }

?

Comments on the wording of the draft:

(14) Section 2.2.1 on subsequent auth says:

  The client uses the values from the previous authentication and sends an
  initial response with a string formatted and computed according to the
  rules for a "digest-response", as defined above, but with a nonce-count
                                                              ^^^^^^^^^^^
  one greater than used in the last "digest-response".

I suggest that it should say "with a nc-value one greater", which
besides being correct with the BNF would make it clear that this is
the value used in the response-value computation.  It took me a while
to figure out that the response-value would change with each
subsequent auth.

(15) The use of separate KD(), H(), MD5() constructions seems much more
confusing than enlightening.  Section 1.1 says:

  Let H(s) be the 16 octet MD5 hash of the string s.

In section 2.3 a MD5() constuction is defined, "where MD5 is as
specified in [RFC 1321]."  RFC 1321 says that MD5 operates on a b-bit
message and produces a 128-bit output.  Is there any difference
between H(s) and MD5(s)?

Section 1.1 defines KD(k, s) as

  the 16 octet MD5 hash of the concatenation of the string k, ":" 
  (a 1 character long string consisting of a colon), and the string s.

KD is used once, in a construction that contains lots of other
embedded colons.  Surely:

   response-value  =
      HEX( KD ( HEX(H(A1)),
               { nonce-value, ":" nc-value, ":",
                 cnonce-value, ":", qop-value, ":", HEX(H(A2))}))

would be clearer as:

   response-value  =
      HEX( H ( HEX(H(A1)), ":",
                 nonce-value, ":" nc-value, ":",
                 cnonce-value, ":", qop-value, ":", HEX(H(A2)) ) )

(16) Section 2.4 doesn't refer to the qop=auth-conf parameter; it
should, for clarity.

(17) What is it about Digest that makes it suitable for using a third-party
authentication server?  This feature is mentioned in the introduction;
a few words saying why this is so would help.