Find file
Fetching contributors…
Cannot retrieve contributors at this time
1151 lines (1030 sloc) 35.4 KB
/*
===========================================================================
Crypt::GCrypt
Perl interface to the GNU Cryptographic library
Author: Alessandro Ranellucci <aar@cpan.org>
Use this software AT YOUR OWN RISK.
===========================================================================
*/
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <gcrypt.h>
#include <string.h>
#ifdef USE_ITHREADS
#ifdef I_PTHREAD
#include <pthread.h>
#define HAVE_PTHREAD
#else
#warning "Perl ithreads not available or not implemented with Pthread: building a non-threadsafe Crypt::GCrypt"
# endif
#endif
static const char my_name[] = "Crypt::GCrypt";
static const char author[] = "Alessandro Ranellucci <aar@cpan.org>";
enum cg_type
{
CG_TYPE_CIPHER,
CG_TYPE_ASYMM,
CG_TYPE_DIGEST
};
enum cg_action
{
CG_ACTION_NONE,
CG_ACTION_ENCRYPT,
CG_ACTION_DECRYPT
};
enum cg_padding
{
CG_PADDING_NONE,
CG_PADDING_STANDARD,
CG_PADDING_NULL,
CG_PADDING_SPACE
};
struct Crypt_GCrypt_s {
int type;
int action;
gcry_cipher_hd_t h;
gcry_ac_handle_t h_ac;
gcry_md_hd_t h_md;
gcry_ac_key_t key_ac;
gcry_error_t err;
int mode;
int padding;
unsigned char *buffer;
STRLEN buflen, blklen, keylen;
int need_to_call_finish;
int buffer_is_decrypted;
};
typedef struct Crypt_GCrypt_s *Crypt_GCrypt;
/* a Crypt_GCrypt_MPI need only be a pointer to a gcrypt MPI object */
typedef gcry_mpi_t Crypt_GCrypt_MPI;
/* return the offset of padding or -1 if none */
int find_padding (Crypt_GCrypt gcr, unsigned char *string, size_t string_len) {
unsigned char last_char = string[string_len-1];
size_t i, offset;
void *p;
switch (gcr->padding) {
case CG_PADDING_STANDARD:
/* padding length is last_char */
for (i = 1; i <= last_char; ++i) {
if (string[string_len-i] != last_char) return -1;
}
return string_len-last_char;
case CG_PADDING_NULL:
p = memchr((char *) string, '\0', string_len);
if (p == NULL) return -1;
offset = (int) p - (int) string;
for (i = offset; i < string_len; ++i) {
if (string[string_len-i] != '\0') return -1;
}
return offset;
case CG_PADDING_SPACE:
p = memchr((char *) string, '\32', string_len);
if (p == NULL) return -1;
offset = (int) p - (int) string;
for (i = offset; i < string_len; ++i) {
if (string[string_len-i] != '\32') return -1;
}
return offset;
}
return -1;
}
Crypt_GCrypt_MPI
dereference_gcm(SV* sv_gcm) {
if (!sv_derived_from(sv_gcm, "Crypt::GCrypt::MPI"))
croak("Not a Crypt::GCrypt::MPI object");
IV tmp = SvIV((SV*)SvRV(sv_gcm));
return INT2PTR(Crypt_GCrypt_MPI, tmp);
}
#ifdef HAVE_PTHREAD
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif
void
init_library() {
gcry_error_t ret;
if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
/* we just need to make sure that the right version is available */
if (!gcry_check_version(GCRYPT_VERSION))
croak("libgcrypt version mismatch (needed: %s)", GCRYPT_VERSION);
return;
}
/* else, we need to go ahead with the full initialization: */
#ifdef HAVE_PTHREAD
ret = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
if (gcry_err_code(ret) != GPG_ERR_NO_ERROR)
croak("could not initialize libgcrypt for threads (%d: %s/%s)",
gcry_err_code(ret),
gcry_strsource(ret),
gcry_strerror(ret));
#endif
if (!gcry_check_version(GCRYPT_VERSION))
croak("libgcrypt version mismatch (needed: %s)", GCRYPT_VERSION);
/* Why do it this way? see
/usr/share/doc/libgcrypt11-doc/html/Initializing-the-library.html#sample-use-suspend-secmem
*/
/* We don't want to see any warnings, e.g. because we have not yet
parsed program options which might be used to suppress such
warnings. */
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
/* Allocate a pool of 32k secure memory. This make the secure memory
available and also drops privileges where needed.
This mirrors changes made in libgcrypt 1.4.3, to auto-initialize
the library with 32KB of secure memory if no other initialization
has been done.
FIXME: we should probably allow the user to choose how much
secure RAM to use something like this:
use Crypt::GCrypt { secmem => 1024*1024 };
*/
gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
/* It is now okay to let Libgcrypt complain when there was/is
a problem with the secure memory. */
gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED);
}
MODULE = Crypt::GCrypt PACKAGE = Crypt::GCrypt PREFIX = cg_
SV *
cg_built_against_version()
CODE:
init_library();
RETVAL = newSVpvn(GCRYPT_VERSION, strlen(GCRYPT_VERSION));
OUTPUT:
RETVAL
SV *
cg_gcrypt_version()
INIT:
const char * v;
CODE:
init_library();
v = gcry_check_version(NULL);
RETVAL = newSVpvn(v, strlen(v));
OUTPUT:
RETVAL
Crypt_GCrypt
cg_new(...)
PROTOTYPE: @
INIT:
char *s, *algo_s, *mode_s, *key_s;
int i, algo, mode;
unsigned int c_flags, ac_flags, md_flags;
gcry_ac_id_t ac_algo;
bool have_mode;
CODE:
New(0, RETVAL, 1, struct Crypt_GCrypt_s);
s = SvPV_nolen(ST(0));
if (strcmp(s, "Crypt::GCrypt") == 0) {
i = 1;
} else {
i = 0;
}
if ((items-i % 2) == 1)
croak("Wrong number of arguments for Crypt::GCrypt->new()");
/* Default values: */
RETVAL->type = -1;
RETVAL->padding = CG_PADDING_STANDARD;
RETVAL->action = CG_ACTION_NONE;
RETVAL->need_to_call_finish = 0;
RETVAL->buffer_is_decrypted = 0;
RETVAL->buffer = NULL;
c_flags = 0;
ac_flags = 0;
md_flags = 0;
have_mode = 0;
/* Let's get parameters: */
while (i < items) {
s = SvPV_nolen(ST(i));
if (strcmp(s, "type") == 0) {
s = SvPV_nolen(ST(i+1));
if (strcmp(s, "cipher") == 0)
RETVAL->type = CG_TYPE_CIPHER;
if (strcmp(s, "asymm") == 0)
RETVAL->type = CG_TYPE_ASYMM;
if (strcmp(s, "digest") == 0)
RETVAL->type = CG_TYPE_DIGEST;
}
if (strcmp(s, "algorithm") == 0) {
algo_s = SvPV_nolen(ST(i+1));
}
if (strcmp(s, "mode") == 0) {
mode_s = SvPV_nolen(ST(i+1));
have_mode = 1;
}
if (strcmp(s, "padding") == 0) {
s = SvPV_nolen(ST(i+1));
if (strcmp(s, "none") == 0)
RETVAL->padding = CG_PADDING_NONE;
if (strcmp(s, "standard") == 0)
RETVAL->padding = CG_PADDING_STANDARD;
if (strcmp(s, "null") == 0)
RETVAL->padding = CG_PADDING_NULL;
}
if (strcmp(s, "secure") == 0) {
if (SvTRUE(ST(i+1))) {
c_flags |= GCRY_CIPHER_SECURE;
md_flags |= GCRY_MD_FLAG_SECURE;
}
}
if (strcmp(s, "hmac") == 0) {
key_s = SvPV(ST(i+1), RETVAL->keylen);
md_flags |= GCRY_MD_FLAG_HMAC;
}
if (strcmp(s, "enable_sync") == 0) {
if (SvTRUE(ST(i+1))) c_flags |= GCRY_CIPHER_ENABLE_SYNC;
}
i = i + 2;
}
if (RETVAL->type == -1)
croak("No valid type specified for Crypt::GCrypt->new()");
if (!algo_s)
croak("No algorithm specified for Crypt::GCrypt->new()");
init_library();
if (RETVAL->type == CG_TYPE_CIPHER) {
/* Checking algorithm */
if (!(algo = gcry_cipher_map_name(algo_s)))
croak("Unknown cipher algorithm %s", algo_s);
RETVAL->blklen = gcry_cipher_get_algo_blklen(algo);
RETVAL->keylen = gcry_cipher_get_algo_keylen(algo);
/* Checking mode */
if (have_mode) {
switch (mode_s[0]) {
case 'e':
if (strcmp(mode_s+1, "cb") == 0)
RETVAL->mode = GCRY_CIPHER_MODE_ECB;
break;
case 'c':
if (strcmp(mode_s+1, "fb") == 0)
RETVAL->mode = GCRY_CIPHER_MODE_CFB;
else if (strcmp(mode_s+1, "bc") == 0)
RETVAL->mode = GCRY_CIPHER_MODE_CBC;
break;
case 's':
if (strcmp(mode_s+1, "tream") == 0)
RETVAL->mode = GCRY_CIPHER_MODE_STREAM;
break;
case 'o':
if (strcmp(mode_s+1, "fb") == 0)
RETVAL->mode = GCRY_CIPHER_MODE_OFB;
break;
}
} else {
RETVAL->mode = RETVAL->blklen > 1 ? GCRY_CIPHER_MODE_CBC
: GCRY_CIPHER_MODE_STREAM;
}
if (!RETVAL->mode)
croak("Unknown mode %s", mode_s);
/* Init cipher */
RETVAL->err = gcry_cipher_open(&RETVAL->h, algo, RETVAL->mode, c_flags);
if (RETVAL->h == NULL) XSRETURN_UNDEF;
}
if (RETVAL->type == CG_TYPE_DIGEST) {
if (!(algo = gcry_md_map_name(algo_s)))
croak("Unknown digest algorithm %s", algo_s);
RETVAL->err = gcry_md_open(&RETVAL->h_md, algo, md_flags);
if (RETVAL->h_md == NULL) XSRETURN_UNDEF;
if (md_flags & GCRY_MD_FLAG_HMAC) {
/* what if this overwrites the earlier error value? */
RETVAL->err = gcry_md_setkey(RETVAL->h_md, key_s, RETVAL->keylen);
}
}
if (RETVAL->type == CG_TYPE_ASYMM) {
croak("Asymmetric cryptography is not yet supported by Crypt::GCrypt");
RETVAL->err = gcry_ac_name_to_id(algo_s, &ac_algo);
if (RETVAL->err)
croak("Unknown algorithm %s", algo_s);
/* Init ac */
RETVAL->err = gcry_ac_open(&RETVAL->h_ac, ac_algo, ac_flags);
if (RETVAL->h_ac == NULL) XSRETURN_UNDEF;
}
OUTPUT:
RETVAL
SV *
cg_encrypt(gcr, in)
Crypt_GCrypt gcr;
SV *in;
PREINIT:
char *ibuf, *curbuf, *obuf;
size_t len, ilen, buflen;
CODE:
if (gcr->action != CG_ACTION_ENCRYPT)
croak("start('encrypting') was not called");
ibuf = SvPV(in, ilen);
if (gcr->padding == CG_PADDING_NONE && ilen % gcr->blklen > 0)
croak("'None' padding requires that input to ->encrypt() is supplied as a multiple of blklen");
/* Get total buffer+ibuf length */
Newz(0, curbuf, ilen + gcr->buflen, char);
memcpy(curbuf, gcr->buffer, gcr->buflen);
memcpy(curbuf+gcr->buflen, ibuf, ilen);
if ((len = (ilen+gcr->buflen) % gcr->blklen) == 0) {
len = ilen+gcr->buflen;
gcr->buffer[0] = '\0';
gcr->buflen = 0;
} else {
char *tmpbuf;
len = (ilen+gcr->buflen) - len; /* len contiene i byte da scrivere effettivemente */
Newz(0, tmpbuf, len, char);
memcpy(tmpbuf, curbuf, len);
memcpy(gcr->buffer, curbuf+len, (ilen+gcr->buflen)-len);
gcr->buflen = (ilen+gcr->buflen)-len;
Safefree(curbuf);
curbuf = tmpbuf;
}
/* Encrypt data */
New(0, obuf, len, char);
if (len > 0) {
if ((gcr->err = gcry_cipher_encrypt(gcr->h, obuf, len, curbuf, len)) != 0)
croak("encrypt: %s", gcry_strerror(gcr->err));
}
RETVAL = newSVpvn(obuf, len);
Safefree(curbuf);
Safefree(obuf);
OUTPUT:
RETVAL
SV *
cg_finish(gcr)
Crypt_GCrypt gcr;
PREINIT:
char *obuf;
size_t rlen, return_len, padding_length;
CODE:
if (gcr->type != CG_TYPE_CIPHER)
croak("Can't call finish when doing non-cipher operations");
gcr->need_to_call_finish = 0;
if (gcr->action == CG_ACTION_ENCRYPT) {
if (gcr->buflen < gcr->blklen) {
unsigned char *tmpbuf;
rlen = gcr->blklen - gcr->buflen;
Newz(0, tmpbuf, gcr->buflen+rlen, unsigned char);
memcpy(tmpbuf, gcr->buffer, gcr->buflen);
switch (gcr->padding) {
case CG_PADDING_STANDARD:
memset(tmpbuf + gcr->buflen, rlen, rlen);
break;
case CG_PADDING_NULL:
memset(tmpbuf + gcr->buflen, 0, rlen);
break;
case CG_PADDING_SPACE:
memset(tmpbuf + gcr->buflen, '\32', rlen);
break;
}
Safefree(gcr->buffer);
gcr->buffer = tmpbuf;
} else {
if (gcr->padding == CG_PADDING_NULL && gcr->blklen == 8) {
unsigned char *tmpbuf;
Newz(0, tmpbuf, gcr->buflen+8, unsigned char);
memcpy(tmpbuf, gcr->buffer, gcr->buflen);
memset(tmpbuf + gcr->buflen, 0, 8);
Safefree(gcr->buffer);
gcr->buffer = tmpbuf;
}
}
Newz(0, obuf, gcr->blklen, char);
if ((gcr->err = gcry_cipher_encrypt(gcr->h, obuf, gcr->blklen, gcr->buffer, gcr->blklen)) != 0)
croak("encrypt: %s", gcry_strerror(gcr->err));
gcr->buffer[0] = '\0';
gcr->buflen = 0;
RETVAL = newSVpvn(obuf, gcr->blklen);
Safefree(obuf);
} else { /* CG_ACTION_DECRYPT */
/* decrypt remaining ciphertext if any */
New(0, obuf, gcr->buflen, char);
return_len = gcr->buflen;
if (gcr->buflen > 0) {
if (gcr->buffer_is_decrypted == 1) {
Move(gcr->buffer, obuf, gcr->buflen, char);
} else {
if ((gcr->err = gcry_cipher_decrypt(gcr->h, obuf, return_len, gcr->buffer, gcr->buflen)) != 0)
croak("decrypt: %s", gcry_strerror(gcr->err));
}
gcr->buffer[0] = '\0';
gcr->buflen = 0;
/* Remove padding */
return_len = find_padding(gcr, (unsigned char *) obuf, return_len);
}
RETVAL = newSVpvn(obuf, return_len);
Safefree(obuf);
}
OUTPUT:
RETVAL
SV *
cg_decrypt(gcr, in)
Crypt_GCrypt gcr;
SV *in;
PREINIT:
unsigned char *ibuf, *obuf, *ciphertext, *decrypted_buffer;
size_t total_len, len, ilen;
int ciphertext_offset;
CODE:
if (gcr->action != CG_ACTION_DECRYPT)
croak("start('decrypting') was not called");
ibuf = (unsigned char *) SvPV(in, ilen);
if ((ilen % gcr->blklen) > 0 || ilen == 0)
croak("input must be a multiple of blklen");
/* Concatenate buffer and input to get total length of ciphertext */
total_len = gcr->buflen + ilen; /* total_len is a multiple of blklen */
Newz(0, ciphertext, total_len, unsigned char);
Move(gcr->buffer, ciphertext, gcr->buflen, unsigned char);
Move(ibuf, ciphertext+gcr->buflen, ilen, unsigned char);
/* if our buffer was decrypted by the previous run of this method,
we set a ciphertext_offset to avoid re-decrypting such plaintext
coming from the buffer */
ciphertext_offset = (gcr->buffer_is_decrypted == 1) ? gcr->buflen : 0;
/* strip last block and move it to buffer */
len = total_len - gcr->blklen; /* len is the length of plaintext we're returning */
Move(ciphertext+len, gcr->buffer, (total_len - len), unsigned char);
gcr->buflen = gcr->blklen;
/* do actual decryption */
New(0, obuf, len, unsigned char);
Copy(ciphertext, obuf, ciphertext_offset, unsigned char);
if (len-ciphertext_offset > 0) { /* that is, if we have something to decrypt */
if ((gcr->err = gcry_cipher_decrypt(gcr->h, obuf+ciphertext_offset, len-ciphertext_offset, ciphertext+ciphertext_offset, len-ciphertext_offset)) != 0)
croak("decrypt: %s", gcry_strerror(gcr->err));
}
Safefree(ciphertext);
/* OPTIMIZATION for compatibility with implementations of Crypt::GCrypt <= 1.17:
decrypt buffer and check if it seems padded */
if ((gcr->err = gcry_cipher_decrypt(gcr->h, gcr->buffer, gcr->buflen, NULL, 0)) != 0) /* in-place decryption */
croak("decrypt: %s", gcry_strerror(gcr->err));
gcr->buffer_is_decrypted = 1;
if (find_padding(gcr, gcr->buffer, gcr->buflen) == -1) {
/* if the string doesn't appear to be padded, let's append it to the
output so that users who don't call ->finish() don't break their applications */
Renew(obuf, len + gcr->buflen, unsigned char);
Move(gcr->buffer, obuf+len, gcr->buflen, unsigned char);
len = len + gcr->buflen;
gcr->buffer[0] = '\0';
gcr->buflen = 0;
gcr->buffer_is_decrypted = 0;
}
RETVAL = newSVpvn((char *) obuf, len);
Safefree(obuf);
OUTPUT:
RETVAL
SV *
cg_sign(gcr, in)
Crypt_GCrypt gcr;
SV *in;
PREINIT:
gcry_mpi_t in_mpi, out_mpi;
gcry_ac_data_t outdata;
size_t len;
const void *inbuf;
const char *label;
char* outbuf;
CODE:
/*
in_mpi = gcry_mpi_new(0);
out_mpi = gcry_mpi_new(0);
inbuf = SvPV(in, len);
printf("inbuf: %s\n", inbuf);
gcry_mpi_scan( &in_mpi, GCRYMPI_FMT_STD, inbuf, strlen(inbuf), NULL );
printf("Key: %s\n", gcr->key_ac);
gcr->err = gcry_ac_data_sign(gcr->h_ac, gcr->key_ac, in_mpi, &outdata);
if (gcr->err) {
croak( gcry_strerror(gcr->err) );
}
printf("Here\n");
gcr->err = gcry_ac_data_get_index (outdata, 0, 0, &label, &out_mpi);
printf("Before (%s)\n", label);
gcry_mpi_print(GCRYMPI_FMT_STD, outbuf, 1024, NULL, out_mpi);
printf("After\n");
RETVAL = newSVpv(outbuf, 0);
*/
OUTPUT:
RETVAL
void
cg_start(gcr, act)
Crypt_GCrypt gcr;
SV *act;
PREINIT:
char *action;
size_t len;
CODE:
gcr->err = gcry_cipher_reset(gcr->h);
Safefree(gcr->buffer);
New(0, gcr->buffer, gcr->blklen, unsigned char);
gcr->buflen = 0;
gcr->need_to_call_finish = 1;
action = SvPV(act, len);
switch (action[0]) {
case 'e':
gcr->action = CG_ACTION_ENCRYPT;
break;
case 'd':
gcr->action = CG_ACTION_DECRYPT;
break;
}
void
cg_setkey(gcr, ...)
Crypt_GCrypt gcr;
PREINIT:
char *k, *s;
char *mykey, *buf;
gcry_ac_key_type_t keytype;
gcry_ac_data_t keydata;
gcry_mpi_t mpi;
size_t len;
CODE:
/* Set key for cipher */
if (gcr->type == CG_TYPE_CIPHER) {
buf = NULL;
mykey = SvPV(ST(1), len);
/* If key is shorter than our algorithm's key size
let's pad it with zeroes */
if (len < gcr->keylen) {
Newz(0, buf, gcr->keylen, char);
memcpy(buf, mykey, len);
mykey = buf;
}
gcr->err = gcry_cipher_setkey(gcr->h, mykey, gcr->keylen);
if (gcr->err != 0) croak("setkey: %s", gcry_strerror(gcr->err));
Safefree(buf);
}
/* Set key for asymmetric criptography */
if (gcr->type == CG_TYPE_ASYMM) {
k = SvPV(ST(2), len);
/* Key type */
keytype = -1;
s = SvPV(ST(1), len);
if (strcmp(s, "private") == 0) keytype = GCRY_AC_KEY_SECRET;
if (strcmp(s, "public") == 0) keytype = GCRY_AC_KEY_PUBLIC;
if (keytype == -1)
croak("Key must be private or public");
gcry_control(GCRYCTL_INIT_SECMEM, strlen(k));
mpi = gcry_mpi_snew(0);
/* gcry_mpi_scan( &mpi, GCRYMPI_FMT_STD, k, NULL, NULL ); */
gcr->err = gcry_ac_data_new(&keydata);
gcr->err = gcry_ac_data_set(keydata, GCRY_AC_FLAG_COPY, "s", mpi);
gcr->err = gcry_ac_key_init(&gcr->key_ac, gcr->h_ac, keytype, keydata);
}
void
cg_setiv(gcr, ...)
Crypt_GCrypt gcr;
PREINIT:
char *buf, *param;
size_t len;
CODE:
buf = NULL;
if (gcr->type != CG_TYPE_CIPHER)
croak("Can't call setiv when doing non-cipher operations");
if (items == 2) {
param = SvPV(ST(1), len);
if (len < gcr->blklen) {
Newz(0, buf, gcr->blklen, char);
memcpy(buf, param, len);
param = buf;
}
} else if (items == 1) {
Newz(0, buf, gcr->blklen, char);
param = buf;
} else
croak("Usage: $cipher->setiv([iv])");
gcry_cipher_setiv(gcr->h, param, gcr->blklen);
Safefree(buf);
void
cg_sync(gcr)
Crypt_GCrypt gcr;
CODE:
if (gcr->type != CG_TYPE_CIPHER)
croak("Can't call sync when doing non-cipher operations");
gcry_cipher_sync(gcr->h);
int
cg_keylen(gcr)
Crypt_GCrypt gcr;
CODE:
if (gcr->type != CG_TYPE_CIPHER)
croak("Can't call keylen when doing non-cipher operations");
RETVAL = gcr->keylen;
OUTPUT:
RETVAL
int
cg_blklen(gcr)
Crypt_GCrypt gcr;
CODE:
if (gcr->type != CG_TYPE_CIPHER)
croak("Can't call blklen when doing non-cipher operations");
RETVAL = gcr->blklen;
OUTPUT:
RETVAL
void
cg_reset(gcr)
Crypt_GCrypt gcr;
CODE:
if (gcr->type != CG_TYPE_DIGEST)
croak("Can't call reset when doing non-digest operations");
gcry_md_reset(gcr->h_md);
void
cg_write(gcr, in)
Crypt_GCrypt gcr;
SV *in;
PREINIT:
char *ibuf;
size_t ilen;
CODE:
if (gcr->type != CG_TYPE_DIGEST)
croak("Can't call write when doing non-digest operations.");
ibuf = SvPV(in, ilen);
gcry_md_write(gcr->h_md, ibuf, ilen);
SV *
cg_read(gcr)
Crypt_GCrypt gcr;
PREINIT:
unsigned char *output;
size_t len;
CODE:
if (gcr->type != CG_TYPE_DIGEST)
croak("Can't call read when doing non-digest operations.");
output = gcry_md_read(gcr->h_md, 0);
len = gcry_md_get_algo_dlen(gcry_md_get_algo(gcr->h_md));
RETVAL = newSVpvn((const char *) output, len);
OUTPUT:
RETVAL
int
cg_digest_length(gcr)
Crypt_GCrypt gcr;
CODE:
if (gcr->type != CG_TYPE_DIGEST)
croak("Can't call digest_length when doing non-digest operations");
RETVAL = gcry_md_get_algo_dlen(gcry_md_get_algo(gcr->h_md));
OUTPUT:
RETVAL
Crypt_GCrypt
cg_clone(gcr)
Crypt_GCrypt gcr;
CODE:
if (gcr->type != CG_TYPE_DIGEST)
croak("Crypt::GCrypt::clone() is only currently defined for digest objects");
New(0, RETVAL, 1, struct Crypt_GCrypt_s);
Copy(gcr, RETVAL, 1, struct Crypt_GCrypt_s);
/* if we allow clone() for cipher objects, we should duplicate the buffer */
RETVAL->err = gcry_md_copy(&RETVAL->h_md, gcr->h_md);
if (RETVAL->h_md == NULL) XSRETURN_UNDEF;
OUTPUT:
RETVAL
int
cg_digest_algo_available(algo)
SV *algo;
PREINIT:
const char *algo_s;
int algo_id;
CODE:
algo_s = SvPV_nolen(algo);
init_library();
algo_id = gcry_md_map_name(algo_s);
if (algo_id) {
if (gcry_md_test_algo(algo_id))
RETVAL = 0;
else
RETVAL = 1;
} else {
RETVAL = 0;
}
OUTPUT:
RETVAL
int
cg_cipher_algo_available(algo)
SV *algo;
PREINIT:
const char *algo_s;
int algo_id;
CODE:
algo_s = SvPV_nolen(algo);
init_library();
algo_id = gcry_cipher_map_name(algo_s);
if (algo_id) {
if (gcry_cipher_algo_info(algo_id, GCRYCTL_TEST_ALGO, 0, 0))
RETVAL = 0;
else
RETVAL = 1;
} else {
RETVAL = 0;
}
OUTPUT:
RETVAL
void
cg_DESTROY(gcr)
Crypt_GCrypt gcr;
CODE:
if (gcr->type == CG_TYPE_CIPHER) gcry_cipher_close(gcr->h);
if (gcr->type == CG_TYPE_ASYMM) gcry_ac_close(gcr->h_ac);
if (gcr->type == CG_TYPE_DIGEST) gcry_md_close(gcr->h_md);
if (gcr->need_to_call_finish == 1)
warn("WARNING: the ->finish() method was not called after encryption/decryption.");
Safefree(gcr->buffer);
Safefree(gcr);
MODULE = Crypt::GCrypt PACKAGE = Crypt::GCrypt::MPI PREFIX = cgm_
BOOT:
{ /* found this method of storing constants in
http://blogs.sun.com/akolb/entry/pitfals_of_the_perl_xs */
HV *stash;
stash = gv_stashpv("Crypt::GCrypt::MPI", TRUE);
newCONSTSUB(stash, "FMT_STD", newSViv(GCRYMPI_FMT_STD));
newCONSTSUB(stash, "FMT_PGP", newSViv(GCRYMPI_FMT_PGP));
newCONSTSUB(stash, "FMT_SSH", newSViv(GCRYMPI_FMT_SSH));
newCONSTSUB(stash, "FMT_HEX", newSViv(GCRYMPI_FMT_HEX));
newCONSTSUB(stash, "FMT_USG", newSViv(GCRYMPI_FMT_USG));
}
Crypt_GCrypt_MPI
cgm_new(...)
PROTOTYPE: @
PREINIT:
char *s;
int i, valix, secure, set, format;
Crypt_GCrypt_MPI src;
size_t len;
gcry_error_t err;
CODE:
RETVAL = NULL;
secure = 0;
valix = -1;
format = GCRYMPI_FMT_STD;
set = 0;
s = SvPV_nolen(ST(0));
if (strcmp(s, "Crypt::GCrypt::MPI") == 0) {
i = 1;
} else {
i = 0;
}
if ((items-i > 1) && ((items-i % 2) == 1))
croak("Wrong number of arguments for Crypt::GCrypt::MPI->new()");
if (items-i == 1) {
/* this is the value */
valix = i;
} else {
/* this is a parameterized list */
while (i < items) {
s = SvPV_nolen(ST(i));
if (strcmp(s, "secure") == 0)
if (SvTRUE(ST(i+1)))
secure = 1;
if (strcmp(s, "format") == 0)
format = SvIV(ST((i+1)));
if (strcmp(s, "value") == 0)
valix = i + 1;
i += 2;
}
}
/* we're copying an mpi: */
if ((valix >= 0) && sv_derived_from(ST(valix), "Crypt::GCrypt::MPI")) {
IV tmp = SvIV((SV*)SvRV(ST(valix))); /* found this incantation at */
src = INT2PTR(Crypt_GCrypt_MPI,tmp); /* http://cpansearch.perl.org/src/DSTH/Math-GSL-Linalg-SVD-0.0.2/SVD.c */
if (secure && ! gcry_mpi_get_flag(src, GCRYMPI_FLAG_SECURE)) {
/* we were asked for a secure MPI, but we were given an insecure one to copy */
RETVAL=gcry_mpi_snew(gcry_mpi_get_nbits(src));
if (RETVAL == NULL) XSRETURN_UNDEF;
gcry_mpi_set(RETVAL, src);
} else {
/* this will give us a secure MPI if a secure MPI was passed in, even if we
also got secure => 0 ; a bit weird, but on the safe side. */
RETVAL=gcry_mpi_copy(src);
}
} else {
if (secure) {
RETVAL=gcry_mpi_snew(0);
} else {
RETVAL=gcry_mpi_new(0);
}
if (RETVAL == NULL) XSRETURN_UNDEF;
/* do something with the info at valix */
if (valix >= 0) {
switch(SvTYPE(ST(valix))) {
case SVt_PVIV:
/* if negative value, we have to jump through some hoops: */
if (SvIV(ST(valix)) < 0) {
RETVAL=gcry_mpi_set_ui(NULL, 0);
gcry_mpi_sub_ui(RETVAL, RETVAL, -1*(SvIV(ST(valix))));
} else {
/* this can be dealt with by regular unsigned ints */
gcry_mpi_set_ui(RETVAL, SvIV(ST(valix)));
}
break;
case SVt_PV:
/* handle bytestrings
*/
s = SvPV(ST(valix), len);
src = NULL;
err = gcry_mpi_scan(&src, format, s, ((format == GCRYMPI_FMT_HEX) ? 0 : len), NULL);
if (err != 0) croak("Crypt::GCrypt::MPI::new (from string, with format %d) libgcrypt internal failure %s", format, gcry_strerror(err));
/* FIXME: can we "eat" the head of the the input string if format is FMT_PGP or FMT_SSH ? */
/* avoid memory leak: */
if (secure) {
gcry_mpi_set(RETVAL, src);
gcry_mpi_release(src);
} else {
gcry_mpi_release(RETVAL);
RETVAL = src;
}
break;
default:
croak("value argument for Crypt::GCrypt::MPI->new() must currently be either an int or another Crypt::GCrypt::MPI (%d, %d, %d)", SvTYPE(ST(valix)), valix, format);
break;
}
}
}
OUTPUT:
RETVAL
void
cgm_swap(sv_gcma, gcmb)
SV* sv_gcma;
Crypt_GCrypt_MPI gcmb;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_swap(gcma, gcmb);
ST(0) = sv_gcma;
XSRETURN(1);
void
cgm_set(sv_gcma, gcmb)
SV* sv_gcma;
Crypt_GCrypt_MPI gcmb;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_set(gcma, gcmb);
ST(0) = sv_gcma;
XSRETURN(1);
bool
cgm_is_secure(gcm)
Crypt_GCrypt_MPI gcm;
CODE:
RETVAL=gcry_mpi_get_flag(gcm, GCRYMPI_FLAG_SECURE);
OUTPUT:
RETVAL
int
cgm_cmp(gcma, gcmb)
Crypt_GCrypt_MPI gcma;
Crypt_GCrypt_MPI gcmb;
CODE:
RETVAL=gcry_mpi_cmp(gcma, gcmb);
OUTPUT:
RETVAL
Crypt_GCrypt_MPI
cgm_copy(gcm)
Crypt_GCrypt_MPI gcm;
CODE:
RETVAL=gcry_mpi_copy(gcm);
OUTPUT:
RETVAL
void
cgm_add(sv_gcma, gcmb)
SV* sv_gcma;
Crypt_GCrypt_MPI gcmb;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_add(gcma, gcma, gcmb);
ST(0) = sv_gcma;
XSRETURN(1);
void
cgm_addm(sv_gcma, gcmb, gcmm)
SV* sv_gcma;
Crypt_GCrypt_MPI gcmb;
Crypt_GCrypt_MPI gcmm;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_addm(gcma, gcma, gcmb, gcmm);
ST(0) = sv_gcma;
XSRETURN(1);
void
cgm_sub(sv_gcma, gcmb)
SV* sv_gcma;
Crypt_GCrypt_MPI gcmb;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_sub(gcma, gcma, gcmb);
ST(0) = sv_gcma;
XSRETURN(1);
void
cgm_subm(sv_gcma, gcmb, gcmm)
SV* sv_gcma;
Crypt_GCrypt_MPI gcmb;
Crypt_GCrypt_MPI gcmm;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_subm(gcma, gcma, gcmb, gcmm);
ST(0) = sv_gcma;
XSRETURN(1);
void
cgm_mul(sv_gcma, gcmb)
SV* sv_gcma;
Crypt_GCrypt_MPI gcmb;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_mul(gcma, gcma, gcmb);
ST(0) = sv_gcma;
XSRETURN(1);
void
cgm_mulm(sv_gcma, gcmb, gcmm)
SV* sv_gcma;
Crypt_GCrypt_MPI gcmb;
Crypt_GCrypt_MPI gcmm;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_mulm(gcma, gcma, gcmb, gcmm);
ST(0) = sv_gcma;
XSRETURN(1);
void
cgm_mul_2exp(sv_gcm, e)
SV* sv_gcm;
int e;
PPCODE:
Crypt_GCrypt_MPI gcm = dereference_gcm(sv_gcm);
if (e >= 0) {
/* this can be dealt with by regular unsigned ints */
gcry_mpi_mul_2exp(gcm, gcm, e);
} else {
croak("exponent argument for Crypt::GCrypt::MPI::mul_2exp() must be an unsigned integer");
}
ST(0) = sv_gcm;
XSRETURN(1);
void
cgm_div(sv_gcma, gcmb)
SV* sv_gcma;
Crypt_GCrypt_MPI gcmb;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_div(gcma, NULL, gcma, gcmb, 0);
ST(0) = sv_gcma;
/* FIXME: should we return the remainder as well, if called in a list context? */
XSRETURN(1);
void
cgm_mod(sv_gcma, gcmb)
SV* sv_gcma;
Crypt_GCrypt_MPI gcmb;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_mod(gcma, gcma, gcmb);
ST(0) = sv_gcma;
XSRETURN(1);
void
cgm_powm(sv_gcma, gcme, gcmm)
SV* sv_gcma;
Crypt_GCrypt_MPI gcme;
Crypt_GCrypt_MPI gcmm;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_powm(gcma, gcma, gcme, gcmm);
ST(0) = sv_gcma;
XSRETURN(1);
void
cgm_invm(sv_gcma, gcmb)
SV* sv_gcma;
Crypt_GCrypt_MPI gcmb;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_invm(gcma, gcma, gcmb);
ST(0) = sv_gcma;
/* FIXME: should we do anything with the return value (1 if invm actually exists)? */
XSRETURN(1);
void
cgm_gcd(sv_gcma, gcmb)
SV* sv_gcma;
Crypt_GCrypt_MPI gcmb;
PPCODE:
Crypt_GCrypt_MPI gcma = dereference_gcm(sv_gcma);
gcry_mpi_gcd(gcma, gcma, gcmb);
ST(0) = sv_gcma;
XSRETURN(1);
bool
cgm_mutually_prime(gcma, gcmb)
Crypt_GCrypt_MPI gcma;
Crypt_GCrypt_MPI gcmb;
PREINIT:
Crypt_GCrypt_MPI gcd;
CODE:
gcd = gcry_mpi_new(0);
RETVAL=gcry_mpi_gcd(gcd, gcma, gcmb);
gcry_mpi_release(gcd);
OUTPUT:
RETVAL
void
cgm_dump(sv_gcm)
SV* sv_gcm;
PPCODE:
Crypt_GCrypt_MPI gcm = dereference_gcm(sv_gcm);
gcry_mpi_dump(gcm);
ST(0) = sv_gcm;
XSRETURN(1);
SV *
cgm_print(gcm, format)
Crypt_GCrypt_MPI gcm;
int format;
PREINIT:
size_t size;
unsigned char* buf;
gcry_error_t err;
CODE:
/* FIXME: make format default to FMT_STD somehow (how do we not require a parameter?) */
err = gcry_mpi_print(format, NULL, 0, &size, gcm);
if (err != 0) croak("GCrypt::MPI::print start: %s", gcry_strerror(err));
/* FMT_HEX asks for an extra byte for the null char, but perl allocates that
already, so we treat that case special */
RETVAL = newSVpv("", ((format == GCRYMPI_FMT_HEX) ? size-1 : size));
buf = (unsigned char*) SvPV_nolen(RETVAL);
err = gcry_mpi_print(format, buf, size, &size, gcm);
if (err != 0) croak("GCrypt::MPI::print finish: %s", gcry_strerror(err));
OUTPUT:
RETVAL
void
cgm_DESTROY(gmpi)
Crypt_GCrypt_MPI gmpi;
CODE:
gcry_mpi_release(gmpi);
gmpi = NULL;