/*
 *****************************************************************************
 *
 * File:    fko_encryption.c
 *
 * Author:  Damien S. Stuart
 *
 * Purpose: Set/Get the spa encryption type.
 *
 * Copyright 2009-2010 Damien Stuart (dstuart@dstuart.org)
 *
 *  License (GNU Public License):
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  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"

#if HAVE_LIBGPGME
  #include "gpgme_funcs.h"
  #if HAVE_SYS_STAT_H
    #include <sys/stat.h>
  #endif
#endif

/* Prep and encrypt using Rijndael
*/
static int
_rijndael_encrypt(fko_ctx_t ctx, const char *enc_key)
{
    char           *plain;
    char           *b64cipher;
    unsigned char  *cipher;
    int             cipher_len;

    /* Make a bucket big enough to hold the enc msg + digest (plaintext)
     * and populate it appropriately.
    */
    plain = malloc(strlen(ctx->encoded_msg) + strlen(ctx->digest) + 4);
    if(plain == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    snprintf(plain, strlen(ctx->encoded_msg) + strlen(ctx->digest) + 4,
            "%s:%s", ctx->encoded_msg, ctx->digest);

    /* Make a bucket for the encrypted version and populate it.
    */
    cipher = malloc(strlen(plain) + 32); /* Plus padding for salt and Block */
    if(cipher == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    cipher_len = rij_encrypt(
        (unsigned char*)plain, strlen(plain), (char*)enc_key, cipher
    );

    /* Now make a bucket for the base64-encoded version and populate it.
    */
    b64cipher = malloc(((cipher_len / 3) * 4) + 8);
    if(b64cipher == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    b64_encode(cipher, b64cipher, cipher_len);
    strip_b64_eq(b64cipher);

    ctx->encrypted_msg = strdup(b64cipher);

    /* Clean-up
    */
    free(plain);
    free(cipher);
    free(b64cipher);

    if(ctx->encrypted_msg == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    return(FKO_SUCCESS);
}

/* Decode, decrypt, and parse SPA data into the context.
*/
static int
_rijndael_decrypt(fko_ctx_t ctx, const char *dec_key)
{
    char           *tbuf;
    unsigned char  *ndx;
    unsigned char  *cipher;
    int             cipher_len, pt_len, i, err = 0;

    int             b64_len = strlen(ctx->encrypted_msg);

    /* 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+strlen(B64_RIJNDAEL_SALT), tbuf, b64_len);

        ctx->encrypted_msg = memcpy(tbuf, B64_RIJNDAEL_SALT, strlen(B64_RIJNDAEL_SALT));

        /* Adjust b64_len for added SALT value and Make sure we are still
         * a properly NULL-terminated string (Ubuntu was one system for
         * which this was an issue).
        */
        b64_len += strlen(B64_RIJNDAEL_SALT);
        tbuf[b64_len] = '\0';
    }

    /* Create a bucket for the (base64) decoded encrypted data and get the
     * raw cipher data.
    */
    cipher = malloc(strlen(ctx->encrypted_msg));
    if(cipher == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    if((cipher_len = b64_decode(ctx->encrypted_msg, cipher)) < 0)
        return(FKO_ERROR_INVALID_DATA);

    /* Create a bucket for the plaintext data and decrypt the message
     * data into it.
    */
    ctx->encoded_msg = malloc(cipher_len);
    if(ctx->encoded_msg == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    pt_len = rij_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 32 bytes of the
     * length of the encrypted version.
    */
    if(pt_len < (cipher_len - 32))
        return(FKO_ERROR_DECRYPTION_SIZE);

    /* At this point we can check the data to see if we have a good
     * decryption by ensuring the first field (16-digit random decimal
     * value) is valid and is followed by a colon.  Additional checks
     * are made in fko_decode_spa_data().
    */
    ndx = (unsigned char *)ctx->encoded_msg;
    for(i=0; i<FKO_RAND_VAL_SIZE; i++)
        if(!isdigit(*(ndx++)))
            err++;

    if(err > 0 || *ndx != ':')
        return(FKO_ERROR_DECRYPTION_FAILURE);

    /* Call fko_decode and return the results.
    */
    return(fko_decode_spa_data(ctx));
}


#if HAVE_LIBGPGME

/* Prep and encrypt using gpgme
*/
static int
gpg_encrypt(fko_ctx_t ctx, const char *enc_key)
{
    int             res;
    char           *plain;
    char           *b64cipher;
    unsigned char  *cipher = NULL;
    size_t          cipher_len;

    /* First make sure we have a recipient key set.
    */
    if(ctx->gpg_recipient == NULL)
        return(FKO_ERROR_MISSING_GPG_KEY_DATA);

    /* Make a bucket big enough to hold the enc msg + digest (plaintext)
     * and populate it appropriately.
    */
    plain = malloc(strlen(ctx->encoded_msg) + strlen(ctx->digest) + 2);
    if(plain == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    snprintf(plain, strlen(ctx->encoded_msg) + strlen(ctx->digest) + 2,
            "%s:%s", ctx->encoded_msg, ctx->digest);

    res = gpgme_encrypt(ctx,
        (unsigned char*)plain, strlen(plain),
        enc_key, &cipher, &cipher_len
    );

    /* --DSS XXX: Better parsing of what went wrong would be nice :)
    */
    if(res != FKO_SUCCESS)
    {
        free(plain);

        if(cipher)
            free(cipher);

        return(res);
    }

    /* Now make a bucket for the base64-encoded version and populate it.
    */
    b64cipher = malloc(((cipher_len / 3) * 4) + 8);
    if(b64cipher == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    b64_encode(cipher, b64cipher, cipher_len);
    strip_b64_eq(b64cipher);

    ctx->encrypted_msg = strdup(b64cipher);

    /* Clean-up
    */
    free(plain);
    free(cipher);
    free(b64cipher);

    if(ctx->encrypted_msg == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    return(FKO_SUCCESS);
}

/* Prep and decrypt using gpgme
*/
static int
gpg_decrypt(fko_ctx_t ctx, const char *dec_key)
{
    char           *tbuf;
    unsigned char  *cipher;
    size_t          cipher_len;
    int             res;

    int             b64_len = strlen(ctx->encrypted_msg);

    /* Now see if we need to add the "hQ" string to the front of the
     * base64-encoded-GPG-encrypted data.
    */
    if(strncmp(ctx->encrypted_msg, B64_GPG_PREFIX, strlen(B64_GPG_PREFIX)))
    {
        /* We need to realloc space for the GPG prefix of hQ.
        */
        tbuf = realloc(ctx->encrypted_msg, b64_len + 12);
        if(tbuf == NULL)
            return(FKO_ERROR_MEMORY_ALLOCATION);

        memmove(tbuf+strlen(B64_GPG_PREFIX), tbuf, b64_len);

        ctx->encrypted_msg = memcpy(tbuf, B64_GPG_PREFIX, strlen(B64_GPG_PREFIX));

        /* Adjust b64_len for added SALT value and Make sure we are still
         * a properly NULL-terminated string (Ubuntu was one system for
         * which this was an issue).
        */
        b64_len += strlen(B64_GPG_PREFIX);
        tbuf[b64_len] = '\0';
    }

    /* Create a bucket for the (base64) decoded encrypted data and get the
     * raw cipher data.
    */
    cipher = malloc(strlen(ctx->encrypted_msg));
    if(cipher == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    if((cipher_len = b64_decode(ctx->encrypted_msg, cipher)) < 0)
        return(FKO_ERROR_INVALID_DATA);

    /* Create a bucket for the plaintext data and decrypt the message
     * data into it.
    */
    /* --DSS Actually, the needed memory will be malloced in the gpgme_decrypt
    //       function. Just leaving this here for reference (for now).
    //ctx->encoded_msg = malloc(cipher_len);
    //if(ctx->encoded_msg == NULL)
    //    return(FKO_ERROR_MEMORY_ALLOCATION);
    */

    res = gpgme_decrypt(ctx, cipher, cipher_len,
        dec_key,  (unsigned char**)&ctx->encoded_msg, &cipher_len
    );

    /* Done with cipher...
    */
    free(cipher);

    if(res != FKO_SUCCESS)
        return(res);

    /* XXX: We could put some kind of sanity check  of the decrypted
     *      data here
    */

    /* Call fko_decode and return the results.
    */
    return(fko_decode_spa_data(ctx));
}

#endif /* HAVE_LIBGPGME */

/* Set the SPA encryption type.
*/
int
fko_set_spa_encryption_type(fko_ctx_t ctx, const short encrypt_type)
{
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    if(encrypt_type < 0 || encrypt_type >= FKO_LAST_ENCRYPTION_TYPE)
        return(FKO_ERROR_INVALID_DATA);

    ctx->encryption_type = encrypt_type;

    ctx->state |= FKO_ENCRYPT_TYPE_MODIFIED;

    return(FKO_SUCCESS);
}

/* Return the SPA encryption type.
*/
int
fko_get_spa_encryption_type(fko_ctx_t ctx, short *enc_type)
{
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    *enc_type = ctx->encryption_type;

    return(FKO_SUCCESS);
}

/* Encrypt the encoded SPA data.
*/
int
fko_encrypt_spa_data(fko_ctx_t ctx, const char *enc_key)
{
    int             res = 0;

    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
    {
        return(FKO_ERROR_CTX_NOT_INITIALIZED);
    }

    /* If there is no encoded data or the SPA data has been modified,
     * go ahead and re-encode here.
    */
    if(ctx->encoded_msg == NULL || FKO_IS_SPA_DATA_MODIFIED(ctx))
        res = fko_encode_spa_data(ctx);

    if(res != FKO_SUCCESS)
        return(res);

    /* Croak on invalid encoded message as well. At present this is a
     * check for a somewhat arbitrary minimum length for the encoded
     * data.
    */
    if(strlen(ctx->encoded_msg) < MIN_SPA_ENCODED_MSG_SIZE)
    {
        return(FKO_ERROR_MISSING_ENCODED_DATA);
    }

    /* Encrypt according to type and return...
    */
    if(ctx->encryption_type == FKO_ENCRYPTION_RIJNDAEL)
        res = _rijndael_encrypt(ctx, enc_key);
    else if(ctx->encryption_type == FKO_ENCRYPTION_GPG)
#if HAVE_LIBGPGME
        res = gpg_encrypt(ctx, enc_key);
#else
        res = FKO_ERROR_UNSUPPORTED_FEATURE;
#endif
    else
        res = FKO_ERROR_INVALID_ENCRYPTION_TYPE;

    return(res);
}

/* Decode, decrypt, and parse SPA data into the context.
*/
int
fko_decrypt_spa_data(fko_ctx_t ctx, const char *dec_key)
{
    int     enc_type, res;

    /* Get the (assumed) type of encryption used. This will also provide
     * some data validation.
    */
    enc_type = fko_encryption_type(ctx->encrypted_msg);

    //strlen(ctx->encrypted_msg) <  MIN_SPA_ENCODED_MSG_SIZE)

    if(enc_type == FKO_ENCRYPTION_GPG)
    {
        ctx->encryption_type = FKO_ENCRYPTION_GPG;
#if HAVE_LIBGPGME
        res = gpg_decrypt(ctx, dec_key);
#else
        res = FKO_ERROR_UNSUPPORTED_FEATURE;
#endif
    }
    else if(enc_type == FKO_ENCRYPTION_RIJNDAEL)
    {
        ctx->encryption_type = FKO_ENCRYPTION_RIJNDAEL;
        res = _rijndael_decrypt(ctx, dec_key);
    }
    else
        return(FKO_ERROR_INVALID_DATA);

    return(res);
}

/* Return the assumed encryption type based on the raw encrypted data.
*/
int
fko_encryption_type(const char *enc_data)
{
    int enc_data_len;

    /* Sanity check the data.
    */
    if(enc_data == NULL)
        return(FKO_ENCRYPTION_INVALID_DATA);

    /* Determine type of encryption used.  For now, we are using the
     * size of the message.
     *
     * XXX: We will want to come up with a more reliable method of
     *      identifying the encryption type.
    */
    enc_data_len = strlen(enc_data);

    if(enc_data_len >= MIN_GNUPG_MSG_SIZE)
        return(FKO_ENCRYPTION_GPG);

    else if(enc_data_len < MIN_GNUPG_MSG_SIZE
      && enc_data_len >= MIN_SPA_ENCODED_MSG_SIZE)
        return(FKO_ENCRYPTION_RIJNDAEL);

    else
        return(FKO_ENCRYPTION_UNKNOWN);
}

/* Set the GPG recipient key name.
*/
int
fko_set_gpg_recipient(fko_ctx_t ctx, const char *recip)
{
#if HAVE_LIBGPGME
    int             res;
    gpgme_key_t     key     = NULL;

    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    if(ctx->encryption_type != FKO_ENCRYPTION_GPG)
        return(FKO_ERROR_WRONG_ENCRYPTION_TYPE);

    ctx->gpg_recipient = strdup(recip);
    if(ctx->gpg_recipient == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    /* Get the key.
    */
    res = get_gpg_key(ctx, &key, 0);
    if(res != FKO_SUCCESS)
    {
        free(ctx->gpg_recipient);
        ctx->gpg_recipient = NULL;
        return(res);
    }

    ctx->recipient_key = key;

    ctx->state |= FKO_DATA_MODIFIED;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

/* Set the GPG home dir.
*/
int
fko_set_gpg_exe(fko_ctx_t ctx, const char *gpg_exe)
{
#if HAVE_LIBGPGME
    struct stat     st;

    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    /* If we are unable to stat the given path/file and determine if it
     * is a regular file or symbolic link, then return with error.
    */
    if(stat(gpg_exe, &st) != 0)
        return(FKO_ERROR_GPGME_BAD_GPG_EXE);

    if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
        return(FKO_ERROR_GPGME_BAD_GPG_EXE);

    ctx->gpg_exe = strdup(gpg_exe);
    if(ctx->gpg_exe == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

/* Get the GPG home dir.
*/
int
fko_get_gpg_exe(fko_ctx_t ctx, char **gpg_exe)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    *gpg_exe = ctx->gpg_exe;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

/* Get the GPG recipient key name.
*/
int
fko_get_gpg_recipient(fko_ctx_t ctx, char **recipient)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    *recipient = ctx->gpg_recipient;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

/* Set the GPG signer key name.
*/
int
fko_set_gpg_signer(fko_ctx_t ctx, const char *signer)
{
#if HAVE_LIBGPGME
    int             res;
    gpgme_key_t     key     = NULL;

    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    if(ctx->encryption_type != FKO_ENCRYPTION_GPG)
        return(FKO_ERROR_WRONG_ENCRYPTION_TYPE);

    ctx->gpg_signer = strdup(signer);
    if(ctx->gpg_signer == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    /* Get the key.
    */
    res = get_gpg_key(ctx, &key, 1);
    if(res != FKO_SUCCESS)
    {
        free(ctx->gpg_signer);
        ctx->gpg_signer = NULL;
        return(res);
    }

    ctx->signer_key = key;

    ctx->state |= FKO_DATA_MODIFIED;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

/* Get the GPG signer key name.
*/
int
fko_get_gpg_signer(fko_ctx_t ctx, char **signer)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    *signer = ctx->gpg_signer;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

/* Set the GPG home dir.
*/
int
fko_set_gpg_home_dir(fko_ctx_t ctx, const char *gpg_home_dir)
{
#if HAVE_LIBGPGME
    struct stat     st;

    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    /* If we are unable to stat the given dir, then return with error.
    */
    if(stat(gpg_home_dir, &st) != 0)
        return(FKO_ERROR_GPGME_BAD_HOME_DIR);

    if(!S_ISDIR(st.st_mode))
        return(FKO_ERROR_GPGME_BAD_HOME_DIR);

    ctx->gpg_home_dir = strdup(gpg_home_dir);
    if(ctx->gpg_home_dir == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

/* Get the GPG home dir.
*/
int
fko_get_gpg_home_dir(fko_ctx_t ctx, char **home_dir)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    *home_dir = ctx->gpg_home_dir;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

int
fko_set_gpg_signature_verify(fko_ctx_t ctx, const unsigned char val)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    ctx->verify_gpg_sigs = (val != 0) ? 1 : 0;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

int
fko_get_gpg_signature_verify(fko_ctx_t ctx, unsigned char *val)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    *val = ctx->verify_gpg_sigs;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

int
fko_set_gpg_ignore_verify_error(fko_ctx_t ctx, const unsigned char val)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    ctx->ignore_gpg_sig_error = (val != 0) ? 1 : 0;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

int
fko_get_gpg_ignore_verify_error(fko_ctx_t ctx, unsigned char *val)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    *val = ctx->ignore_gpg_sig_error;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}


int
fko_get_gpg_signature_fpr(fko_ctx_t ctx, char **fpr)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    /* Must be using GPG encryption.
    */
    if(ctx->encryption_type != FKO_ENCRYPTION_GPG)
        return(FKO_ERROR_WRONG_ENCRYPTION_TYPE);

    /* Make sure we are supposed to verify signatures.
    */
    if(ctx->verify_gpg_sigs == 0)
        return(FKO_ERROR_GPGME_SIGNATURE_VERIFY_DISABLED);

    /* Make sure we have a signature to work with.
    */
    if(ctx->gpg_sigs == NULL)
        return(FKO_ERROR_GPGME_NO_SIGNATURE);

    *fpr = ctx->gpg_sigs->fpr;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

int
fko_get_gpg_signature_id(fko_ctx_t ctx, char **id)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    /* Must be using GPG encryption.
    */
    if(ctx->encryption_type != FKO_ENCRYPTION_GPG)
        return(FKO_ERROR_WRONG_ENCRYPTION_TYPE);

    /* Make sure we are supposed to verify signatures.
    */
    if(ctx->verify_gpg_sigs == 0)
        return(FKO_ERROR_GPGME_SIGNATURE_VERIFY_DISABLED);

    /* Make sure we have a signature to work with.
    */
    if(ctx->gpg_sigs == NULL)
        return(FKO_ERROR_GPGME_NO_SIGNATURE);

    *id = ctx->gpg_sigs->fpr + strlen(ctx->gpg_sigs->fpr) - 8;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

int
fko_get_gpg_signature_summary(fko_ctx_t ctx, int *sigsum)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    /* Must be using GPG encryption.
    */
    if(ctx->encryption_type != FKO_ENCRYPTION_GPG)
        return(FKO_ERROR_WRONG_ENCRYPTION_TYPE);

    /* Make sure we are supposed to verify signatures.
    */
    if(ctx->verify_gpg_sigs == 0)
        return(FKO_ERROR_GPGME_SIGNATURE_VERIFY_DISABLED);

    /* Make sure we have a signature to work with.
    */
    if(ctx->gpg_sigs == NULL)
        return(FKO_ERROR_GPGME_NO_SIGNATURE);

    *sigsum = ctx->gpg_sigs->summary;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

int
fko_get_gpg_signature_status(fko_ctx_t ctx, int *sigstat)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    /* Must be using GPG encryption.
    */
    if(ctx->encryption_type != FKO_ENCRYPTION_GPG)
        return(FKO_ERROR_WRONG_ENCRYPTION_TYPE);

    /* Make sure we are supposed to verify signatures.
    */
    if(ctx->verify_gpg_sigs == 0)
        return(FKO_ERROR_GPGME_SIGNATURE_VERIFY_DISABLED);

    /* Make sure we have a signature to work with.
    */
    if(ctx->gpg_sigs == NULL)
        return(FKO_ERROR_GPGME_NO_SIGNATURE);

    *sigstat = ctx->gpg_sigs->status;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

int
fko_gpg_signature_id_match(fko_ctx_t ctx, const char *id, unsigned char *result)
{
#if HAVE_LIBGPGME
    char *curr_id;

    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    /* Must be using GPG encryption.
    */
    if(ctx->encryption_type != FKO_ENCRYPTION_GPG)
        return(FKO_ERROR_WRONG_ENCRYPTION_TYPE);

    /* Make sure we are supposed to verify signatures.
    */
    if(ctx->verify_gpg_sigs == 0)
        return(FKO_ERROR_GPGME_SIGNATURE_VERIFY_DISABLED);

    /* Make sure we have a signature to work with.
    */
    if(ctx->gpg_sigs == NULL)
        return(FKO_ERROR_GPGME_NO_SIGNATURE);

    fko_get_gpg_signature_id(ctx, &curr_id);

    *result = strcmp(id, curr_id) == 0 ? 1 : 0;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

int
fko_gpg_signature_fpr_match(fko_ctx_t ctx, const char *id, unsigned char *result)
{
#if HAVE_LIBGPGME
    /* Must be initialized
    */
    if(!CTX_INITIALIZED(ctx))
        return(FKO_ERROR_CTX_NOT_INITIALIZED);

    /* Must be using GPG encryption.
    */
    if(ctx->encryption_type != FKO_ENCRYPTION_GPG)
        return(FKO_ERROR_WRONG_ENCRYPTION_TYPE);

    /* Make sure we are supposed to verify signatures.
    */
    if(ctx->verify_gpg_sigs == 0)
        return(FKO_ERROR_GPGME_SIGNATURE_VERIFY_DISABLED);

    /* Make sure we have a signature to work with.
    */
    if(ctx->gpg_sigs == NULL)
        return(FKO_ERROR_GPGME_NO_SIGNATURE);

    *result = strcmp(id, ctx->gpg_sigs->fpr) == 0 ? 1 : 0;

    return(FKO_SUCCESS);
#else
    return(FKO_ERROR_UNSUPPORTED_FEATURE);
#endif  /* HAVE_LIBGPGME */
}

/***EOF***/
