updates.
[silc.git] / lib / silccore / silcauth.c
1 /*
2
3   silcauth.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "silcincludes.h"
23 #include "silcauth.h"
24
25 /******************************************************************************
26
27                            Authentication Payload
28
29 ******************************************************************************/
30
31 /* Authentication Payload structure */
32 struct SilcAuthPayloadStruct {
33   unsigned short len;
34   unsigned short auth_method;
35   unsigned short random_len;
36   unsigned char *random_data;
37   unsigned short auth_len;
38   unsigned char *auth_data;
39 };
40
41 /* Parses and returns Authentication Payload */
42
43 SilcAuthPayload silc_auth_payload_parse(SilcBuffer buffer)
44 {
45   SilcAuthPayload new;
46   int ret;
47
48   SILC_LOG_DEBUG(("Parsing Authentication Payload"));
49
50   new = silc_calloc(1, sizeof(*new));
51
52   /* Parse the payload */
53   ret = silc_buffer_unformat(buffer, 
54                              SILC_STR_UI_SHORT(&new->len),
55                              SILC_STR_UI_SHORT(&new->auth_method),
56                              SILC_STR_UI16_NSTRING_ALLOC(&new->random_data,
57                                                          &new->random_len),
58                              SILC_STR_UI16_NSTRING_ALLOC(&new->auth_data,
59                                                          &new->auth_len),
60                              SILC_STR_END);
61   if (ret == -1) {
62     silc_free(new);
63     return NULL;
64   }
65
66   if (new->len != buffer->len) {
67     silc_auth_payload_free(new);
68     return NULL;
69   }
70
71   /* If password authentication, random data must not be set */
72   if (new->auth_method == SILC_AUTH_PASSWORD && new->random_len) {
73     silc_auth_payload_free(new);
74     return NULL;
75   }
76
77   return new;
78 }
79
80 /* Encodes authentication payload into buffer and returns it */
81
82 SilcBuffer silc_auth_payload_encode(SilcAuthMethod method,
83                                     unsigned char *random_data,
84                                     unsigned short random_len,
85                                     unsigned char *auth_data,
86                                     unsigned short auth_len)
87 {
88   SilcBuffer buffer;
89   unsigned int len;
90
91   SILC_LOG_DEBUG(("Encoding Authentication Payload"));
92
93   len = 4 + 4 + random_len + auth_len;
94   buffer = silc_buffer_alloc(len);
95   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
96   silc_buffer_format(buffer,
97                      SILC_STR_UI_SHORT(len),
98                      SILC_STR_UI_SHORT(method),
99                      SILC_STR_UI_SHORT(random_len),
100                      SILC_STR_UI_XNSTRING(random_data, random_len),
101                      SILC_STR_UI_SHORT(auth_len),
102                      SILC_STR_UI_XNSTRING(auth_data, auth_len),
103                      SILC_STR_END);
104
105   return buffer;
106 }
107
108 /* Frees authentication payload. */
109
110 void silc_auth_payload_free(SilcAuthPayload payload)
111 {
112   if (payload) {
113     if (payload->random_data) {
114       memset(payload->random_data, 0, payload->random_len);
115       silc_free(payload->random_data);
116     }
117     if (payload->auth_data) {
118       memset(payload->auth_data, 0, payload->auth_len);
119       silc_free(payload->auth_data);
120     }
121     silc_free(payload);
122   }
123 }
124
125 /******************************************************************************
126
127                            Authentication Routines
128
129 ******************************************************************************/
130
131 /* Encodes the authentication data for hashing and signing as the protocol
132    dictates. */
133
134 static unsigned char *
135 silc_auth_public_key_encode_data(SilcPKCS pkcs, unsigned char *random,
136                                  unsigned int random_len, void *id,
137                                  SilcIdType type, unsigned int *ret_len)
138 {
139   SilcBuffer buf;
140   unsigned char *pk, *id_data, *ret;
141   unsigned int pk_len, id_len;
142
143   pk = silc_pkcs_get_public_key(pkcs, &pk_len);
144   if (!pk)
145     return NULL;
146
147   id_data = silc_id_id2str(id, type);
148   if (!id_data) {
149     silc_free(pk);
150     return NULL;
151   }
152   id_len = silc_id_get_len(type);
153
154   buf = silc_buffer_alloc(random_len + pk_len);
155   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
156   silc_buffer_format(buf,
157                      SILC_STR_UI_XNSTRING(random, random_len),
158                      SILC_STR_UI_XNSTRING(id_data, id_len),
159                      SILC_STR_UI_XNSTRING(pk, pk_len),
160                      SILC_STR_END);
161   
162   ret = silc_calloc(buf->len, sizeof(*ret));
163   memcpy(ret, buf->data, buf->len);
164
165   if (ret_len)
166     *ret_len = buf->len;
167
168   silc_buffer_free(buf);
169   silc_free(id_data);
170   silc_free(pk);
171
172   return ret;
173 }
174
175 /* Generates Authentication Payload with authentication data. This is used
176    to do public key based authentication. This generates the random data
177    and the actual authentication data. Returns NULL on error. */
178
179 SilcBuffer silc_auth_public_key_auth_generate(SilcPKCS pkcs,
180                                               SilcHash hash,
181                                               void *id, SilcIdType type)
182 {
183   unsigned char *random;
184   unsigned char auth_data[32];
185   unsigned int auth_len;
186   unsigned char *tmp;
187   unsigned int tmp_len;
188   SilcBuffer buf;
189
190   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
191
192   /* Get 256 bytes of random data */
193   random = silc_rng_global_get_rn_data(256);
194   if (!random)
195     return NULL;
196   
197   /* Encode the auth data */
198   tmp = silc_auth_public_key_encode_data(pkcs, random, 256, id, type, 
199                                          &tmp_len);
200   if (!tmp)
201     return NULL;
202
203   /* Compute the hash and the signature. */
204   if (!silc_pkcs_sign_with_hash(pkcs, hash, tmp, tmp_len, auth_data,
205                                 &auth_len)) {
206     memset(random, 0, 256);
207     memset(tmp, 0, tmp_len);
208     silc_free(tmp);
209     silc_free(random);
210     return NULL;
211   }
212
213   /* Encode Authentication Payload */
214   buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, random, 256,
215                                  auth_data, auth_len);
216
217   memset(tmp, 0, tmp_len);
218   memset(auth_data, 0, sizeof(auth_data));
219   memset(random, 0, 256);
220   silc_free(tmp);
221   silc_free(random);
222
223   return buf;
224 }
225
226 /* Verifies the authentication data. Returns TRUE if authentication was
227    successfull. */
228
229 int silc_auth_public_key_auth_verify(SilcAuthPayload payload,
230                                      SilcPKCS pkcs, SilcHash hash,
231                                      void *id, SilcIdType type)
232 {
233   unsigned char *tmp;
234   unsigned int tmp_len;
235
236   SILC_LOG_DEBUG(("Verifying authentication data"));
237
238   /* Encode auth data */
239   tmp = silc_auth_public_key_encode_data(pkcs, payload->random_data, 
240                                          payload->random_len, 
241                                          id, type, &tmp_len);
242   if (!tmp) {
243     SILC_LOG_DEBUG(("Authentication failed"));
244     return FALSE;
245   }
246
247   /* Verify the authentication data */
248   if (!silc_pkcs_verify_with_hash(pkcs, hash, payload->auth_data,
249                                   payload->auth_len, tmp, tmp_len)) {
250
251     memset(tmp, 0, tmp_len);
252     silc_free(tmp);
253     SILC_LOG_DEBUG(("Authentication failed"));
254     return FALSE;
255   }
256
257   memset(tmp, 0, tmp_len);
258   silc_free(tmp);
259
260   SILC_LOG_DEBUG(("Authentication successfull"));
261
262   return TRUE;
263 }
264
265 /* Same as above but the payload is not parsed yet. This will parse it. */
266
267 int silc_auth_public_key_auth_verify_data(SilcBuffer payload,
268                                           SilcPKCS pkcs, SilcHash hash,
269                                           void *id, SilcIdType type)
270 {
271   SilcAuthPayload auth_payload;
272   int ret;
273
274   auth_payload = silc_auth_payload_parse(payload);
275   if (!auth_payload) {
276     SILC_LOG_DEBUG(("Authentication failed"));
277     return FALSE;
278   }
279
280   ret = silc_auth_public_key_auth_verify(auth_payload, pkcs, hash, 
281                                          id, type);
282
283   silc_auth_payload_free(auth_payload);
284
285   return ret;
286 }