Added decrypting/decoding/parsing of SPA data.

git-svn-id: file:///home/mbr/svn/fwknop/trunk@24 510a4753-2344-4c79-9c09-4d669213fbeb
This commit is contained in:
Damien Stuart
2008-12-28 03:08:04 +00:00
parent 620ffec5cc
commit a4e82aa68f
14 changed files with 467 additions and 35 deletions

View File

@@ -52,7 +52,7 @@ AC_C_CONST
# Checks for library functions.
#
AC_FUNC_MALLOC
AC_CHECK_FUNCS([bzero gettimeofday memmove memset setlocale strchr strdup])
AC_CHECK_FUNCS([bzero gettimeofday memmove memset strchr strcspn strdup strrchr strspn])
AC_CONFIG_FILES([Makefile
fko/Makefile

View File

@@ -3,7 +3,7 @@ lib_LTLIBRARIES = libfko.la
libfko_source_files = \
base64.c base64.h cipher_funcs.c cipher_funcs.h digest.c digest.h \
fko_client_timeout.c fko_common.h fko_digest.c fko_encode.c \
fko_encryption.c fko_error.c fko_funcs.c fko_message.c \
fko_decode.c fko_encryption.c fko_error.c fko_funcs.c fko_message.c \
fko_nat_access.c fko_rand_value.c fko_server_auth.c fko.h \
fko_timestamp.c fko_types.h fko_user.c fko_util.c fko_util.h \
md5.c md5.h rijndael.c rijndael.h sha1.c sha256.c sha.h strlcat.c \

View File

@@ -67,7 +67,7 @@ void get_random_data(unsigned char *data, int len)
* This is is done to be compatible with the data produced via
* the Perl Crypt::CBC module's use of Rijndael.
*/
void salt_and_iv(RIJNDAEL_context *ctx, char *pass, unsigned char *data)
void salt_and_iv(RIJNDAEL_context *ctx, const char *pass, unsigned char *data)
{
char pw_buf[16];
unsigned char tmp_buf[64]; /* How big does this need to be? */
@@ -132,7 +132,7 @@ void salt_and_iv(RIJNDAEL_context *ctx, char *pass, unsigned char *data)
/* Initialization entry point.
*/
void rijndael_init(RIJNDAEL_context *ctx, char *pass, unsigned char *data)
void rijndael_init(RIJNDAEL_context *ctx, const char *pass, unsigned char *data)
{
/* Use ECB mode to be compatible with the Crypt::CBC perl module.
@@ -151,7 +151,7 @@ void rijndael_init(RIJNDAEL_context *ctx, char *pass, unsigned char *data)
/* Take a chunk of data, encrypt it in the same way the perl Crypt::CBC
* module would.
*/
int fko_encrypt(unsigned char *in, int in_len, char *pass, unsigned char *out)
int fko_encrypt(unsigned char *in, int in_len, const char *pass, unsigned char *out)
{
RIJNDAEL_context ctx;
unsigned char plaintext[16];
@@ -204,7 +204,7 @@ int fko_encrypt(unsigned char *in, int in_len, char *pass, unsigned char *out)
/* Decrypt the given data.
*/
int fko_decrypt(unsigned char *in, int in_len, char *pass, unsigned char *out)
int fko_decrypt(unsigned char *in, int in_len, const char *pass, unsigned char *out)
{
RIJNDAEL_context ctx;
unsigned char plaintext[16];

View File

@@ -33,8 +33,8 @@
*/
#define PREDICT_ENCSIZE(x) (1+(x>>4)+(x&0xf?1:0))<<4
int fko_encrypt(uchar *in, int len, char *key, uchar *out);
int fko_decrypt(uchar *in, int len, char *key, uchar *out);
int fko_encrypt(uchar *in, int len, const char *key, uchar *out);
int fko_decrypt(uchar *in, int len, const char *key, uchar *out);
void hex_dump(uchar *data, int size);
#endif /* CIPHER_FUNCS_H */

View File

@@ -139,7 +139,8 @@ enum {
FKO_ERROR_INVALID_SPA_ACCESS_MSG,
FKO_ERROR_INVALID_SPA_NAT_ACCESS_MSG,
FKO_ERROR_INVALID_ENCRYPTION_TYPE,
/* Add more errors here */
FKO_ERROR_DECRYPTION_SIZE_ERROR,
/* Add more errors above this line */
FKO_ERROR_UNSUPPORTED_FEATURE,
FKO_ERROR_UNKNOWN
};
@@ -162,6 +163,7 @@ enum {
#define MAX_SPA_SERVER_AUTH_SIZE 64
#define MIN_SPA_ENCODED_MSG_SIZE 36 /* Somewhat arbitrary */
#define MIN_GNUPG_MSG_SIZE 400
/* Misc.
*/
@@ -202,6 +204,7 @@ typedef struct _fko_ctx {
/* Function prototypes
*/
int fko_new(fko_ctx_t *ctx);
int fko_new_with_data(fko_ctx_t *ctx, char *enc_data);
void fko_destroy(fko_ctx_t *ctx);
char* fko_version(fko_ctx_t *ctx);
@@ -235,7 +238,7 @@ int fko_encode_spa_data(fko_ctx_t *ctx);
int fko_decode_spa_data(fko_ctx_t *ctx);
int fko_encrypt_spa_data(fko_ctx_t *ctx, const char *enc_key);
int fko_decrypt_spa_data(fko_ctx_t *ctx, unsigned char *data, int size);
int fko_decrypt_spa_data(fko_ctx_t *ctx, const char *dec_key);
#endif /* FKO_H */

305
fko/fko_decode.c Normal file
View File

@@ -0,0 +1,305 @@
/* $Id$
*****************************************************************************
*
* File: fko_decode.c
*
* Author: Damien S. Stuart
*
* Purpose: Decrypt and decode an FKO SPA message.
*
* Copyright (C) 2008 Damien Stuart (dstuart@dstuart.org)
*
* License (GNU Public License):
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
*****************************************************************************
*/
#include "fko_common.h"
#include "fko.h"
#include "cipher_funcs.h"
#include "base64.h"
#include "digest.h"
/* Decrypt the encoded SPA data.
*/
int fko_decode_spa_data(fko_ctx_t *ctx)
{
char *tbuf, *ndx;
//int cipher_len, res;
int edata_size, t_size;
/* Check for required data.
*/
if(ctx->encoded_msg == NULL
|| strlen(ctx->encoded_msg) < MIN_SPA_ENCODED_MSG_SIZE)
return(FKO_ERROR_INVALID_DATA);
edata_size = strlen(ctx->encoded_msg);
/* Move the Digest to its place in the context.
*/
ndx = strrchr(ctx->encoded_msg, ':'); /* Find the last : in the data */
if(ndx == NULL)
return(FKO_ERROR_INVALID_DATA);
ndx++;
t_size = strlen(ndx);
switch(t_size)
{
case MD_B64_SIZE(MD5_DIGESTSIZE):
ctx->digest_type = FKO_DIGEST_MD5;
break;
case MD_B64_SIZE(SHA1_DIGESTSIZE):
ctx->digest_type = FKO_DIGEST_SHA1;
break;
case MD_B64_SIZE(SHA256_DIGESTSIZE):
ctx->digest_type = FKO_DIGEST_SHA256;
break;
default: /* Invalid or unsupported digest */
return(FKO_ERROR_INVALID_DIGEST_TYPE);
}
/* Copy the digest into the context and terminate the encoded data
* at that point so the original digest is not part of the
* encoded string.
*/
if((ctx->digest = strdup(ndx)) == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
/* Zero out the rest of the encoded_msg bucket...
*/
bzero((ndx-1), t_size);
/* Now we will work through the encoded data and extract (and base64-
* decode where necessary), the SPA data fields and populate the context.
*/
ndx = ctx->encoded_msg;
/* The rand val data */
if((t_size = strcspn(ndx, ":")) < FKO_RAND_VAL_SIZE)
return(FKO_ERROR_INVALID_DATA);
strlcpy(ctx->rand_val, ndx, FKO_RAND_VAL_SIZE+1);
/* Make a tmp bucket for processinv base64 encoded data.
*/
if((tbuf = malloc(FKO_ENCODE_TMP_BUF_SIZE)) == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
/* Jump to the next field (username). We need to use the temp buffer
* for the base64 decode step.
*/
ndx += t_size + 1;
if((t_size = strcspn(ndx, ":")) < 1)
{
free(tbuf);
return(FKO_ERROR_INVALID_DATA);
}
strlcpy(tbuf, ndx, t_size+1);
if((ctx->username = malloc(t_size+1)) == NULL) /* Yes, more than we need */
{
free(tbuf);
return(FKO_ERROR_MEMORY_ALLOCATION);
}
b64_decode(tbuf, (unsigned char*)ctx->username, t_size);
/* Extract the timestamp value.
*/
ndx += t_size + 1;
if((t_size = strcspn(ndx, ":")) < 1)
{
free(tbuf);
return(FKO_ERROR_INVALID_DATA);
}
strlcpy(tbuf, ndx, t_size+1);
ctx->timestamp = (unsigned int)atoi(tbuf);
/* Extract the version string.
*/
ndx += t_size + 1;
if((t_size = strcspn(ndx, ":")) < 1)
{
free(tbuf);
return(FKO_ERROR_INVALID_DATA);
}
if((ctx->version = malloc(t_size+1)) == NULL)
{
free(tbuf);
return(FKO_ERROR_MEMORY_ALLOCATION);
}
strlcpy(ctx->version, ndx, t_size+1);
/* Extract the message type value.
*/
ndx += t_size + 1;
if((t_size = strcspn(ndx, ":")) < 1)
{
free(tbuf);
return(FKO_ERROR_INVALID_DATA);
}
strlcpy(tbuf, ndx, t_size+1);
ctx->message_type = (unsigned int)atoi(tbuf);
/* Extract the SPA message string.
*/
ndx += t_size + 1;
if((t_size = strcspn(ndx, ":")) < 1)
{
free(tbuf);
return(FKO_ERROR_INVALID_DATA);
}
strlcpy(tbuf, ndx, t_size+1);
if((ctx->message = malloc(t_size+1)) == NULL) /* Yes, more than we need */
{
free(tbuf);
return(FKO_ERROR_MEMORY_ALLOCATION);
}
b64_decode(tbuf, (unsigned char*)ctx->message, t_size);
/* Extract nat_access string if the message_type indicates so.
*/
if( ctx->message_type == FKO_NAT_ACCESS_MSG
|| ctx->message_type == FKO_LOCAL_NAT_ACCESS_MSG
|| ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
|| ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
{
ndx += t_size + 1;
if((t_size = strcspn(ndx, ":")) < 1)
{
free(tbuf);
return(FKO_ERROR_INVALID_DATA);
}
strlcpy(tbuf, ndx, t_size+1);
if((ctx->nat_access = malloc(t_size+1)) == NULL) /* Yes, more than we need */
{
free(tbuf);
return(FKO_ERROR_MEMORY_ALLOCATION);
}
b64_decode(tbuf, (unsigned char*)ctx->nat_access, t_size);
}
/* Now look for a server_auth string.
*/
ndx += t_size + 1;
if((t_size = strlen(ndx)) > 0)
{
/* There is data, but what is it?
* If the message_type does not have a timeout, assume it is a
* server_auth field.
*/
if( ctx->message_type != FKO_CLIENT_TIMEOUT_ACCESS_MSG
&& ctx->message_type != FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
&& ctx->message_type != FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
{
strlcpy(tbuf, ndx, t_size+1);
if((ctx->server_auth = malloc(t_size+1)) == NULL) /* Yes, more than we need */
{
free(tbuf);
return(FKO_ERROR_MEMORY_ALLOCATION);
}
b64_decode(tbuf, (unsigned char*)ctx->server_auth, t_size);
/* At this point we should be done.
*/
free(tbuf);
/* Call the context initialized.
*/
ctx->initval = FKO_CTX_INITIALIZED;
FKO_SET_CTX_INITIALIZED(ctx);
return(FKO_SUCCESS);
}
/* If we are here then we may still have a server_auth string,
* or a timeout, or both. So we look for a ':' delimiter. If
* it is there we have both, if not we check the message_type
* again.
*/
if(strchr(ndx, ':'))
{
t_size = strcspn(ndx, ":");
/* Looks like we have both, so assume this is the
*/
strlcpy(tbuf, ndx, t_size+1);
if((ctx->server_auth = malloc(t_size+1)) == NULL) /* Yes, more than we need */
{
free(tbuf);
return(FKO_ERROR_MEMORY_ALLOCATION);
}
b64_decode(tbuf, (unsigned char*)ctx->server_auth, t_size);
ndx += t_size + 1;
}
/* Now we look for a timeout value if one is supposed to be there.
*/
if( ctx->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG
|| ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
|| ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
{
if((t_size = strlen(ndx)) < 1)
{
free(tbuf);
return(FKO_ERROR_INVALID_DATA);
}
/* Should be a number only.
*/
if(strspn(ndx, "0123456789") != t_size)
{
free(tbuf);
return(FKO_ERROR_INVALID_DATA);
}
ctx->client_timeout = (unsigned int)atoi(ndx);
}
}
/* Done with the tmp buffer.
*/
free(tbuf);
/* Call the context initialized.
*/
ctx->initval = FKO_CTX_INITIALIZED;
FKO_SET_CTX_INITIALIZED(ctx);
return(FKO_SUCCESS);
}
/***EOF***/

View File

@@ -137,13 +137,13 @@ int fko_encode_spa_data(fko_ctx_t *ctx)
/* If we have a server_auth field set. Add it here.
*
*/
if(ctx->server_auth != NULL)
{
strlcat(tbuf, ":", FKO_ENCODE_TMP_BUF_SIZE);
if((res = append_b64(tbuf, ctx->server_auth)) != FKO_SUCCESS)
return(res);
}
*/
/* If a client timeout is specified and we are not dealing with a
* SPA command message, add the timeout here.
@@ -179,19 +179,5 @@ int fko_encode_spa_data(fko_ctx_t *ctx)
return(FKO_SUCCESS);
}
/* Return the SPA encryption type.
*/
int fko_decode_spa_data(fko_ctx_t *ctx)
{
/* Must be initialized
*/
if(!CTX_INITIALIZED(ctx))
return(FKO_ERROR_CTX_NOT_INITIALIZED);
return(FKO_SUCCESS);
}
/***EOF***/

View File

@@ -28,6 +28,8 @@
#include "cipher_funcs.h"
#include "base64.h"
#define B64_RIJNDAEL_SALT "U2FsdGVkX1"
/* Set the SPA encryption type.
*/
int fko_set_spa_encryption_type(fko_ctx_t *ctx, short encrypt_type)
@@ -130,4 +132,78 @@ int fko_encrypt_spa_data(fko_ctx_t *ctx, const char *enc_key)
return(FKO_SUCCESS);
}
/* Decode, decrypt, and parse SPA data into the context.
*/
int fko_decrypt_spa_data(fko_ctx_t *ctx, const char *dec_key)
{
char *tbuf;
unsigned char *cipher;
int b64_len, cipher_len, pt_len;
/* First, make sure we have data to work with.
*/
if(ctx->encrypted_msg == NULL
|| strlen(ctx->encrypted_msg) < MIN_SPA_ENCODED_MSG_SIZE)
return(FKO_ERROR_INVALID_DATA);
/* Determine type of encryption used. For know, we are using the
* size of the message. However, we will want to come up with a
* more reliable method of identification.
*/
b64_len = strlen(ctx->encrypted_msg);
if(b64_len > MIN_GNUPG_MSG_SIZE)
{
/* TODO: add GPG handling */
/* Since we do not support GPG yet, we will just fall through */
}
/* Assuming Rijndael */
/* Now see if we need to add the "Salted__" string to the front of the
* encrypted data.
*/
if(strncmp(ctx->encrypted_msg, B64_RIJNDAEL_SALT, strlen(B64_RIJNDAEL_SALT)))
{
/* We need to realloc space for the salt.
*/
tbuf = realloc(ctx->encrypted_msg, b64_len + 12);
if(tbuf == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
memmove(tbuf+10, tbuf, b64_len);
ctx->encrypted_msg = memcpy(tbuf, B64_RIJNDAEL_SALT, strlen(B64_RIJNDAEL_SALT));
}
/* Create a bucket for the (base64) decoded encrypted data and get the
* raw cipher data.
*/
if((cipher = malloc(strlen(ctx->encrypted_msg))) == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
cipher_len = b64_decode(ctx->encrypted_msg, cipher, b64_len);
/* Create a bucket for the plaintext data and decrypt the message
* data into it.
*/
if((ctx->encoded_msg = malloc(cipher_len)) == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
pt_len = fko_decrypt(cipher, cipher_len, dec_key, (unsigned char*)ctx->encoded_msg);
/* Done with cipher...
*/
free(cipher);
/* The length of the decrypted data should be within 16 of the
* length of the encrypted version.
*/
if(pt_len < (cipher_len - 32))
return(FKO_ERROR_DECRYPTION_SIZE_ERROR);
/* Call fko_decode and return the results.
*/
return(fko_decode_spa_data(ctx));
}
/***EOF***/

View File

@@ -33,7 +33,7 @@ static const char *fko_err_msgs[] = {
"Success",
"FKO Context is not initialized",
"Unable to allocate memory",
"Args contains invalid data",
"Args contain invalid data",
"Value or Size of the data exceeded the max allowed",
"Unable to determine username",
"Missing or incomplete SPA data",
@@ -44,7 +44,8 @@ static const char *fko_err_msgs[] = {
"Invalid SPA access mesage format",
"Invalid SPA nat_access mesage format",
"Invalid encryption type",
"Unsupported or unimplemented feature or function.",
"Unexpected or invalid size for decrypted data",
"Unsupported or unimplemented feature or function",
"Unknown/Unclassified error",
0
};

View File

@@ -26,6 +26,11 @@
#include "fko_common.h"
#include "fko.h"
/* The base64 encoded version of "Salted__" that may be found at the head
* of Rijndael encrypted data.
*/
#define B64_RIJNDAEL_SALT "U2FsdGVkX1"
/* Initialize an fko context.
*/
int fko_new(fko_ctx_t *ctx)
@@ -113,6 +118,26 @@ int fko_new(fko_ctx_t *ctx)
return(FKO_SUCCESS);
}
/* Initialize an fko context with external (encrypted/encoded) data.
* This is used to create a context with the purpose of decoding
* and parsing the provided data into the context data.
*/
int fko_new_with_data(fko_ctx_t *ctx, char *enc_msg)
{
/* Zero out the context...
*/
bzero(ctx, sizeof(fko_ctx_t));
/* First, add the data to the context.
*/
if((ctx->encrypted_msg = strdup(enc_msg)) == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
FKO_SET_CTX_INITIALIZED(ctx);
return(FKO_SUCCESS);
}
/* Destroy a context and free its resources
*/
void fko_destroy(fko_ctx_t *ctx)

View File

@@ -194,7 +194,7 @@ int validate_access_msg(const char *msg)
&& strncmp(ndx, "none", 4))
return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
ndx = strchr(ndx, ',');
ndx = strchr(ndx, '/');
if(ndx == NULL || (1+(ndx - msg)) >= startlen)
return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);

View File

@@ -59,6 +59,20 @@ int fko_set_spa_nat_access(fko_ctx_t *ctx, const char *msg)
if(ctx->nat_access == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
/* If we set the nat_access message Then we force the message_type
* as well. Technically, the message type should be set already.
* This will serve a half-protective measure.
* --DSS XXX: should do this better.
*/
if(ctx->client_timeout > 0)
{
if(ctx->message_type != FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
ctx->message_type = FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG;
}
else
if(ctx->message_type != FKO_LOCAL_NAT_ACCESS_MSG)
ctx->message_type = FKO_NAT_ACCESS_MSG;
return(FKO_SUCCESS);
}

View File

@@ -34,7 +34,7 @@ int fko_set_spa_server_auth(fko_ctx_t *ctx, const char *msg)
* --DSS This is not supported yet
****************************************
*/
return(FKO_ERROR_UNSUPPORTED_FEATURE);
//return(FKO_ERROR_UNSUPPORTED_FEATURE);
/* Context must be initialized.

View File

@@ -32,7 +32,7 @@ void hex_dump(unsigned char *data, int size);
int main(int argc, char **argv)
{
fko_ctx_t ctx;
fko_ctx_t ctx, ctx2;
int res;
/* Intialize the context */
@@ -40,17 +40,26 @@ int main(int argc, char **argv)
if(res != FKO_SUCCESS)
fprintf(stderr, "Error #%i from fko_new: %s\n", res, fko_errstr(res));
res = fko_set_spa_message_type(&ctx, FKO_ACCESS_MSG);
if(res != FKO_SUCCESS)
fprintf(stderr, "Error #%i from fko_set_spa_message_type: %s\n", res, fko_errstr(res));
/* Set a message string */
res = fko_set_spa_message(&ctx, "192.168.1.5,tcp,22");
res = fko_set_spa_message(&ctx, "0.0.0.0,tcp/22");
if(res != FKO_SUCCESS)
fprintf(stderr, "Error #%i from fko_set_spa_message: %s\n", res, fko_errstr(res));
//fko_set_spa_digest_type(&ctx, FKO_DIGEST_SHA1);
fko_set_spa_client_timeout(&ctx, 300);
//res = fko_set_spa_server_auth(&ctx, "crypt,BubbaWasHere");
//if(res != FKO_SUCCESS)
// fprintf(stderr, "Error #%i from fko_set_spa_server_auth: %s\n", res, fko_errstr(res));
res = fko_set_spa_nat_access(&ctx, "192.168.1.2,22");
if(res != FKO_SUCCESS)
fprintf(stderr, "Error #%i from fko_set_spa_nat_access: %s\n", res, fko_errstr(res));
fko_set_spa_client_timeout(&ctx, 120);
res = fko_set_spa_server_auth(&ctx, "crypt,BubbaWasHere");
if(res != FKO_SUCCESS)
fprintf(stderr, "Error #%i from fko_set_spa_server_auth: %s\n", res, fko_errstr(res));
/* Encode the SPA data
res = fko_encode_spa_data(&ctx);
@@ -68,7 +77,20 @@ int main(int argc, char **argv)
//printf("\nHex dump of fko_ctx:\n====================\n");
//hex_dump((unsigned char*)&ctx, sizeof(fko_ctx_t));
/* Now we create a new context based on data from the first one.
*/
res = fko_new_with_data(&ctx2, ctx.encrypted_msg);
if(res != FKO_SUCCESS)
fprintf(stderr, "Error #%i from fko_new_with_data: %s\n", res, fko_errstr(res));
res = fko_decrypt_spa_data(&ctx2, "BubbaWasHere");
if(res != FKO_SUCCESS)
fprintf(stderr, "Error #%i from fko_decrypt_spa_data: %s\n", res, fko_errstr(res));
display_ctx(&ctx2);
fko_destroy(&ctx);
fko_destroy(&ctx2);
return(0);
}