Subject: [anonsec] details of IKE/IPsec channel binding

From: mcr at sandelman.ottawa.on.ca (Michael Richardson)
Date: Sun, 01 Apr 2007 17:43:05 -0400

>>>>> "Nicolas" == Nicolas Williams <Nicolas.Williams at sun.com> writes:
    Nicolas>  - Imagine I send a SYN packet protected with some SA, say,
    Nicolas> SPI 1001, but the SYN|ACK does not arrive in time so I
    Nicolas> retransmit, only this time I use a new SA (SPI 1002, say)
    Nicolas> because, say, the old one expired.

    Nicolas>    We can get into a weird situation: the SA that _I_ see
    Nicolas> as the one used to protect the channel creation trigger is
    Nicolas> _different_ from the one seen by my peer, so we fail to
    Nicolas> agree on channel binding!

  Perhaps, in this case, it should fail.

- -- 
]            Bear: "Me, I'm just the shape of a bear."          |  firewalls  [
]   Michael Richardson,    Xelerance Corporation, Ottawa, ON    |net architect[
] mcr at xelerance.com      http://www.sandelman.ottawa.on.ca/mcr/ |device driver[
] panic("Just another Debian GNU/Linux using, kernel hacking, security guy"); [

---

From: mcr at sandelman.ottawa.on.ca (Michael Richardson)
Date: Sun, 01 Apr 2007 17:44:30 -0400

>>>>> "Nicolas" == Nicolas Williams <Nicolas.Williams at sun.com> writes:
    Nicolas> This needs to work for IKEv1.  Assuming that the IKE_SA is
    Nicolas> still around is not a good assumption.

  I have no problem with requiring that the IKE_SA remain around.  
  Seriously.  

- --

---

From: Nicolas.Williams at sun.com (Nicolas Williams)
Date: Sun, 1 Apr 2007 17:31:59 -0500

On Sun, Apr 01, 2007 at 05:43:05PM -0400, Michael Richardson wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> 

Well, it will fail.  But with the other channel binding scheme it will
succeed, as it should.

We seem to be willing to accept this pathological condition.  The only
question is: why not go with the way that doesn't have this corner case?

Possible reasons to reject public-keys-of-peers-as-channel-bindings:

 - doesn't work for IKEv2 w/ EAP authentication

   But does anyone want to do channel binding to channels where the
   peers were authenticated with EAP?

   I had a hallway conversation with Sam and Jeff H. about this but I
   came away unconvinced: the peers would have to be the client and the
   SG (since IKEv2 w/ EAP is only applicable to network access through
   IPsec VPNs), so given strong authentication to/of the SG at the IPsec
   layer why should any application on the client want to use channel
   bindings?  Perhaps the EAP method used wasn't very strong -- but then
   why use it at all?

   Still, I'm certainly open to arguments on this.

 - unique channel bindings are better

   They certainly are.  In SASL WG we saw a presentation about a
   password-based challenge/response authentication mechanism (YAP) that
   uses unique channel bindings as the server challenge and client
   nonce, thus saving a full round-trip (and since it only runs inside a
   secure channel it also provides a measure of privacy protection).

So count me as half-convinced.

Nico
--

---

From: Nicolas.Williams at sun.com (Nicolas Williams)
Date: Sun, 1 Apr 2007 17:35:34 -0500

>   I have no problem with requiring that the IKE_SA remain around.  

The channel initiator (client) can certainly keep it around.  The
server need not have any idea that it will have to keep it around.

What we could do is make the connect() fail if the client were to
retransmit a TCP SYN (or whatever) protected by a child SA of a
different IKE_SA even though for the same peer.

But how do you make that work for connect()ed UDP sockets?  Or perhaps
we should say that connect()ed UDP sockets don't have channel bindings,
use IPsec APIs instead of connected UDP socets.  I'd be willing to live
with that.

Nico
--

---

From: Nicolas.Williams at sun.com (Nicolas Williams)
Date: Sun, 1 Apr 2007 17:46:49 -0500

> server need not have any idea that it will have to keep it around.

Responding to myself: i.e., what if the server reboots or otherwise
loses IKE state?

Nico
--

---

From: julien.IETF at laposte.net (Julien Laganier)
Date: Tue, 3 Apr 2007 15:26:42 +0200

On Wednesday 21 March 2007 18:06, Nicolas Williams 
wrote:
> On Wed, Mar 21, 2007 at 05:46:03PM +0100, Michael 
Richardson wrote:
> > At lunch I was discussing the question of what the
> > IKE/IPsec channel binding blog would be.
>
> I think you meant "blob" not "blog" :)
>
> We've discussed this before and the answer is:
>
>  - the public key values of the two peers
> concatenated in this order: channel initiator ||
> channel acceptor
>
> or some similar transformation of those two values.

You might also want to do something similar to what HIP 
does:

min(pk_i, pk_r) || max(pk_i, pk_r) so that the channel 
bindings between two peers is independent from who 
initiated the IKE exchange.

--julien

---

From: julien.IETF at laposte.net (Julien Laganier)
Date: Tue, 3 Apr 2007 15:37:57 +0200

On Monday 02 April 2007 00:31, Nicolas Williams wrote:
> >     Nicolas>  - Imagine I send a SYN packet
> > protected with some SA, say, Nicolas> SPI 1001,
> > but the SYN|ACK does not arrive in time so I
> > Nicolas> retransmit, only this time I use a new SA
> > (SPI 1002, say) Nicolas> because, say, the old one
> > expired.
> >
> >     Nicolas>    We can get into a weird situation:
> > the SA that _I_ see Nicolas> as the one used to
> > protect the channel creation trigger is Nicolas>
> > _different_ from the one seen by my peer, so we
> > fail to Nicolas> agree on channel binding!
> >
> Well, it will fail.  But with the other channel
> binding scheme it will succeed, as it should.
>
> We seem to be willing to accept this pathological
> condition.  The only question is: why not go with
> the way that doesn't have this corner case?
>
> Possible reasons to reject
> public-keys-of-peers-as-channel-bindings:
>

When IKEv2 is used with EAP authentication, the channel 
binding blob could fallback from being peers' public 
keys to a PRF of the EAP MSK. Both peers know that EAP 
was used, so that doesn't even require additional 
signalling to let peers generate the appropriate blob.

--julien

---

From: mcr at sandelman.ottawa.on.ca (Michael Richardson)
Date: Tue, 03 Apr 2007 10:37:28 -0400

>>>>> "Julien" == Julien Laganier <julien.IETF at laposte.net> writes:
    >> We seem to be willing to accept this pathological condition.  The
    >> only question is: why not go with the way that doesn't have this
    >> corner case?
    >> 

    Julien> When IKEv2 is used with EAP authentication, the channel
    Julien> binding blob could fallback from being peers' public keys to
    Julien> a PRF of the EAP MSK. Both peers know that EAP was used, so
    Julien> that doesn't even require additional signalling to let peers
    Julien> generate the appropriate blob.

  This suffers from all the problems that using something derived from
the DH SKEYSEED has.  I don't see a *BTNS* case for doing EAP and then
using channel binding, but I might be wrong.  

  (I can see BTNS for PARENT_SA "authentication", and then EAP for
further authentication of the SA, but at which point, it might be
unnecessary to actually do a channel binding.  That's is in theory, but
perhaps in practice, there isn't the total awareness necessary to avoid
this.)

  I raised this question, because I felt that it needed to be resolved
in some way,  or resolved that we are comfortable with what we had
already.  The above proposal has the advantage that, like the
concatenated public keys, it can be checked with memcmp().

  This is the fundamental API issue. Is the API:

#1 memcpy suffices:

       u_char *my_channel_binding_blob;
       int     my_channel_binding_blob_len;

       u_char *his_channel_binding_blob;
       int     his_channel_binding_blob_len;

       ipsec_get_iToken_attr(iToken, CHANNEL_BINDING,
			     &my_channel_binding_blob, 
			     &my_channel_binding_blob_len);

       gssapi_somefoo_send_channel_binding(my_channel_binding_blob, 
				           my_channel_binding_blob_len);
       /* do I look like I know anything about gssapi? */

       gssapi_somefoo_recv_channel_binding(&his_channel_binding_blob, 
				           &his_channel_binding_blob_len);

       if(my_channel_binding_blob_len == his_channel_binding_blob_len &&
	  memcmp(my_channel_binding_blob, his_channel_binding_blob,
					  his_channel_binding_blob_len)==0))
       {
          return TRUE;
       } else {
          printf("Mallor has been seen!\n");
          return FALSE;
       }

#2 channel binding needs to be checked:
	   			
       u_char *my_channel_binding_blob;
       int     my_channel_binding_blob_len;

       u_char *his_channel_binding_blob;
       int     his_channel_binding_blob_len;

       ipsec_get_iToken_attr(iToken, CHANNEL_BINDING,
			     &my_channel_binding_blob, 
			     &my_channel_binding_blob_len);

       gssapi_somefoo_send_channel_binding(my_channel_binding_blob, 
				           my_channel_binding_blob_len);
       /* do I look like I know anything about gssapi? */

       gssapi_somefoo_recv_channel_binding(&his_channel_binding_blob, 
				           &his_channel_binding_blob_len);

       if(ipsec_check_channel_binding(my_channel_binding_blob, 
				      my_channel_binding_blob_len,
				      his_channel_binding_blob,
			              his_channel_binding_blob_len) {
       {
          return TRUE;
       } else {
          printf("Mallor has been seen!\n");
          return FALSE;
       }

Obviously, case #1 can be implemented inside of case #2.
Case #2 may, however, involve a round trip through the kernel to talk to
the keying deamon if the channel binding blob can not be fully
authenticated without key material that the keying daemon prefers not to
disclose.

- --

---

From: mcr at sandelman.ottawa.on.ca (Michael Richardson)
Date: Tue, 03 Apr 2007 10:39:46 -0400

>>>>> "Julien" == Julien Laganier <julien.IETF at laposte.net> writes:
    Julien> You might also want to do something similar to what HIP
    Julien> does:

    Julien> min(pk_i, pk_r) || max(pk_i, pk_r) so that the channel
    Julien> bindings between two peers is independent from who initiated
    Julien> the IKE exchange.

  I agree that perhaps this makes more sense.
  I was going to propose ordering by ordered IP addresses, but this is
perhaps more cool.  

- --

---

From: julien.IETF at laposte.net (Julien Laganier)
Date: Wed, 4 Apr 2007 17:36:08 +0200

On Tuesday 03 April 2007 16:37, Michael Richardson 
wrote:
> Julien Laganier <julien.IETF at laposte.net> writes:
>
> > When IKEv2 is used with EAP
> > authentication, the channel binding blob
> > could fallback from being peers' public keys to
> > a PRF of the EAP MSK. Both peers know that
> > EAP was used, so Julien> that doesn't even require
> > additional signalling to let peers generate
> > the appropriate blob.
>
>   This suffers from all the problems that using
> something derived from the DH SKEYSEED has.  I don't
> see a *BTNS* case for doing EAP and then using
> channel binding, but I might be wrong.

I don't see a case either. If EAP is used it means that 
both peers were authenticated at the IPsec layer (the 
initiator via EAP, the responder via a certificate,  
and possibly also via EAP mutual authentication). Why 
would someone want to authenticate "again" at the 
IPsec layer via channel binding with an ULP...

Still it might be compelling to provide a channel 
binding method anyway in case we're both wrong...

>   (I can see BTNS for PARENT_SA "authentication",
> and then EAP for further authentication of the SA,
> but at which point, it might be unnecessary to
> actually do a channel binding.  That's is in theory,
> but perhaps in practice, there isn't the total
> awareness necessary to avoid this.)
>
>   I raised this question, because I felt that it
> needed to be resolved in some way,  or resolved that
> we are comfortable with what we had already.  The
> above proposal has the advantage that, like the

Yes.

> [...]

        if(my_channel_binding_blob_len ==
           his_channel_binding_blob_len &&
           memcmp(my_channel_binding_blob,
                  his_channel_binding_blob,
                  his_channel_binding_blob_len)==0))

> [...]
> [...]
 
if(ipsec_check_channel_binding(my_channel_binding_blob, 
                          my_channel_binding_blob_len,
                          his_channel_binding_blob,  
                          his_channel_binding_blob_len) 

> Obviously, case #1 can be implemented inside of case
> #2. Case #2 may, however, involve a round trip
> through the kernel to talk to the keying deamon if
> the channel binding blob can not be fully
> authenticated without key material that the keying
> daemon prefers not to disclose.

I think it's good if we spare the additional 
userland-kernel RTT.

--julien

---

From: Nicolas.Williams at sun.com (Nicolas Williams)
Date: Wed, 4 Apr 2007 14:39:49 -0500

> signalling to let peers generate the appropriate blob.

That's true, we could do that.

We could also specify both kinds of channel binding for IPsec and let
ULPs and/or application protocols decide which to use.

For example: TCP (and any protocol like it that has a handshake at the
beginning) can recover from IKE_SA expiration by simply not
retransmitting the SYN packet protected with an SA such that the
SKEYSEED-based channel bindings would be different than for the first
SYN -- instead TCP can fail the connect() with a suitable error code or
even begin again with a new 5-tuple (diff client side ephemeral port #).

Whereas for "connected" UDP we either have to accept that SKEYSEED-based
channel bindings break across IKE_SA expiration/abandonment or we have
to accept end-point public keys as the channel bindings.

For unconnected UDP we can leave the matter to the application: if it
has a TCP-like handshake then the app can deal with SKEYSEED-based
channel bindings just like TCP could, else see above.

This could be a suitable compromise because it means we can have unique
channel bindings (SKEYSEED-based) in more places.  "Unique channel
bindings" are preferable over "end-point channel bindigs" because they
can double as nonce/challenge in application layers to obtain
authentication replay protection with fewer messages or w/o clock
synchronization + replay caches.

Nico
--

---

From: Nicolas.Williams at sun.com (Nicolas Williams)
Date: Wed, 4 Apr 2007 14:41:39 -0500

> initiated the IKE exchange.

We've long known we'd have to do something like that because
initiator/responder at the IKE layer can be the opposite of
client/server roles at the ULP or higher layers.  (TCP-like protocols
can always figure out which is which and put the two public keys in
client || server order.

---

From: Nicolas.Williams at sun.com (Nicolas Williams)
Date: Wed, 4 Apr 2007 14:42:45 -0500

>   I was going to propose ordering by ordered IP addresses, but this is

I don't believe that you'd think of ordering by IP addresses when you're
also so concerned about support for NAT!  ;)

---

From: Nicolas.Williams at sun.com (Nicolas Williams)
Date: Wed, 4 Apr 2007 14:56:17 -0500

On Tue, Apr 03, 2007 at 10:37:28AM -0400, Michael Richardson wrote:
>                       I don't see a *BTNS* case for doing EAP and then

Well, I don't see a use for channel binding to EAP-authenticated IPsec
channels (subtle difference).

That's because EAP, by its applicability, would be used in IPsec
VPN-type use cases, and so the end-points of the channel would be the
client and the SG, which have already authenticated each other, so why
authenticate again at the application layer?

> perhaps in practice, there isn't the total awareness necessary to avoid

Well, the channel binding could be done inside EAP, and this could be
used to enroll client public keys (a useful use case if you want to do
two-factor authentication where one factor is a BTNS key and it is
enrolled by one-time one-factor authentication).

> concatenated public keys, it can be checked with memcmp().

Unique and end-point channel bindings can both be checked with memcmp()
(or memcmp() of a hash/MAC of the channel bindings).

>        /* do I look like I know anything about gssapi? */

No :)

Look at RFC2743 or RFC2744 and look at the function signatures for
GSS_Init_sec_context()/GSS_Accept_sec_context().

In the GSS-API (and in SASL/GS2) channel bindings are as an input to a
black box at each peer -- both have to provide the same channel binding
octet strings, else -> failure[*].

The black box can (and does, e.g., in the case of the Kerberos V GSS
mech) cause hashes/MACs of the channel bindings octet strings to be
exchanged.

[*]  Total failure in the GSS-API case, partial failure for SASL/GS2.

Nico
--

---

From: mcr at sandelman.ottawa.on.ca (Michael Richardson)
Date: Thu, 05 Apr 2007 09:29:15 -0400

>> I was going to propose ordering by ordered IP addresses, but this
    >> is perhaps more cool.

    Nicolas> I don't believe that you'd think of ordering by IP
    Nicolas> addresses when you're also so concerned about support for
    Nicolas> NAT!  ;)

  Yes, that's why I prefer the min/max construct.

- --

---

From: mcr at sandelman.ottawa.on.ca (Michael Richardson)
Date: Thu, 05 Apr 2007 09:33:00 -0400

>>>>> "Nicolas" == Nicolas Williams <Nicolas.Williams at sun.com> writes:
    Nicolas> That's because EAP, by its applicability, would be used in
    Nicolas> IPsec VPN-type use cases, and so the end-points of the
    Nicolas> channel would be the client and the SG, which have already
    Nicolas> authenticated each other, so why authenticate again at the
    Nicolas> application layer?

  Because the application layer isn't aware of whether or not the "VPN"
is actually active or not. Take the case of a laptop doing an NFS mount
of a server (assume NFS server and VPN gateway are co-located, such as 
happens with a small office server).
  When at "home", there is no tunnel, so the application needs to
authenticate. When remote, it can exploit the IPsec tunnel that exists.

  (Ironically, if you have any hardware offload on the gateway, it would
actually be better to use the IPsec all the time, since the hardware is
likely faster, and you can, when local, probably get more use of it...)
  
    Nicolas> Well, the channel binding could be done inside EAP, and
    Nicolas> this could be used to enroll client public keys (a useful
    Nicolas> use case if you want to do two-factor authentication where
    Nicolas> one factor is a BTNS key and it is enrolled by one-time
    Nicolas> one-factor authentication).

  But, it still doesn't tell the application that it can avoid a second
layer of encryption.

    Nicolas> Look at RFC2743 or RFC2744 and look at the function
    Nicolas> signatures for
    Nicolas> GSS_Init_sec_context()/GSS_Accept_sec_context().

    Nicolas> In the GSS-API (and in SASL/GS2) channel bindings are as an
    Nicolas> input to a black box at each peer -- both have to provide
    Nicolas> the same channel binding octet strings, else -> failure[*].

    Nicolas> The black box can (and does, e.g., in the case of the
    Nicolas> Kerberos V GSS mech) cause hashes/MACs of the channel
    Nicolas> bindings octet strings to be exchanged.

  But are they compared with memcmp(), or with a function?

- --

---

From: Nicolas.Williams at sun.com (Nicolas Williams)
Date: Thu, 5 Apr 2007 10:52:24 -0500

> authenticate. When remote, it can exploit the IPsec tunnel that exists.

OK, that's a probable use case.

---

From: Nicolas.Williams at sun.com (Nicolas Williams)
Date: Thu, 5 Apr 2007 10:58:28 -0500

>   But are they compared with memcmp(), or with a function?

Something is compared, but the something isn't necessarily the raw
channel binding octet string.

It could be a hash of them, it could that the channel bindings are used
in key derivation so that an "authenticator" message is what gets
compared, etc...

In the case of SASL/GS2 the raw channel binding octet string is
memcmp()ed.  In the case of the Kerberos V GSS-API mechanism (and, I
think, the upcoming replacement for SPKM, 'PKU2U'), hash or a MAC of the
channel binding octet string is memcmp()ed.

Nico
--