File: README.secure

package info (click to toggle)
rpc2 2.7%2Bdebian-5
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 2,852 kB
  • ctags: 2,661
  • sloc: ansic: 19,928; sh: 9,110; lex: 437; yacc: 416; makefile: 126; asm: 35
file content (335 lines) | stat: -rw-r--r-- 13,774 bytes parent folder | download | duplicates (2)
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
Secure Framework
================

This layer provides wrappers around sendto(2) and recvfrom(2), the
secure versions expect an additional 'struct security_association *sa'.

The security association describes the encryption/authentication and
decryption/validation functions that are used to provide confidentiality
and integrity for sent messages.

The implementation heavily draws on the following IPsec related RFCs,

    RFC 2401 -- Security Architecture for the Internet Protocol
    RFC 2406 -- IP Encapsulating Security Payload (ESP)
    RFC 3948 -- UDP Encapsulation of IPsec ESP Packets

The main difference is that in our case the security layer does not work
between a pair of hosts, but assists the application to create secure
tunnels between (logical) application level endpoints. Unlike IPsec, our
payload does not include the TCP or UDP, and IP headers.

Similar to IPsec, this layer does not implement any key exchange and
setup, that step is left up to the application.

Here is a short description of the important structures and functions,

    struct security_association {
    /* incoming packets */

	/* identifier to match an incoming packet with the the correct
	 * logical connection. Really only here for the convenience of
	 * the application, it is not used by secure_recvfrom.
	 * Security identifiers < 256 are considered 'reserved', see
	 * secure_sendto/secure_recvfrom */
	uint32_t recv_spi;

	/* The following are used for to detect replay attacks and
	 * should be initialized to 0 */
	uint32_t recv_seq;
	unsigned long recv_win;

	/* function descriptor and state for packet validation */
	const struct secure_auth *validate;
	void *validate_context;

	/* function descriptor and state for decryption */
	const struct secure_encr *decrypt;
	void *decrypt_context;

    /* outgoing packets */
	/* remote connection identifier */
	uint32_t peer_spi;

	/* sequence number used for outgoing packets, should be
	 * initialized to 0 */
	uint32_t peer_seq;

	/* trusted address of the peer, outgoing encrypted packets will
	 * be sent to this address, incoming packets that are correctly
	 * validated will update this address */
	struct sockaddr_storage peer;
	socklen_t peerlen;

	/* initialization vector/counter, should be initialized to a
	 * random value, secure_sendto will properly increment it */
	uint8_t send_iv[MAXIVLEN];

	/* function descriptor and context for encryption */
	const struct secure_encr *encrypt;
	void *encrypt_context;

	/* function descriptor and context for packet authentication */
	const struct secure_auth *authenticate;
	void *authenticate_context;
    };

This structure contains all the information required to securely send
and receive encrypted and authenticated packets.


    ssize_t secure_recvfrom(int s, void *buf, size_t len, int flags,
			    struct sockaddr *peer, socklen_t *peerlen,
			    struct security_association **sa,
			    struct security_association *(*GETSA)(uint32_t spi))

Wrapper around recvfrom to decrypt/validate incoming packets. If the
packet is smaller than 8 bytes, or the first big-endian 32-bit word is
less than 256, the packet is considered unencrypted and passed through
without any futher processing.

Otherwise, the GETSA callback is called with the 32-bit identifier (in
native byte order). If GETSA returns NULL, the packet is dropped and we
return EAGAIN which is (in Linux) identical to a received packet with a
bad UDP checksum.

If GETSA did return a valid security association,
   - the sequence number is checked (anti-replay)
   - we validate the message checksum
   - update the receive window and sa->peer
   - decrypt the packet
   - check the padding

If any of these steps fail, the packet is dropped and EAGAIN is
returned. 'peer' is set whenever the packet is received, but sa->peer
is only updated when the packet has been successfully validated.


    ssize_t secure_sendto(int s, const void *buf, size_t len, int flags,
			  const struct sockaddr *to, socklen_t tolen,
			  struct security_association *sa)

Send a packet securely. If sa is NULL or does not have an encryption and
authentication function defined, and the first 32-bit big-endian word in
buf is less than 256 then the data in 'buf' is sent as-is to the address
specified by the 'to' argument. The check if the value is less than 256
is to make sure the packet does not get interpreted by the receiver side
as a valid encrypted packet.

If we do have a valid security association, the payload is padded,
encrypted and authenticated. The result is sent to the address in
sa->peer (i.e. NOT to the 'to' address).

Implemented authentication and encryption modes
===============================================

The framework is pretty flexible and should be able to support many
encryption and authentication algorithms, however the provided ones are
all based on the AES block cipher.

AES-XCBC-MAC-96 authentication
==============================

    RFC 3566 -- The AES-XCBC-MAC-96 Algorithm and Its Use With IPsec

AES-CBC based message integrity checksum, requires a 128-bit (16-byte)
key and adds 12 checksum bytes to the packet.


AES-CBC encryption
==================

    NIST Special Publication 800-38A -- Recommendation for Block Cipher
					Modes of Operation
    RFC 3602 -- The AES-CBC Cipher Algorithm and Its Use with IPsec

This is a pretty straightforward and well understood cipher block
chaining mode using AES as the encryption algorithm. CBC encryption
requires the initialization vector to be the same size as the encryption
block (which is 16-bytes for AES) The initialization vector has to be
unpredictable, so we encrypt the IV-counter that was set up by the
generic code in secure_sendto to obtain a pseudo random sequence.

Can be used with 128, 192, and 256 bit keys (16, 24, and 32 bytes).


AES-CCM encryption
==================

    NIST Special Publication 800-38C -- Recommendation for Block Cipher
					Modes of Operation: The CCM Mode for
					Authentication and Confidentiality
    RFC 4309 -- Using Advanced Encryption Standard (AES) CCM Mode with
		IPsec Encapsulating Security Payload (ESP)

This is a combined encryption and authentication algorithm, so we do not
need a separate authentication function. It also only needs 8 bytes for
the initialization vector. The checksum can be either 8, 12, or 16
bytes. As a result this algorithm has a lower per packet overhead, only
between 16 and 24 bytes instead of the 28 bytes we need for AES-CBC with
AES-XCBC-MAC-96.

We also need less key material because we do not need a separate key for
the message authentication algorithm. Finally, this algorithm lends
itself well for several optimizations, it only uses the AES encrypt
operation, initializing the encryption stream can be done off-line
by the sender before the packet has be be sent and it is highly
parallelizable.

This encryption mode uses 152, 206, or 280-bits of key material
(19, 27, or 35 bytes).

Although there are several advantages, there is one pretty significant
disadvantage. When a combination of the same key and initialization
vector is reused at any time it becomes a trivial operation to obtain
the plaintext of both messages. As such it is not recommended to use
this encryption mode when we have static keys, such as during the
initial handshake (user passwords, the keys in a Coda token, etc).


AES implementation
==================

The used AES implementation is the Rijndael reference implementation (v3.0).
I picked this because it seems to be fairly portable ANSI-C and we do not have
to deal with trying to teach automake/autoconf about various platform specific
assembly implementations and they pose no licensing conflicts wrt. to RPC2's
LGPL license.

As an alternative there is also the very small implementation by Mike Scott,
however that code relies on global variables for the encryption/decryption
state, only supports in-place operations and uses fairly generic naming. So it
could use a bit of cleaning up so that the context can be passed avoid
name-clashes.

To make it simply to replace the AES implementation, the remaining code
expects to be able to include "aes.h", which defines 5 functions to
initialize, setup keys and to encrypt/decrypt a single block.

There are several alternative implementation that can be used,

- A more optimized implementation by Dr. Brian Gladman, his code is dual
  licensed as BSD with an advertising clause, or alternatively pure GPL.
  Neither of these mix well with RPC2's LGPL license so we probably can't
  distribute binaries that are linked using his code. If you really need the
  extra performance and make sure you comply with his license terms (as this
  code is LGPL that would be the BSD license + advertising clause) and provide
  the required copyright notice and disclaimer in any documentation and/or
  associated materials that you distribute, you can find his version at,

	http://fp.gladman.plus.com/AES/index.htm

- There is also a modified version of the rijndael v3.0 reference code
  available as part of the wpa_supplicant sources. It can optionally use
  smaller tables which make the code 8KB smaller, and possibly make the code
  less vulnerable to timing attacks, however it only supports 128-bit keys.
  The modification are by Jouni Malinen and it seems to be dual licensed as
  BSD without advertising clause or GPL,

	http://hostap.epitest.fi/wpa_supplicant/

- There are more, but from what I've seen most implementations are based on,

  * original NIST submission (aka. rijndael v2.2 reference implementation),
  * The optimized/cleaned-up Rijndael v3.0 reference implementation,
  * Mike Scott's code, when the requirements tend to favour small size,
  * Brian Gladman's code, when the requirements are mostly performance.

- If you feel brave you can implement your own, the following is an excellent
  article that explains a lot of the implementations details,

	http://msdn.microsoft.com/msdnmag/issues/03/11/AES/


AES testvectors
===============

testvectors.h contains several AES testvectors from

    http://csrc.nist.gov/CryptoToolkit/aes/rijndael/rijndael-vals.zip

If you want to regenerate or expand the number of tests that are run
during initialization, unzip the testvalues in a subdirectory named
'testvalues' and run gen_testvectors.sh to rebuild. At the top of the
script are some comments and possible settings to vary the memory
overhead/execution time to run these tests.

We run the complete set of included test vectors during every startup.
It only adds a delay of about 0.43 seconds on a 600MHz PIII, and less
than 0.07 seconds on a 3.2GHz P4. But the delay does add a tiny amount
of non-deterministic entropy for the PRNG initialization.


PRNG implementation
===================

A deterministic pseudo random number generator based on ANSI X9.31, with
the NIST recommended usage for using AES as a mixing function. The
algorithm is fairly close to CBC mode encryption.

There is a 16-byte pool of random data that we use as the IV. Then when
we want to get random data we generate an initial seed based on the
current timestamp, some uninitialized data from the stack, and a counter.

This block is then encrypted using AES-CBC where the pool is used as the
IV. This results in a block of 16-bytes of random data. The random block
is then xor-ed with the original seed to get the next block of seed
data. We then refresh the pool of random data by encrypting the seed
block. These steps are repeated until we've returned the number of
random bytes that were requested.

To initialize the pool of random data and the AES128 encryption key, we
get the current timestamp, and read random data from /dev/random (or
/dev/urandom). When /dev/random is unavailable we fall back on several
lower entropy sources such as times(), getpid(), and libc's random().

The first block of random data is discarded, and we run a couple of
statistical tests to see if the resulting random data actually looks
reasonable. Passing these tests does not guarantee that the generated
random numbers are cryptographically strong, but it should detect
serious breakage.


RPC2 secure handshake
=====================

The modified RPC2 handshake is based on the analysis and proposed
implementation of the Andrew Secure RPC Handshake in 'A Logic of
Authentication', by Michael Burrows, Martin Abadi, and Roger Needham.

The handshake uses 4 steps so set up separate server->client and
client->server encryption and authentication keys.

 1. client -> server: Na, A

    this is a normal RPC2 INIT1 packet, but with the RPC2SEC_CAPABLE
    flag set in a header field. Na is a nonce which is used to avoid
    replay attacks. A is the client identifier, it can be a username
    or an encrypted Coda token.

 2. server -> client: {Na, K'ab}Kab

    The server sends back the nonce and a random key which will be used
    for client -> server traffic encrypted with the shared secret that
    was obtained from the client identifier. The client knows this is in
    response to it's INIT1 packet because of the value of the nonce.

    This packet is encrypted with AES-CBC and authenticated with
    AES-XCBC-MAC-96

 3. client -> server: {Na, K'ba}K'ab

    The client responds with the nonce and a random key that will be
    used for any further server -> client traffic, this is encrypted and
    authenticated with the random session key we received in step 2.

    The packet is encrypted with the encryption algorithm that server
    chose and sent in step 2 along with the key. Currently this is
    AES-CCM8.

 4. server -> client: {Na, Nb}K'ba

    The server sends back the nonce and an initial sequence number (Nb)
    encrypted with the session key and algorithm it received from the
    client in step 3.