[nanomsg] Re: end-to-end security

  • From: Alex Elsayed <eternaleye@xxxxxxxxx>
  • To: nanomsg@xxxxxxxxxxxxx
  • Date: Tue, 11 Mar 2014 15:45:29 -0700

Replies inline

Drew Crawford wrote:

> Hello folks,
> 
> I’ve written before to gauge the interest level on landing encryption
> support to nanomsg.
> 
> After my last post, I tentatively decided to go with a libzmq-based
> solution.  However, for reasons outside the scope of this list, that
> hasn’t gone as well as I’d liked, and I’m now thinking about nanomsg once
> again.
> 
> The problem is important enough that I actually have time to work on it,
> and due to time constraints I’m going to settle on some solution in the
> next few days.  The only open question at this point is whether I’m going
> to land patches in nanomsg, or whether I’m going to be doing some kind of
> private solution, like a private fork or wrap of some library.  I’d prefer
> the former if possible.
> 
> I’d like to make a concrete proposal for comment.  As far as I can tell,
> there hasn’t been further discussion on the subject of encryption since my
> last post.  Here is what I’m thinking on design decisions:
> 
> End-to-end, “well-above-transport-layer” security.  Don’t get me wrong,
> there is a good case for transport-layer security.  Zeromq has used it
> with some success.  I use it right now.  The thing is, I’ve become
> convinced it’s the wrong approach for **my** set of problems.

Alright, that's a fair enough thing to say...

> Zeromq's support gets poor when you move out of TCP transport.

...but this implies to me that you are conflating a poor implementation with 
a poor approach.

TLS works over any reliable in-order stream - if you have AF_UNIX 
SOCK_STREAM, then you have something TLS can be run over.

DTLS works over any bidirectional datagram transport (and there are ways to 
make it work for unidirectional cases) - thus you can use UDP, AF_UNIX 
SOCK_DGRAM, or AF_UNIX SOCK_SEQPACKET.

In both cases, you just need to let the TLS library know how to send the 
data. Some make this easier than others; OpenSSL in particular is sadly 
burdened with a rather poor API. GnuTLS is nicer in various ways, but uses a 
license that makes it unlikely to be the first (or possibly even _a_) choice 
for nanomsg.

> It would be a lot of work for them to support IPC, for example, which
> I’m mildly interested in. I suspect that UDP is somewhat challenging
> as well, which is a long-term goal.

If that's the case, then the issue is a poor implementation in ZMQ. Not a 
limitation of TLS/DTLS - see above.

> Doing security work near the surface means it’s
> completely decoupled from adding new transports, which is good if you want
> new transports, and also good if you want security to work with them.

Agreed, and doing security at the transport layer means it's decoupled from 
new SPs, and the same arguments apply. That's the reason I feel that _both_ 
should be implemented sooner or later.

> Patches to the cryptography require deep knowledge of zeromq internals,
> and the people with the right knowledge are often busy.

Patches to any cryptography require deep knowledge of the many pitfalls, and 
the people with the right knowledge are quite uncommon overall. It's the 
main reason that sticking with tested, well-known systems is so critical - 
changes that _seem_ small and inconsequential have time-and-again resulted 
in complete invalidation of the assumptions that the security of a system 
relies on.

> When minor features to security are needed it creates major delays.

...which, IMO, are better than minor changes to security leading to major 
losses of security.

> If security sits near the surface it requires knowledge of mostly public
> APIs and so cryptography work can proceed without scheduling meetings
> with core committers to understand the obscure internal design of the day.

"Obscure internal design of the day" was one of the problems with ZMQ that 
inspired the creation of nanomsg in the first place - it's explicitly 
designed to be componentized, such that this kind of work _isn't_ arcane 
deep magic.

> Focus on REQ/REP, and maybe DEVICE, which are the sockets I’m interested
> in.

The problem is that REQ/REP has some very hostile semantics when 
implementing encryption atop it. Incomplete list:

- Requires 0-RTT key exchange (means forward secrecy is impossible)
- Cannot assume two REQs go to the same endpoint. Thus, every single REQ 
must contain entire key exchange data (REP, however, may potentially reuse 
state in some cases. Requires study). This bloats small requests enormously.

> The other socket types can wait until somebody is sufficiently
> motivated to make security work for those socket types.

Wholly agreed there. Transport security is per-transport, SP security is 
per-SP. Because of the wide variance in semantics between SPs, it's 
incredibly unlikely they can all provide the same security guarantees, much 
less use the same protocols.

> Stick close to CurveCP where sensible, but allow for some experimentation.
>  Maybe the user can choose from several competing security mechanisms.

CurveCP will not work here. First of all, it's not 0-RTT. If you require a 
REQ/REP for key exchange before the data, your system is broken due to 
endpoint load-balancing. You need to bundle key data with your outgoing REQ, 
or at least sufficient identifiers for it to be looked up out-of-band (say, 
via the management interface). REP must do the same. The result looks more 
like OpenPGP than CurveCP - in fact, OpenPGP would work without any changes. 
Pity about the message inflation.

In addition, you have the problem of key management. If you have a load-
balanced set of REQ/REP endpoints, then do they all have the same key? If 
yes, that's a problem because you can't cut one out in the case of 
compromise etc. If no, you can't control which one the REQ gets routed to, 
and thus the sender must encrypt it to _every_ potential recipient, 
resulting in a major amplification of both compute time and message size.

I have yet to see someone suggest doing encryption over REQ/REP without 
completely ignoring the fundamental part of REQ/REP where it says that there 
are no guarantees of endpoint continuity between two REQs.

If that is ignored it's easy! It's also wrong, broken, and insecure.

<implementation details snipped>


Other related posts: