created.
[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_CHAR(&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(SilcPKCS pkcs, unsigned char *random,
136                             unsigned int random_len, unsigned int *ret_len)
137 {
138   SilcBuffer buf;
139   unsigned char *pk, *ret;
140   unsigned int pk_len;
141
142   pk = silc_pkcs_get_public_key(pkcs, &pk_len);
143   if (!pk)
144     return NULL;
145
146   buf = silc_buffer_alloc(random_len + pk_len);
147   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
148   silc_buffer_format(buf,
149                      SILC_STR_UI_XNSTRING(random, random_len),
150                      SILC_STR_UI_XNSTRING(pk, pk_len),
151                      SILC_STR_END);
152   
153   ret = silc_calloc(buf->len, sizeof(*ret));
154   memcpy(ret, buf->data, buf->len);
155
156   if (ret_len)
157     *ret_len = buf->len;
158
159   silc_buffer_free(buf);
160   silc_free(pk);
161
162   return ret;
163 }
164
165 /* Generates Authentication Payload with authentication data. This is used
166    to do public key based authentication. This generates the random data
167    and the actual authentication data. Returns NULL on error. */
168
169 SilcBuffer silc_auth_public_key_auth_generate(SilcPKCS pkcs,
170                                               SilcHash hash)
171 {
172   unsigned char *random;
173   unsigned char auth_data[32];
174   unsigned int auth_len;
175   unsigned char *tmp;
176   unsigned int tmp_len;
177   SilcBuffer buf;
178
179   SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
180
181   /* Get 256 bytes of random data */
182   random = silc_rng_global_get_rn_data(256);
183   if (!random)
184     return NULL;
185   
186   /* Encode the auth data */
187   tmp = silc_auth_public_key_encode(pkcs, random, 256, &tmp_len);
188   if (!tmp)
189     return NULL;
190
191   /* Compute the hash and the signature. */
192   if (!silc_pkcs_sign_with_hash(pkcs, hash, tmp, tmp_len, auth_data,
193                                 &auth_len)) {
194     memset(random, 0, 256);
195     memset(tmp, 0, tmp_len);
196     silc_free(tmp);
197     silc_free(random);
198     return NULL;
199   }
200
201   /* Encode Authentication Payload */
202   buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, random, 256,
203                                  auth_data, auth_len);
204
205   memset(tmp, 0, tmp_len);
206   memset(auth_data, 0, sizeof(auth_data));
207   memset(random, 0, 256);
208   silc_free(tmp);
209   silc_free(random);
210
211   return buf;
212 }
213
214 /* Verifies the authentication data. Returns TRUE if authentication was
215    successfull. */
216
217 int silc_auth_public_key_auth_verify(SilcAuthPayload payload,
218                                      SilcPKCS pkcs, SilcHash hash)
219 {
220   unsigned char *tmp;
221   unsigned int tmp_len;
222
223   SILC_LOG_DEBUG(("Verifying authentication data"));
224
225   /* Encode auth data */
226   tmp = silc_auth_public_key_encode(pkcs, payload->random_data, 
227                                     payload->random_len, &tmp_len);
228   if (!tmp) {
229     SILC_LOG_DEBUG(("Authentication failed"));
230     return FALSE;
231   }
232
233   /* Verify the authencation data */
234   if (!silc_pkcs_verify_with_hash(pkcs, hash, payload->auth_data,
235                                   payload->auth_len, tmp, tmp_len)) {
236
237     memset(tmp, 0, tmp_len);
238     silc_free(tmp);
239     SILC_LOG_DEBUG(("Authentication failed"));
240     return FALSE;
241   }
242
243   memset(tmp, 0, tmp_len);
244   silc_free(tmp);
245
246   SILC_LOG_DEBUG(("Authentication successfull"));
247
248   return TRUE;
249 }
250
251 /* Same as above but the payload is not parsed yet. This will parse it. */
252
253 int silc_auth_public_key_auth_verify_data(SilcBuffer payload,
254                                           SilcPKCS pkcs, SilcHash hash)
255 {
256   SilcAuthPayload auth_payload;
257   int ret;
258
259   auth_payload = silc_auth_payload_parse(payload);
260   if (!auth_payload) {
261     SILC_LOG_DEBUG(("Authentication failed"));
262     return FALSE;
263   }
264
265   ret = silc_auth_public_key_auth_verify(auth_payload, pkcs, hash);
266
267   silc_auth_payload_free(auth_payload);
268
269   return ret;
270 }