1/*
2 * Copyright (c) 2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <sys/types.h>
30#include <sys/malloc.h>
31
32#include <kern/locks.h>
33
34#include <libkern/crypto/sha1.h>
35
36#include <net/if.h>
37
38#include <netinet/in.h>
39#include <netinet6/in6_var.h>
40#include <netinet/ip6.h>
41#include <netinet6/ip6_var.h>
42#include <netinet6/nd6.h>
43
44#define IN6_CGA_HASH1_LENGTH	8
45#define IN6_CGA_HASH2_LENGTH	14
46#define IN6_CGA_PREPARE_ZEROES	9
47
48struct in6_cga_hash1 {
49	u_int8_t octets[IN6_CGA_HASH1_LENGTH];
50};
51
52struct in6_cga_hash2 {
53	u_int8_t octets[IN6_CGA_HASH2_LENGTH];
54};
55
56struct in6_cga_singleton {
57	boolean_t cga_initialized;
58	decl_lck_mtx_data(, cga_mutex);
59	struct in6_cga_prepare cga_prepare;
60	struct iovec cga_pubkey;
61	struct iovec cga_privkey;
62};
63
64static struct in6_cga_singleton in6_cga = {
65	.cga_initialized = FALSE,
66	.cga_mutex = {},
67	.cga_prepare = {
68		.cga_modifier = {},
69		.cga_security_level = 0,
70	},
71	.cga_pubkey = {
72		.iov_base = NULL,
73		.iov_len = 0,
74	},
75	.cga_privkey = {
76		.iov_base = NULL,
77		.iov_len = 0,
78	},
79};
80
81static void
82in6_cga_node_lock_assert(int owned)
83{
84	VERIFY(in6_cga.cga_initialized);
85	lck_mtx_assert(&in6_cga.cga_mutex, owned);
86}
87
88static boolean_t
89in6_cga_is_prepare_valid(const struct in6_cga_prepare *prepare,
90    const struct iovec *pubkey)
91{
92	static const u_int8_t zeroes[IN6_CGA_PREPARE_ZEROES] = { };
93	SHA1_CTX ctx;
94	u_int8_t sha1[SHA1_RESULTLEN];
95	u_int i, n;
96
97	VERIFY(prepare != NULL);
98	VERIFY(pubkey != NULL && pubkey->iov_base != NULL);
99
100	if (prepare->cga_security_level == 0)
101		return (TRUE);
102
103	if (prepare->cga_security_level > 7)
104		return (FALSE);
105
106	SHA1Init(&ctx);
107	SHA1Update(&ctx, &prepare->cga_modifier.octets,
108	    IN6_CGA_MODIFIER_LENGTH);
109	SHA1Update(&ctx, &zeroes, IN6_CGA_PREPARE_ZEROES);
110	SHA1Update(&ctx, pubkey->iov_base, pubkey->iov_len);
111	/* FUTURE: extension fields */
112	SHA1Final(sha1, &ctx);
113
114	n = 2 * (u_int) prepare->cga_security_level;
115	VERIFY(n < SHA1_RESULTLEN);
116	for (i = 0; i < n; ++i)
117		if (sha1[i] != 0)
118			return (FALSE);
119
120	return (TRUE);
121}
122
123static void
124in6_cga_generate_iid(const struct in6_cga_prepare *prepare,
125    const struct iovec *pubkey, u_int8_t collisions, struct in6_addr *in6)
126{
127	SHA1_CTX ctx;
128	u_int8_t sha1[SHA1_RESULTLEN];
129
130	VERIFY(prepare != NULL);
131	VERIFY(prepare->cga_security_level < 8);
132	VERIFY(pubkey != NULL && pubkey->iov_base != NULL);
133	VERIFY(in6 != NULL);
134
135	SHA1Init(&ctx);
136	SHA1Update(&ctx, &prepare->cga_modifier.octets, 16);
137	SHA1Update(&ctx, in6->s6_addr, 8);
138	SHA1Update(&ctx, &collisions, 1);
139	SHA1Update(&ctx, pubkey->iov_base, pubkey->iov_len);
140	/* FUTURE: extension fields */
141	SHA1Final(sha1, &ctx);
142
143	in6->s6_addr8[8] =
144	    (prepare->cga_security_level << 5) | (sha1[0] & 0x1c);
145	in6->s6_addr8[9] = sha1[1];
146	in6->s6_addr8[10] = sha1[2];
147	in6->s6_addr8[11] = sha1[3];
148	in6->s6_addr8[12] = sha1[4];
149	in6->s6_addr8[13] = sha1[5];
150	in6->s6_addr8[14] = sha1[6];
151	in6->s6_addr8[15] = sha1[7];
152}
153
154void
155in6_cga_init(void)
156{
157	lck_mtx_init(&in6_cga.cga_mutex, ifa_mtx_grp, ifa_mtx_attr);
158	in6_cga.cga_initialized = TRUE;
159}
160
161void
162in6_cga_node_lock(void)
163{
164	VERIFY(in6_cga.cga_initialized);
165	lck_mtx_lock(&in6_cga.cga_mutex);
166}
167
168void
169in6_cga_node_unlock(void)
170{
171	VERIFY(in6_cga.cga_initialized);
172	lck_mtx_unlock(&in6_cga.cga_mutex);
173}
174
175void
176in6_cga_query(struct in6_cga_nodecfg *cfg)
177{
178	VERIFY(cfg != NULL);
179	in6_cga_node_lock_assert(LCK_MTX_ASSERT_OWNED);
180
181	cfg->cga_pubkey = in6_cga.cga_pubkey;
182	cfg->cga_prepare = in6_cga.cga_prepare;
183}
184
185int
186in6_cga_start(const struct in6_cga_nodecfg *cfg)
187{
188	struct iovec privkey, pubkey;
189	const struct in6_cga_prepare *prepare;
190	caddr_t pubkeycopy, privkeycopy;
191
192	VERIFY(cfg != NULL);
193	in6_cga_node_lock_assert(LCK_MTX_ASSERT_OWNED);
194
195	privkey = cfg->cga_privkey;
196	if (privkey.iov_base == NULL || privkey.iov_len == 0 ||
197	    privkey.iov_len >= IN6_CGA_KEY_MAXSIZE)
198		return (EINVAL);
199	pubkey = cfg->cga_pubkey;
200	if (pubkey.iov_base == NULL || pubkey.iov_len == 0 ||
201	    pubkey.iov_len >= IN6_CGA_KEY_MAXSIZE)
202		return (EINVAL);
203	prepare = &cfg->cga_prepare;
204
205	if (!in6_cga_is_prepare_valid(prepare, &pubkey))
206		return (EINVAL);
207
208	in6_cga.cga_prepare = *prepare;
209
210	MALLOC(privkeycopy, caddr_t, privkey.iov_len, M_IP6CGA, M_WAITOK);
211	if (privkeycopy == NULL)
212		return (ENOMEM);
213
214	MALLOC(pubkeycopy, caddr_t, pubkey.iov_len, M_IP6CGA, M_WAITOK);
215	if (pubkeycopy == NULL) {
216		if (privkeycopy != NULL)
217			FREE(privkeycopy, M_IP6CGA);
218		return (ENOMEM);
219	}
220
221	bcopy(privkey.iov_base, privkeycopy, privkey.iov_len);
222	privkey.iov_base = privkeycopy;
223	if (in6_cga.cga_privkey.iov_base != NULL)
224		FREE(in6_cga.cga_privkey.iov_base, M_IP6CGA);
225	in6_cga.cga_privkey = privkey;
226
227	bcopy(pubkey.iov_base, pubkeycopy, pubkey.iov_len);
228	pubkey.iov_base = pubkeycopy;
229	if (in6_cga.cga_pubkey.iov_base != NULL)
230		FREE(in6_cga.cga_pubkey.iov_base, M_IP6CGA);
231	in6_cga.cga_pubkey = pubkey;
232
233	return (0);
234}
235
236int
237in6_cga_stop(void)
238{
239	in6_cga_node_lock_assert(LCK_MTX_ASSERT_OWNED);
240
241	if (in6_cga.cga_privkey.iov_base != NULL) {
242		FREE(in6_cga.cga_privkey.iov_base, M_IP6CGA);
243		in6_cga.cga_privkey.iov_base = NULL;
244		in6_cga.cga_privkey.iov_len = 0;
245	}
246
247	if (in6_cga.cga_pubkey.iov_base != NULL) {
248		FREE(in6_cga.cga_pubkey.iov_base, M_IP6CGA);
249		in6_cga.cga_pubkey.iov_base = NULL;
250		in6_cga.cga_pubkey.iov_len = 0;
251	}
252
253	return (0);
254}
255
256ssize_t
257in6_cga_parameters_prepare(void *output, size_t max,
258    const struct in6_addr *prefix, u_int8_t collisions,
259    const struct in6_cga_modifier *modifier)
260{
261	caddr_t cursor;
262
263	in6_cga_node_lock_assert(LCK_MTX_ASSERT_OWNED);
264
265	if (in6_cga.cga_pubkey.iov_len == 0) {
266		/* No public key */
267		return (EINVAL);
268	}
269
270	if (output == NULL ||
271	    max < in6_cga.cga_pubkey.iov_len + sizeof (modifier->octets) + 9) {
272		/* Output buffer error */
273		return (EINVAL);
274	}
275
276	cursor = output;
277	if (modifier == NULL) modifier = &in6_cga.cga_prepare.cga_modifier;
278	if (prefix == NULL) {
279		static const struct in6_addr llprefix = {{{ 0xfe, 0x80 }}};
280		prefix = &llprefix;
281	}
282
283	bcopy(&modifier->octets, cursor, sizeof (modifier->octets));
284	cursor += sizeof (modifier->octets);
285
286	*cursor++ = (char) collisions;
287
288	bcopy(&prefix->s6_addr[0], cursor, 8);
289	cursor += 8;
290
291	bcopy(in6_cga.cga_pubkey.iov_base, cursor, in6_cga.cga_pubkey.iov_len);
292	cursor += in6_cga.cga_pubkey.iov_len;
293
294	/* FUTURE: Extension fields */
295
296	return ((ssize_t)(cursor - (caddr_t)output));
297}
298
299int
300in6_cga_generate(const struct in6_cga_prepare *prepare, u_int8_t collisions,
301    struct in6_addr *in6)
302{
303	int error;
304	const struct iovec *pubkey;
305
306	in6_cga_node_lock_assert(LCK_MTX_ASSERT_OWNED);
307	VERIFY(in6 != NULL);
308
309	if (prepare == NULL)
310		prepare = &in6_cga.cga_prepare;
311
312	pubkey = &in6_cga.cga_pubkey;
313
314	if (pubkey->iov_base != NULL) {
315		in6_cga_generate_iid(prepare, pubkey, collisions, in6);
316		error = 0;
317	}
318	else
319		error = EADDRNOTAVAIL;
320
321	return (error);
322}
323
324/* End of file */
325