2 *****************************************************************************
6 * Author: Damien S. Stuart
8 * Purpose: Decrypt and decode an FKO SPA message.
10 * Copyright 2009-2010 Damien Stuart (dstuart@dstuart.org)
12 * License (GNU Public License):
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 *****************************************************************************
31 #include "fko_common.h"
33 #include "cipher_funcs.h"
37 /* Decrypt the encoded SPA data.
40 fko_decode_spa_data(fko_ctx_t ctx)
45 /* Check for required data.
47 if(ctx->encoded_msg == NULL
48 || strlen(ctx->encoded_msg) < MIN_SPA_ENCODED_MSG_SIZE)
49 return(FKO_ERROR_INVALID_DATA);
51 /* Move the Digest to its place in the context.
53 ndx = strrchr(ctx->encoded_msg, ':'); /* Find the last : in the data */
55 return(FKO_ERROR_INVALID_DATA);
64 ctx->digest_type = FKO_DIGEST_MD5;
68 ctx->digest_type = FKO_DIGEST_SHA1;
71 case SHA256_B64_LENGTH:
72 ctx->digest_type = FKO_DIGEST_SHA256;
75 case SHA384_B64_LENGTH:
76 ctx->digest_type = FKO_DIGEST_SHA384;
79 case SHA512_B64_LENGTH:
80 ctx->digest_type = FKO_DIGEST_SHA512;
83 default: /* Invalid or unsupported digest */
84 return(FKO_ERROR_INVALID_DIGEST_TYPE);
87 /* Copy the digest into the context and terminate the encoded data
88 * at that point so the original digest is not part of the
91 ctx->digest = strdup(ndx);
92 if(ctx->digest == NULL)
93 return(FKO_ERROR_MEMORY_ALLOCATION);
95 /* Zero out the rest of the encoded_msg bucket...
97 bzero((ndx-1), t_size);
99 /* Make a tmp bucket for processing base64 encoded data and
102 tbuf = malloc(FKO_ENCODE_TMP_BUF_SIZE);
104 return(FKO_ERROR_MEMORY_ALLOCATION);
106 /* Can now verify the digest.
108 switch(ctx->digest_type)
111 md5_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
114 case FKO_DIGEST_SHA1:
115 sha1_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
118 case FKO_DIGEST_SHA256:
119 sha256_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
122 case FKO_DIGEST_SHA384:
123 sha384_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
126 case FKO_DIGEST_SHA512:
127 sha512_base64(tbuf, (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
132 /* We give up here if the computed digest does not match the
133 * digest in the message data.
135 if(strcmp(ctx->digest, tbuf))
138 return(FKO_ERROR_DIGEST_VERIFICATION_FAILED);
141 /* Now we will work through the encoded data and extract (and base64-
142 * decode where necessary), the SPA data fields and populate the context.
144 ndx = ctx->encoded_msg;
146 /* The rand val data */
147 if((t_size = strcspn(ndx, ":")) < FKO_RAND_VAL_SIZE)
150 return(FKO_ERROR_INVALID_DATA);
153 ctx->rand_val = calloc(1, FKO_RAND_VAL_SIZE+1);
154 if(ctx->rand_val == NULL)
157 return(FKO_ERROR_MEMORY_ALLOCATION);
159 ctx->rand_val = strncpy(ctx->rand_val, ndx, FKO_RAND_VAL_SIZE);
161 /* Jump to the next field (username). We need to use the temp buffer
162 * for the base64 decode step.
165 if((t_size = strcspn(ndx, ":")) < 1)
168 return(FKO_ERROR_INVALID_DATA);
171 strlcpy(tbuf, ndx, t_size+1);
173 ctx->username = malloc(t_size+1); /* Yes, more than we need */
174 if(ctx->username == NULL)
177 return(FKO_ERROR_MEMORY_ALLOCATION);
180 b64_decode(tbuf, (unsigned char*)ctx->username);
182 /* Extract the timestamp value.
185 if((t_size = strcspn(ndx, ":")) < 1)
188 return(FKO_ERROR_INVALID_DATA);
191 strlcpy(tbuf, ndx, t_size+1);
193 ctx->timestamp = (unsigned int)atoi(tbuf);
195 /* Extract the version string.
198 if((t_size = strcspn(ndx, ":")) < 1)
201 return(FKO_ERROR_INVALID_DATA);
204 ctx->version = malloc(t_size+1);
205 if(ctx->version == NULL)
208 return(FKO_ERROR_MEMORY_ALLOCATION);
211 strlcpy(ctx->version, ndx, t_size+1);
213 /* Extract the message type value.
216 if((t_size = strcspn(ndx, ":")) < 1)
219 return(FKO_ERROR_INVALID_DATA);
222 strlcpy(tbuf, ndx, t_size+1);
224 ctx->message_type = (unsigned int)atoi(tbuf);
226 /* Extract the SPA message string.
229 if((t_size = strcspn(ndx, ":")) < 1)
232 return(FKO_ERROR_INVALID_DATA);
235 strlcpy(tbuf, ndx, t_size+1);
237 ctx->message = malloc(t_size+1); /* Yes, more than we need */
238 if(ctx->message == NULL)
241 return(FKO_ERROR_MEMORY_ALLOCATION);
244 b64_decode(tbuf, (unsigned char*)ctx->message);
246 /* Extract nat_access string if the message_type indicates so.
248 if( ctx->message_type == FKO_NAT_ACCESS_MSG
249 || ctx->message_type == FKO_LOCAL_NAT_ACCESS_MSG
250 || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
251 || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
254 if((t_size = strcspn(ndx, ":")) < 1)
257 return(FKO_ERROR_INVALID_DATA);
260 strlcpy(tbuf, ndx, t_size+1);
262 ctx->nat_access = malloc(t_size+1); /* Yes, more than we need */
263 if(ctx->nat_access == NULL)
266 return(FKO_ERROR_MEMORY_ALLOCATION);
269 b64_decode(tbuf, (unsigned char*)ctx->nat_access);
272 /* Now look for a server_auth string.
275 if((t_size = strlen(ndx)) > 0)
277 /* There is data, but what is it?
278 * If the message_type does not have a timeout, assume it is a
281 if( ctx->message_type != FKO_CLIENT_TIMEOUT_ACCESS_MSG
282 && ctx->message_type != FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
283 && ctx->message_type != FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
285 strlcpy(tbuf, ndx, t_size+1);
287 ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */
288 if(ctx->server_auth == NULL)
291 return(FKO_ERROR_MEMORY_ALLOCATION);
294 b64_decode(tbuf, (unsigned char*)ctx->server_auth);
296 /* At this point we should be done.
300 /* Call the context initialized.
302 ctx->initval = FKO_CTX_INITIALIZED;
303 FKO_SET_CTX_INITIALIZED(ctx);
308 /* If we are here then we may still have a server_auth string,
309 * or a timeout, or both. So we look for a ':' delimiter. If
310 * it is there we have both, if not we check the message_type
315 t_size = strcspn(ndx, ":");
317 /* Looks like we have both, so assume this is the
319 strlcpy(tbuf, ndx, t_size+1);
321 ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */
322 if(ctx->server_auth == NULL)
325 return(FKO_ERROR_MEMORY_ALLOCATION);
328 b64_decode(tbuf, (unsigned char*)ctx->server_auth);
333 /* Now we look for a timeout value if one is supposed to be there.
335 if( ctx->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG
336 || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
337 || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
339 if((t_size = strlen(ndx)) < 1)
342 return(FKO_ERROR_INVALID_DATA);
345 /* Should be a number only.
347 if(strspn(ndx, "0123456789") != t_size)
350 return(FKO_ERROR_INVALID_DATA);
353 ctx->client_timeout = (unsigned int)atoi(ndx);
357 /* Done with the tmp buffer.
361 /* Call the context initialized.
363 ctx->initval = FKO_CTX_INITIALIZED;
364 FKO_SET_CTX_INITIALIZED(ctx);