#include <ctype.h>
#include <string.h>
-/*
- * PEM looks like:
- *
- * -----BEGIN RSA PRIVATE KEY-----
- * Proc-Type: 4,ENCRYPTED
- * DEK-Info: DES-EDE3-CBC,704CFFD62FBA03E9
- *
- * 4AV/g0BiTeb07hzo4/Ct47HGhHEshMhBPGJ843QzuAinpZBbg3OxwPsQsLgoPhJL
- * Bg6Oxyz9M4UN1Xlx6Lyo2lRT908mBP6dl/OItLsVArqAzM+e29KHQVNjV1h7xN9F
- * u84tOgZftKun+ZkQUOoRvMLLu4yV4CUraks9tgyXquugGba/tbeyj2MYsC8wwSJX
- * ....
- * -----END RSA PRIVATE KEY-----
- */
-
-#define PEM_SUFF "-----"
-#define PEM_SUFF_L 5
-#define PEM_PREF_BEGIN "-----BEGIN "
-#define PEM_PREF_BEGIN_L 11
-#define PEM_PREF_END "-----END "
-#define PEM_PREF_END_L 9
-
EGG_SECURE_DECLARE (openssl);
-static void
-parse_header_lines (const gchar *hbeg, const gchar *hend, GHashTable **result)
-{
- gchar **lines, **l;
- gchar *line, *name, *value;
- gchar *copy;
-
- copy = g_strndup (hbeg, hend - hbeg);
- lines = g_strsplit (copy, "\n", 0);
- g_free (copy);
-
- for (l = lines; l && *l; ++l) {
- line = *l;
- g_strstrip (line);
-
- /* Look for the break between name: value */
- value = strchr (line, ':');
- if (value == NULL)
- continue;
-
- *value = 0;
- value = g_strdup (value + 1);
- g_strstrip (value);
-
- name = g_strdup (line);
- g_strstrip (name);
-
- if (!*result)
- *result = egg_openssl_headers_new ();
- g_hash_table_replace (*result, name, value);
- }
-
- g_strfreev (lines);
-}
-
-static const gchar*
-pem_find_begin (const gchar *data,
- gsize n_data,
- GQuark *type,
- const gchar **outer)
-{
- const gchar *pref, *suff;
- gchar *stype;
-
- /* Look for a prefix */
- pref = g_strstr_len ((gchar*)data, n_data, PEM_PREF_BEGIN);
- if (!pref)
- return NULL;
-
- n_data -= (pref - data) + PEM_PREF_BEGIN_L;
- data = pref + PEM_PREF_BEGIN_L;
-
- /* Look for the end of that begin */
- suff = g_strstr_len ((gchar*)data, n_data, PEM_SUFF);
- if (!suff)
- return NULL;
-
- /* Make sure on the same line */
- if (memchr (pref, '\n', suff - pref))
- return NULL;
-
- if (outer)
- *outer = pref;
-
- if (type) {
- *type = 0;
- pref += PEM_PREF_BEGIN_L;
- g_assert (suff > pref);
- stype = g_alloca (suff - pref + 1);
- memcpy (stype, pref, suff - pref);
- stype[suff - pref] = 0;
- *type = g_quark_from_string (stype);
- }
-
- /* The byte after this ---BEGIN--- */
- return suff + PEM_SUFF_L;
-}
-
-static const gchar*
-pem_find_end (const gchar *data,
- gsize n_data,
- GQuark type,
- const gchar **outer)
-{
- const gchar *stype;
- const gchar *pref;
- gsize n_type;
-
- /* Look for a prefix */
- pref = g_strstr_len (data, n_data, PEM_PREF_END);
- if (!pref)
- return NULL;
-
- n_data -= (pref - data) + PEM_PREF_END_L;
- data = pref + PEM_PREF_END_L;
-
- /* Next comes the type string */
- stype = g_quark_to_string (type);
- n_type = strlen (stype);
- if (strncmp ((gchar*)data, stype, n_type) != 0)
- return NULL;
-
- n_data -= n_type;
- data += n_type;
-
- /* Next comes the suffix */
- if (strncmp ((gchar*)data, PEM_SUFF, PEM_SUFF_L) != 0)
- return NULL;
-
- if (outer != NULL) {
- data += PEM_SUFF_L;
- if (isspace (data[0]))
- data++;
- *outer = data;
- }
-
- /* The beginning of this ---END--- */
- return pref;
-}
-
-static gboolean
-pem_parse_block (const gchar *data, gsize n_data, guchar **decoded, gsize *n_decoded,
- GHashTable **headers)
-{
- const gchar *x, *hbeg, *hend;
- const gchar *p, *end;
- gint state = 0;
- guint save = 0;
-
- g_assert (data);
- g_assert (n_data);
-
- g_assert (decoded);
- g_assert (n_decoded);
-
- p = data;
- end = p + n_data;
-
- hbeg = hend = NULL;
-
- /* Try and find a pair of blank lines with only white space between */
- while (hend == NULL) {
- x = memchr (p, '\n', end - p);
- if (!x)
- break;
- ++x;
- while (isspace (*x)) {
- /* Found a second line, with only spaces between */
- if (*x == '\n') {
- hbeg = data;
- hend = x;
- break;
- /* Found a space between two lines */
- } else {
- ++x;
- }
- }
-
- /* Try next line */
- p = x;
- }
-
- /* Headers found? */
- if (hbeg && hend) {
- data = hend;
- n_data = end - data;
- }
-
- *n_decoded = (n_data * 3) / 4 + 1;
- if (egg_secure_check (data))
- *decoded = egg_secure_alloc (*n_decoded);
- else
- *decoded = g_malloc0 (*n_decoded);
- g_return_val_if_fail (*decoded, FALSE);
-
- *n_decoded = g_base64_decode_step (data, n_data, *decoded, &state, &save);
- if (!*n_decoded) {
- egg_secure_free (*decoded);
- return FALSE;
- }
-
- if (headers && hbeg && hend)
- parse_header_lines (hbeg, hend, headers);
-
- return TRUE;
-}
-
-GHashTable*
-egg_openssl_headers_new (void)
-{
- return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-}
-
-guint
-egg_openssl_pem_parse (gconstpointer data, gsize n_data,
- EggOpensslPemCallback callback, gpointer user_data)
-{
- const gchar *beg, *end;
- const gchar *outer_beg, *outer_end;
- guint nfound = 0;
- guchar *decoded = NULL;
- gsize n_decoded = 0;
- GHashTable *headers = NULL;
- GQuark type;
-
- g_return_val_if_fail (data, 0);
- g_return_val_if_fail (n_data, 0);
- g_return_val_if_fail (callback, 0);
-
- while (n_data > 0) {
-
- /* This returns the first character after the PEM BEGIN header */
- beg = pem_find_begin ((const gchar*)data, n_data, &type, &outer_beg);
- if (!beg)
- break;
-
- g_assert (type);
-
- /* This returns the character position before the PEM END header */
- end = pem_find_end ((const gchar*)beg, n_data - ((const gchar*)beg - (const gchar *)data),
- type, &outer_end);
- if (!end)
- break;
-
- if (beg != end) {
- if (pem_parse_block (beg, end - beg, &decoded, &n_decoded, &headers)) {
- g_assert (outer_end > outer_beg);
- (callback) (type,
- decoded, n_decoded,
- outer_beg, outer_end - outer_beg,
- headers, user_data);
- ++nfound;
- egg_secure_free (decoded);
- if (headers)
- g_hash_table_remove_all (headers);
- }
- }
-
- /* Try for another block */
- end += PEM_SUFF_L;
- n_data -= (const gchar*)end - (const gchar*)data;
- data = end;
- }
-
- if (headers)
- g_hash_table_destroy (headers);
-
- return nfound;
-}
-
-static void
-append_each_header (gpointer key, gpointer value, gpointer user_data)
-{
- GString *string = (GString*)user_data;
-
- g_string_append (string, (gchar*)key);
- g_string_append (string, ": ");
- g_string_append (string, (gchar*)value);
- g_string_append_c (string, '\n');
-}
-
-guchar*
-egg_openssl_pem_write (const guchar *data, gsize n_data, GQuark type,
- GHashTable *headers, gsize *n_result)
-{
- GString *string;
- gint state, save;
- gsize i, length;
- gsize n_prefix, estimate;
-
- g_return_val_if_fail (data || !n_data, NULL);
- g_return_val_if_fail (type, NULL);
- g_return_val_if_fail (n_result, NULL);
-
- string = g_string_sized_new (4096);
-
- /* The prefix */
- g_string_append_len (string, PEM_PREF_BEGIN, PEM_PREF_BEGIN_L);
- g_string_append (string, g_quark_to_string (type));
- g_string_append_len (string, PEM_SUFF, PEM_SUFF_L);
- g_string_append_c (string, '\n');
-
- /* The headers */
- if (headers && g_hash_table_size (headers) > 0) {
- g_hash_table_foreach (headers, append_each_header, string);
- g_string_append_c (string, '\n');
- }
-
- /* Resize string to fit the base64 data. Algorithm from Glib reference */
- estimate = n_data * 4 / 3 + n_data * 4 / (3 * 65) + 7;
- n_prefix = string->len;
- g_string_set_size (string, n_prefix + estimate);
-
- /* The actual base64 data, without line breaks */
- state = save = 0;
- length = g_base64_encode_step (data, n_data, FALSE,
- string->str + n_prefix, &state, &save);
- length += g_base64_encode_close (TRUE, string->str + n_prefix + length,
- &state, &save);
-
- g_assert (length <= estimate);
- g_string_set_size (string, n_prefix + length);
-
- /*
- * OpenSSL is absolutely certain that it wants its PEM base64
- * lines to be 64 characters in length. So go through and break
- * those lines up.
- */
-
- for (i = 64; i < length; i += 64) {
- g_string_insert_c (string, n_prefix + i, '\n');
- ++length;
- ++i;
- }
-
- /* The suffix */
- g_string_append_len (string, PEM_PREF_END, PEM_PREF_END_L);
- g_string_append (string, g_quark_to_string (type));
- g_string_append_len (string, PEM_SUFF, PEM_SUFF_L);
- g_string_append_c (string, '\n');
-
- *n_result = string->len;
- return (guchar*)g_string_free (string, FALSE);
-}
-
-/* ----------------------------------------------------------------------------
- * DEFINITIONS
- */
-
static const struct {
const gchar *desc;
int algo;
return success;
}
-gboolean
-egg_openssl_decrypt_block (const gchar *dekinfo, const gchar *password,
- gssize n_password, const guchar *data, gsize n_data,
- guchar **decrypted, gsize *n_decrypted)
+guchar *
+egg_openssl_decrypt_block (const gchar *dekinfo,
+ const gchar *password,
+ gssize n_password,
+ GBytes *data,
+ gsize *n_decrypted)
{
gcry_cipher_hd_t ch;
guchar *key = NULL;
int gcry, ivlen;
int algo = 0;
int mode = 0;
+ guchar *decrypted;
if (!parse_dekinfo (dekinfo, &algo, &mode, &iv))
return FALSE;
/* IV is already set from the DEK info */
if (!egg_symkey_generate_simple (algo, GCRY_MD_MD5, password,
- n_password, iv, 8, 1, &key, NULL)) {
+ n_password, iv, 8, 1, &key, NULL)) {
g_free (iv);
- return FALSE;
+ return NULL;
}
- /* TODO: Use secure memory */
gcry = gcry_cipher_open (&ch, algo, mode, 0);
- g_return_val_if_fail (!gcry, FALSE);
+ g_return_val_if_fail (!gcry, NULL);
gcry = gcry_cipher_setkey (ch, key, gcry_cipher_get_algo_keylen (algo));
- g_return_val_if_fail (!gcry, FALSE);
+ g_return_val_if_fail (!gcry, NULL);
egg_secure_free (key);
/* 16 = 128 bits */
gcry = gcry_cipher_setiv (ch, iv, ivlen);
- g_return_val_if_fail (!gcry, FALSE);
+ g_return_val_if_fail (!gcry, NULL);
g_free (iv);
/* Allocate output area */
- *n_decrypted = n_data;
- *decrypted = egg_secure_alloc (n_data);
+ *n_decrypted = g_bytes_get_size (data);
+ decrypted = egg_secure_alloc (*n_decrypted);
- gcry = gcry_cipher_decrypt (ch, *decrypted, *n_decrypted, (void*)data, n_data);
+ gcry = gcry_cipher_decrypt (ch, decrypted, *n_decrypted,
+ g_bytes_get_data (data, NULL),
+ g_bytes_get_size (data));
if (gcry) {
- egg_secure_free (*decrypted);
- g_return_val_if_reached (FALSE);
+ egg_secure_free (decrypted);
+ g_return_val_if_reached (NULL);
}
gcry_cipher_close (ch);
- return TRUE;
+ return decrypted;
}
-gboolean
-egg_openssl_encrypt_block (const gchar *dekinfo, const gchar *password,
- gssize n_password, const guchar *data, gsize n_data,
- guchar **encrypted, gsize *n_encrypted)
+guchar *
+egg_openssl_encrypt_block (const gchar *dekinfo,
+ const gchar *password,
+ gssize n_password,
+ GBytes *data,
+ gsize *n_encrypted)
{
gsize n_overflow, n_batch, n_padding;
gcry_cipher_hd_t ch;
int gcry, ivlen;
int algo = 0;
int mode = 0;
+ gsize n_data;
+ guchar *encrypted;
+ const guchar *dat;
if (!parse_dekinfo (dekinfo, &algo, &mode, &iv))
- g_return_val_if_reached (FALSE);
+ g_return_val_if_reached (NULL);
ivlen = gcry_cipher_get_algo_blklen (algo);
/* We assume the iv is at least as long as at 8 byte salt */
- g_return_val_if_fail (ivlen >= 8, FALSE);
+ g_return_val_if_fail (ivlen >= 8, NULL);
/* IV is already set from the DEK info */
if (!egg_symkey_generate_simple (algo, GCRY_MD_MD5, password,
n_password, iv, 8, 1, &key, NULL))
- g_return_val_if_reached (FALSE);
+ g_return_val_if_reached (NULL);
gcry = gcry_cipher_open (&ch, algo, mode, 0);
- g_return_val_if_fail (!gcry, FALSE);
+ g_return_val_if_fail (!gcry, NULL);
gcry = gcry_cipher_setkey (ch, key, gcry_cipher_get_algo_keylen (algo));
- g_return_val_if_fail (!gcry, FALSE);
+ g_return_val_if_fail (!gcry, NULL);
egg_secure_free (key);
/* 16 = 128 bits */
gcry = gcry_cipher_setiv (ch, iv, ivlen);
- g_return_val_if_fail (!gcry, FALSE);
+ g_return_val_if_fail (!gcry, NULL);
g_free (iv);
+ dat = g_bytes_get_data (data, &n_data);
+
/* Allocate output area */
n_overflow = (n_data % ivlen);
n_padding = n_overflow ? (ivlen - n_overflow) : 0;
n_batch = n_data - n_overflow;
*n_encrypted = n_data + n_padding;
- *encrypted = g_malloc0 (*n_encrypted);
+ encrypted = g_malloc0 (*n_encrypted);
g_assert (*n_encrypted % ivlen == 0);
g_assert (*n_encrypted >= n_data);
g_assert (*n_encrypted == n_batch + n_overflow + n_padding);
/* Encrypt everything but the last bit */
- gcry = gcry_cipher_encrypt (ch, *encrypted, n_batch, (void*)data, n_batch);
+ gcry = gcry_cipher_encrypt (ch, encrypted, n_batch, dat, n_batch);
if (gcry) {
- g_free (*encrypted);
- g_return_val_if_reached (FALSE);
+ g_free (encrypted);
+ g_return_val_if_reached (NULL);
}
/* Encrypt the padded block */
if (n_overflow) {
padded = egg_secure_alloc (ivlen);
memset (padded, 0, ivlen);
- memcpy (padded, data + n_batch, n_overflow);
- gcry = gcry_cipher_encrypt (ch, *encrypted + n_batch, ivlen, padded, ivlen);
+ memcpy (padded, dat + n_batch, n_overflow);
+ gcry = gcry_cipher_encrypt (ch, encrypted + n_batch, ivlen, padded, ivlen);
egg_secure_free (padded);
if (gcry) {
- g_free (*encrypted);
- g_return_val_if_reached (FALSE);
+ g_free (encrypted);
+ g_return_val_if_reached (NULL);
}
}
gcry_cipher_close (ch);
- return TRUE;
+ return encrypted;
}
const gchar*
g_return_val_if_fail (hex, NULL);
dekinfo = g_strdup_printf ("DES-EDE3-CBC,%s", hex);
g_free (hex);
+ g_free (iv);
g_hash_table_insert (headers, g_strdup ("DEK-Info"), (void*)dekinfo);
g_hash_table_insert (headers, g_strdup ("Proc-Type"), g_strdup ("4,ENCRYPTED"));