1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
---
date: 2024-11-10
title: "The other end of the authentication rabbit hole"
linkTitle: "Authentication"
author: Dimitri Staessens
---
```
I know the pieces fit
'Cause I watched them tumble down
-- Tool, Schism (2001)
```
Mariah Carey signals it almost Christmas, so it sounds like as good a
time for a blog post. Been a long time since my last one.
My plan for this year for Ouroboros was to clean up the flow allocator
code, then add authentication and clean up and fix packet loss
handling (EFCP), fix congestion avoidance, and then leave the big
missing part - the naming system - for later.
As they say, plans never survive contact with the enemy. At least I
got the clean-up of the flow allocator code done.
So how did the authentication implementation go, you ask? Or maybe
not, but I'm going to tell you anyway. Despite not having an
implementation, the journey has been quite interesting.
The idea was quite simple, actually. I've been been told ad nauseam
not to "reinvent the wheel", so my approach to authentication was
going to be as boring as one could expect: establish a flow between
the client and server, and then do some well-known public key
cryptography magic to authenticate. It's how SSL does it, nothing
wrong with that, is there?
After an initial check, I validated I could easily use OpenSSL and
X509 certificates. This authentication implementation was going to be
a walk in the park.
But here I am, at least 5 months later, and I don't have this
implemented.
There was one thing I absolutely wanted to avoid: having to configure
the certificates as part of the application. The library could of
course add some configuration/command line options to each linked
application (e.g. --certificate <cert.pem>), but that is also not to
my liking. I know this is roughly how it is with OpenSSL certificates
today, but it's a drag, and the O7s code was very emphatically hinting
that it could be done without.
But how? One way was to map programs to certificates in the IRM
configuration, and when an application starts, the IRMd will pass it
the certificate. As an example, the configuration has an entry
/usr/bin/apache -> /etc/ouroboros/apache.crt and when /usr/bin/apache
starts, it would get that certificate loaded). That would kind of have
worked, but it left another problem: what if I wanted the same
application to start twice, but with different certificates? In other
words, I'd need to map the certificate to a process, not to a program.
That kept my mind busy for a while, as there seemingly was no obvious
solution to this. I say seemingly, because the solution is as obvious
as it is simple. But it's far from the current state of affairs.
So, how could I get a process to load a certificate at runtime, but
configure it in a central location, and before the process is running?
That seems like a catch-22. First mapping the program, instantiating
the program, then configure a new mapping, starting another process
would be an error-prone mess riddled with race conditions.
The answer was simple: don't map the process, map the service
name. O7s already has the primitives to map programs and processes to
service names. And this method is actually not that far off the
current Internet approach. X509 certficates are usually tied to Domain
Names. One part of the puzzle solved using this indirection, and I can
still use standard X509 certificates, nice.
So, why don't I have this yet? Glad you asked.
Mapping certificates to names and as such indirectly tying them to
processes allows for a significantly different approach for
authentication. Why would we trust the peer with sending us his
certificate? We can get it from the name system, before establishing
any communications with the peer. Translated to the current Internet:
DNS could return the public key together with the IP address. So,
instead of first allocating a flow, and then authenticating and
deriving symmetric keys for end-to-end in-flight encryption - as is
done with TLS - we can do something a lot more secure: get the public
key of the peer from the naming system and use it to encrypt the
initial (flow allocation) handshake.
But this has another impact on my implementation plan: I need the
naming system, as the public certificate needs to be retrieved from it
for this to work.
The naming system is a component that has not been written yet. It is
a distributed application/database and it needs IPC with the IRMd.
And so this has opened another debate in my head: should I start over
with the implementation?
O7s started in 2016 with the intention to be a RINA implentation, but
as we went on we changed it quite a bit. Some core parts are a thorn
in my eye, most notably the synchronous RPC implementation. I don't
want to build the new component (name system) using that approach, and
ripping it out of the current implementation is going to be messy.
Starting over would allow using a more "modern" language than C. I've
been looking at rust, but from my initial survey, rust don't seem like
a good fit due to the lack of shared memory/robust mutex
support. Golang inherits mutexes from C pthreads, but I'm not that
fond of switching to golang over C (and it might not work that
transparently with other OS like BSD or OS X if I rely on the pthreads
and shared memory). If C is the only viable language for this thing,
ripping the guts out of the current implementation might be the best
option after all.
I've not reached a conclusion yet.
Anyway, as always: Stay Curious. And have a nice end of year.
Dimitri
|