59791e27a0cac0702ca0ee2f0d8a48cf3819f942
[strongswan.git] / src / libcharon / sa / keymat_v1.c
1 /*
2 * Copyright (C) 2011 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "keymat_v1.h"
17
18 #include <daemon.h>
19
20 typedef struct private_keymat_v1_t private_keymat_v1_t;
21
22 /**
23 * Private data of an keymat_t object.
24 */
25 struct private_keymat_v1_t {
26
27 /**
28 * Public keymat_v1_t interface.
29 */
30 keymat_v1_t public;
31
32 /**
33 * IKE_SA Role, initiator or responder
34 */
35 bool initiator;
36
37 /**
38 * General purpose PRF
39 */
40 prf_t *prf;
41
42 /**
43 * Negotiated PRF algorithm
44 */
45 pseudo_random_function_t prf_alg;
46
47 /**
48 * Key used for authentication during main mode
49 */
50 chunk_t skeyid;
51
52 /**
53 * Key to derive key material from for non-ISAKMP SAs, rekeying
54 */
55 chunk_t skeyid_d;
56
57 /**
58 * Key used for authentication after main mode
59 */
60 chunk_t skeyid_a;
61 };
62
63 /**
64 * Constants used in key derivation.
65 */
66 static const chunk_t octet_0 = chunk_from_chars(0x00);
67 static const chunk_t octet_1 = chunk_from_chars(0x01);
68 static const chunk_t octet_2 = chunk_from_chars(0x02);
69
70
71 /**
72 * Converts integrity algorithm to PRF algorithm
73 */
74 static u_int16_t auth_to_prf(u_int16_t alg)
75 {
76 switch (alg)
77 {
78 case AUTH_HMAC_SHA1_96:
79 return PRF_HMAC_SHA1;
80 case AUTH_HMAC_SHA2_256_128:
81 return PRF_HMAC_SHA2_256;
82 case AUTH_HMAC_SHA2_384_192:
83 return PRF_HMAC_SHA2_384;
84 case AUTH_HMAC_SHA2_512_256:
85 return PRF_HMAC_SHA2_512;
86 case AUTH_HMAC_MD5_96:
87 return PRF_HMAC_MD5;
88 case AUTH_AES_XCBC_96:
89 return PRF_AES128_XCBC;
90 default:
91 return PRF_UNDEFINED;
92 }
93 }
94
95 /**
96 * Adjust the key length for PRF algorithms that expect a fixed key length.
97 */
98 static void adjust_keylen(u_int16_t alg, chunk_t *key)
99 {
100 switch (alg)
101 {
102 case PRF_AES128_XCBC:
103 /* while rfc4434 defines variable keys for AES-XCBC, rfc3664 does
104 * not and therefore fixed key semantics apply to XCBC for key
105 * derivation. */
106 key->len = min(key->len, 16);
107 break;
108 default:
109 /* all other algorithms use variable key length */
110 break;
111 }
112 }
113
114 METHOD(keymat_v1_t, derive_ike_keys, bool,
115 private_keymat_v1_t *this, proposal_t *proposal, diffie_hellman_t *dh,
116 chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id,
117 auth_class_t auth, shared_key_t *shared_key)
118 {
119 chunk_t g_xy, g_xi, g_xr, dh_me, spi_i, spi_r, nonces, data, skeyid_e;
120 u_int16_t alg;
121
122 spi_i = chunk_alloca(sizeof(u_int64_t));
123 spi_r = chunk_alloca(sizeof(u_int64_t));
124
125 if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL))
126 { /* no PRF negotiated, use HMAC version of integrity algorithm instead */
127 if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL)
128 || (alg = auth_to_prf(alg)) == PRF_UNDEFINED)
129 {
130 DBG1(DBG_IKE, "no %N selected",
131 transform_type_names, PSEUDO_RANDOM_FUNCTION);
132 return FALSE;
133 }
134 }
135 this->prf_alg = alg;
136 this->prf = lib->crypto->create_prf(lib->crypto, alg);
137 if (!this->prf)
138 {
139 DBG1(DBG_IKE, "%N %N not supported!",
140 transform_type_names, PSEUDO_RANDOM_FUNCTION,
141 pseudo_random_function_names, alg);
142 return FALSE;
143 }
144 if (this->prf->get_block_size(this->prf) <
145 this->prf->get_key_size(this->prf))
146 { /* TODO-IKEv1: support PRF output expansion (RFC 2409, Appendix B) */
147 DBG1(DBG_IKE, "expansion of %N %N output not supported!",
148 transform_type_names, PSEUDO_RANDOM_FUNCTION,
149 pseudo_random_function_names, alg);
150 return FALSE;
151 }
152
153 if (dh->get_shared_secret(dh, &g_xy) != SUCCESS)
154 {
155 return FALSE;
156 }
157 DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &g_xy);
158
159 *((u_int64_t*)spi_i.ptr) = id->get_initiator_spi(id);
160 *((u_int64_t*)spi_r.ptr) = id->get_responder_spi(id);
161 nonces = chunk_cata("cc", nonce_i, nonce_r);
162
163 switch (auth)
164 {
165 case AUTH_CLASS_PSK:
166 { /* SKEYID = prf(pre-shared-key, Ni_b | Nr_b) */
167 chunk_t psk;
168 if (!shared_key)
169 {
170 chunk_clear(&g_xy);
171 return FALSE;
172 }
173 psk = shared_key->get_key(shared_key);
174 adjust_keylen(alg, &psk);
175 this->prf->set_key(this->prf, psk);
176 this->prf->allocate_bytes(this->prf, nonces, &this->skeyid);
177 break;
178 }
179 case AUTH_CLASS_PUBKEY:
180 {
181 /* signatures : SKEYID = prf(Ni_b | Nr_b, g^xy)
182 * pubkey encr: SKEYID = prf(hash(Ni_b | Nr_b), CKY-I | CKY-R) */
183 /* TODO-IKEv1: implement key derivation for other schemes,
184 * fall for now */
185 }
186 default:
187 /* authentication class not supported */
188 chunk_clear(&g_xy);
189 return FALSE;
190 }
191 adjust_keylen(alg, &this->skeyid);
192 DBG4(DBG_IKE, "SKEYID %B", &this->skeyid);
193
194 /* SKEYID_d = prf(SKEYID, g^xy | CKY-I | CKY-R | 0) */
195 data = chunk_cat("cccc", g_xy, spi_i, spi_r, octet_0);
196 this->prf->set_key(this->prf, this->skeyid);
197 this->prf->allocate_bytes(this->prf, data, &this->skeyid_d);
198 chunk_clear(&data);
199 DBG4(DBG_IKE, "SKEYID_d %B", &this->skeyid_d);
200
201 /* SKEYID_a = prf(SKEYID, SKEYID_d | g^xy | CKY-I | CKY-R | 1) */
202 data = chunk_cat("ccccc", this->skeyid_d, g_xy, spi_i, spi_r, octet_1);
203 this->prf->set_key(this->prf, this->skeyid);
204 this->prf->allocate_bytes(this->prf, data, &this->skeyid_a);
205 chunk_clear(&data);
206 DBG4(DBG_IKE, "SKEYID_a %B", &this->skeyid_a);
207
208 /* SKEYID_e = prf(SKEYID, SKEYID_a | g^xy | CKY-I | CKY-R | 2) */
209 data = chunk_cat("ccccc", this->skeyid_a, g_xy, spi_i, spi_r, octet_2);
210 this->prf->set_key(this->prf, this->skeyid);
211 this->prf->allocate_bytes(this->prf, data, &skeyid_e);
212 chunk_clear(&data);
213 DBG4(DBG_IKE, "SKEYID_e %B", &skeyid_e);
214
215 chunk_clear(&g_xy);
216
217 return TRUE;
218 }
219
220 METHOD(keymat_t, create_dh, diffie_hellman_t*,
221 private_keymat_v1_t *this, diffie_hellman_group_t group)
222 {
223 return lib->crypto->create_dh(lib->crypto, group);
224 }
225
226 METHOD(keymat_t, get_aead, aead_t*,
227 private_keymat_v1_t *this, bool in)
228 {
229 return NULL;
230 }
231
232 METHOD(keymat_t, destroy, void,
233 private_keymat_v1_t *this)
234 {
235 DESTROY_IF(this->prf);
236 chunk_clear(&this->skeyid);
237 chunk_clear(&this->skeyid_d);
238 chunk_clear(&this->skeyid_a);
239 free(this);
240 }
241
242 /**
243 * See header
244 */
245 keymat_v1_t *keymat_v1_create(bool initiator)
246 {
247 private_keymat_v1_t *this;
248
249 INIT(this,
250 .public = {
251 .keymat = {
252 .create_dh = _create_dh,
253 .get_aead = _get_aead,
254 .destroy = _destroy,
255 },
256 .derive_ike_keys = _derive_ike_keys,
257 },
258 .initiator = initiator,
259 .prf_alg = PRF_UNDEFINED,
260 );
261
262 return &this->public;
263 }