Subject: ABNF for the three cases of GS2

From: Sam Hartman <[email-masked]>
Date: Mon, 11 Aug 2008 17:14:33 -0400

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

---

From: Nicolas Williams <[email-masked]>
Date: Mon, 11 Aug 2008 16:42:00 -0500

>   out.  That may involve negotiation with kitten.

If Chris doesn't mind the RFC2743 pseudo-ASN.1 OID + length header, then
let's keep it.  Otherwise let's compress it in GS2.

>   are required to be prot_ready when the client final message is sent.

Agreed.

Nico
--

---

From: Sam Hartman <[email-masked]>
Date: Mon, 11 Aug 2008 17:47:39 -0400

>>>>> "Nicolas" == Nicolas Williams <[email-masked]> writes:

    Nicolas> On Mon, Aug 11, 2008 at 05:14:33PM -0400, Sam Hartman
    Nicolas> wrote:
    >> * 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.

    Nicolas> If Chris doesn't mind the RFC2743 pseudo-ASN.1 OID +
    Nicolas> length header, then let's keep it.  Otherwise let's
    Nicolas> compress it in GS2.

I thought Chris minded the fact that it is a non-constant string with a DER length covering the entire context token.

My preference is to update 2743 to allow the length in that encoding
to cover either the entire token or the oid as defined by the
mechanism spec.

If we cannot make that fly either because of interop problems or
consensus problems to compress in GS2.

---

From: Simon Josefsson <[email-masked]>
Date: Tue, 12 Aug 2008 12:25:19 +0200

> I have put together ABNF for the three cases of GS2.

Thanks for doing this.

> end of the non-context part.

I have a preference for case 1: while wasting 33% of the size, it uses
known encoding that are easily accessible in many languages.

I think an encoder for the (likely non-reusable) escape mechanism in
case 2 will create more complexity and will be trickier to get right.

I don't care strongly about this, but I think case 2 will look "uglier".

/Simon

---

From: Nicolas Williams <[email-masked]>
Date: Tue, 12 Aug 2008 10:35:23 -0500

On Mon, Aug 11, 2008 at 05:47:39PM -0400, Sam Hartman wrote:
> I thought Chris minded the fact that it is a non-constant string with
> a DER length covering the entire context token.
> 

!

> consensus problems to compress in GS2.

I'd rather just compress it in GS2 (on all context tokens).  The code
needed to do it pretty much necessarily exists in various GSS
implementations, and can either be made a public API or copy-pasted into
GS2 implementations.