Subject: Re: ABNF for the three cases of GS2

From: Chris Newman <[email-masked]>
Date: Tue, 18 Nov 2008 15:00:55 -0600

I was unaware this was stalled waiting for my response until I was told 
yesterday; apologies for not responding sooner.

I'm finding it very difficult to get my head around this at the moment due 
to the nested ABNF.  To attempt to better understand it, I'm going to 
unfold the nested references with some simplifications.  First, "gunk" is 
something I believe to be constant when sent by SASL-SCRAM implementations 
and ignored when received by SASL-SCRAM implementations (albeit included in 
a hash if necessary) -- that includes all the security layer stuff since I 
assume SASL-SCRAM implementations will always negotiate off security 
layers.  Second, I'm dropping hash-list (we agreed to move that to the mech 
name), the non-relevant syntax and extensibility for now.  Third, I'm only 
showing a data item the first time it is transferred (it's sometimes 
helpful to transfer an item more than once to simplify the code, but that's 
an optimization we need not consider at the moment).

So I think we end up with (please correct me if this is wrong):

Case 1:

C1: b64(gunk,c-nonce,username) ":" %x00
S1: b64(s-nonce,salt,iter-count) ":"
C2: b64(b64(channel-bind),b64(ClientProof))
      ":" b64(b64(<MIC-of-something?>),authzid,gunk)
S2: b64(b64(ServerSignature)) ":" b64(b64(<MIC-of-something?>),gunk)

I'm don't understand how a SASL-SCRAM implementation is supposed to 
generate the <MIC-of-something> that GSS wrap does.  Does SASL-SCRAM just 
set this to 0?  I know how the ClientProof is computed (which implicitly 
includes a MIC), I don't understand the MIC although it doesn't matter if I 
can set it to 0 and ignore it.

I don't understand why two sets of channel bindings appear to be present, 
but I'm assuming that SASL-SCRAM treats the GSS-channel-binding as gunk 
(send constant / ignore on receipt) for simplicity.  If SASL-SCRAM 
implementation has to do something else I need to understand what it's 
expected to do to understand the proposal.

I'm confused about the placement of authzid, since it needs to be included 
in the SCRAM computations.  The current SCRAM spec includes it in the first 
client message (optionally).  It's harmless moving it to the second client 
message but I don't really understand why it ends up in the second b64 blob.

I'm not fond of this approach due to the three levels of base64-encoding 
that will occur in many SASL profiles (e.g. IMAP) that use base64 as a 
wrapper for SASL exchanges.  That extra middle layer makes the interesting 
content unreadable to any debug/logging mechanism provided in the protocol 
or SASL layer -- debugging/diagnostics have to be put directly in the SCRAM 
layer after that middle layer is unwrapped.

Case 2:

C1: gunk,c-nonce,username delim
S1: s-nonce,salt,iter-count delim
C2: b64(channel-bind),b64(ClientProof) delim 
b64(<MIC-of-something?>),authzid,gunk
S2: b64(ServerSignature) delim b64(<MIC-of-something?>),gunk

Escaping is used where necessary to avoid delim.  Other than not wanting a 
NUL delim due to the negative impact it has on debugging (I'd consider ^A), 
this seems workable assuming MIC-of-something isn't needed by SASL-SCRAM.

Case 3:

C1: "ctx=" gunk,c-nonce,username
S1: "ctx=" s-nonce,salt,iter-count
C2: "mic=" b64(<MIC-of-something?>),gunk,authzid ",ctx=" 
b64(channel-bind),b64(ClientProof)
S2: "mic=" b64(<MIC-of-something?>),gunk ",ctx=" b64(ServerSignature)

I'm not sure I understand how this is different from case 2.  It seems 
there's just a different delimiter (specifically, ",ctx=") and the GSS 
stuff is moved to the beginning.  Regardless, it seems fine to me.

		- Chris

--On August 11, 2008 17:14:33 -0400 Sam Hartman <[email-masked]> 
wrote:
> I have put together ABNF for the three cases of GS2.
>
> A few notes:
>
> * I've punted on the whole OID issue at the beginning of the client
>   context token, defining a rule oidgunk that we'll need to figure
>   out.  That may involve negotiation with kitten.
>
> * I'm only providing Grammar for the parts of scram that SASL actually
>   uses.  Nico at least believes that the GSS-API mechanism should
>   provide sequencing.  I've included enough stuff that a pure SASL
>   mechanism would parse and ignore any such usage but have not
>   explored it.
>
> * There are two channel binding slots: one used by sasl in
>   scram-wrap-c2-inner and one used by non-SASL GSS-API applications in
> scram-c2-ctx
>
> * Most of the protocol is the same between all three cases.
>
> * This assumes that gs2 implementations are required to send the
>   mic/wrap token as soon as possible and that scram implementations
>   are required to be prot_ready when the client final message is sent.
>
> My current preference is a moderate preference for case 2.  I might
> like case 3 better if someone suggested a better way to determine the
> end of the non-context part.
>
> ; Some rules used by most of the grammars:
> gs2-to-be-protected = qop ["," maxbuf] ["," cbqop "," channelbinding]
>     ["," authzid] ["," 1*extension]
> qop = "qop=" qopvalue *( "+" qopvalue)
> qopvalue = "none" ; no security layer
>     / "integ" ; integrity protection
>     / "conf" ; confidentiality protection
>     / "cbgood" ; channel binding validated (server to client)
> maxbuf = "maxbuf=" digits
> cbqop = "cbqop=" qopvalue *( "+" qopvalue); QOPs that can be used if
> channel binding succeeds channelbinding = "cb=" base64
> authzid = "authzid=" base64
> extension = alphanum "=" *extensionchar
> extensionchar = value-char
> value-safe-char = %20-2B / %2D-3C / %3E-7E /
> 		  UTF8-2 / UTF-3 / UTF8-4
> 		  ;; UTF8-char except CTL, "=", and ",".
>
> value-char      = value-safe-char / "="
>
> ; The scram message parts that are constant across all three cases
> scram-c1-ctx = oidgunk username "," hash-list "," nonce
> ; oidgunk needs to be discussed on the list
> scram-s1-ctx = nonce "," hash-list "," salt "," iteration-count
> scram-c2-ctx = nonce "," gss-channel-binding "," proof
> ; gss-channel-binding is used  for non-SASL GSS-API
> ; but not for sasl apps
>
> scram-s2-ctx = verifier
>
> ; The content that gets wrapped is also constant across all three cases
> ; We don't consider it desirable that scram have a security layer.
> ; However it seems like scram is going to be enough of a gss-api mechanism
> ; that it will end up having a well-defined security layer.
> ; A SCRAM implementation with a full GSS-API  implementation MAY end up
> using  ; security layers and as such all SCRAM implementations need to
> parse qops  and ; maxbuf specifications. However pure SASL implementation
> can send a limited set  of responses ; and need not actually implement
> security layers
> ; The following rules demonstrate what will be carried between  two sasl-
> ; only scram  implementations that do not support security layers.
> ; the following two rules are the scram wrap from client to server and
> server to client saslscram-wrap-c2-inner = "qop=none" ["," "cbqop=none"
> "," channelbinding]      ["," authzid] [ "," 1*extension]
> saslscram-wrap-s2-inner = "qop=none" [ "+cbgood" ] [ "," 1*extension]
> ; and here are rules for what implementations must parse
> scram-wrap-c2-inner = qop ["," maxbuf ] ["," cbqop "," channelbinding]
> ["," authzid]      ["," 1*extension]
> ; The following rule could probably be simplified  as it's not clear
> maxbuf ; will ever appear in the s2 message unless you have two
> gs2-implementations  ; talking that have actually decided to use security
> layers
> scram-wrap-s2-inner = qop ["," maxbuf] ["," 1*extension]
>
>
> ; Grammar: case 1 two base 64 tokens
>
> gs2-message = gs2-context "," gs2-wrap
> gs2-context = base64 ; completely defined by the mechanism
> gs2-wrap = base64 ; processed by the mechanism but including the data
> from gs2-wrap-inner gs2-wrap-inner = gs2-to-be-protected
> scram-wrap = "mic="base64 ["," 1*extension] "," "d=" *octet ; MIC and
> integrity protected data ; So, that would make the SCRAM c2 message look
> like:
> scram-c2-wrap = "mic=" base64 [ "," 1*extension] ",d=" scram-wrap-c2-inner
> scram-s2-wrap = "mic=" base64 ["," 1*extension] ",d=" scram-wrap-s2-inner
> ; So, the client sends base64(scram-c1-ctx):null
> ; the server sends base64(scram-s1-ctx):nothing
> ; the client sends base64(scram-c2-ctx):base64(scram-c2-wrap)
> ; the server sends base64(scram-s2-ctx):base64(scram-s2-wrap)
>
> ; Grammar 2: Escaping
> ; null null encodes null
> ; null "," separates the context from wrap token
> ; Feel free to use a different escaping mechanism
> escape-char = %x01-ff / (%x00 %x00)
> escaped = 1*escape-char
> delim = %x00 ","
> gs2-message = gs2-ctx delim gs2-wrap
> gs2-ctx = escaped
> gs2-wrap = escaped
> ; It happens that the scram messages below conform to escaped
> scram-wrap-part = "mic=" base64 ["," 1*extension] ",d="
> scram-wrap = scram-wrap-part escaped
>
> scram-c1 = scram-c1-ctx delim
> scram-s1 = scram-s1-ctx delim
> scram-c2 = scram-c2-ctx delim scram-wrap-part scram-wrap-c2-inner
> scram-s2 = scram-s2-ctx delim scram-wrap-part scram-wrap-s2-inner
>
> ; Grammar 3: mic and base64
> ; In this case we assume that scram-mic is a fixed-length binary hmac
> followed by extra stuff  ; used by GSS-API but not SASL for sequencing
> and replay
> gs2-message = [gs2-mic gs2-stuff  ","] "ctx="gs2-ctx
> ; Note that determining where the end of the MIC is a bit tricky; it
> extends to ; but does not include the final comma in gs2-stuff. You don't
> know you are at ; the end until you read ctx
> gs2-mic = "mic=" base64 "," ; base64 of gss_getmic of gs2-stuff
> gs2-stuff = gs2-to-be-protected
> gs2-ctx = 1*octet
> scram-c1 = "ctx"= scram-c1-ctx
> scram-s1 = "ctx"= scram-s1-ctx
> scram-c2 = gs2-mic scram-wrap-c2-inner ",ctx=" scram-c2-ctx
> scram-s2 = gs2-mic scram-wrap-s2-inner ",ctx=" scram-s2-ctx
>
>