1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
/* egg-openssl.c - OpenSSL compatibility functionality
4
Copyright (C) 2007 Stefan Walter
6
The Gnome Keyring Library is free software; you can redistribute it and/or
7
modify it under the terms of the GNU Library General Public License as
8
published by the Free Software Foundation; either version 2 of the
9
License, or (at your option) any later version.
11
The Gnome Keyring Library is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
Library General Public License for more details.
16
You should have received a copy of the GNU Library General Public
17
License along with the Gnome Library; see the file COPYING.LIB. If not,
18
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19
Boston, MA 02111-1307, USA.
21
Author: Stef Walter <stef@memberwebs.com>
27
#include "egg-armor.h"
28
#include "egg-secure-memory.h"
40
* -----BEGIN RSA PRIVATE KEY-----
41
* Proc-Type: 4,ENCRYPTED
42
* DEK-Info: DES-EDE3-CBC,704CFFD62FBA03E9
44
* 4AV/g0BiTeb07hzo4/Ct47HGhHEshMhBPGJ843QzuAinpZBbg3OxwPsQsLgoPhJL
45
* Bg6Oxyz9M4UN1Xlx6Lyo2lRT908mBP6dl/OItLsVArqAzM+e29KHQVNjV1h7xN9F
46
* u84tOgZftKun+ZkQUOoRvMLLu4yV4CUraks9tgyXquugGba/tbeyj2MYsC8wwSJX
47
* ................................................................
49
* -----END RSA PRIVATE KEY-----
51
* The last line before END is an option OpenPGP armor checksum
54
EGG_SECURE_DECLARE (armor);
56
#define ARMOR_SUFF "-----"
57
#define ARMOR_SUFF_L 5
58
#define ARMOR_PREF_BEGIN "-----BEGIN "
59
#define ARMOR_PREF_BEGIN_L 11
60
#define ARMOR_PREF_END "-----END "
61
#define ARMOR_PREF_END_L 9
64
parse_header_lines (const gchar *hbeg,
69
gchar *line, *name, *value;
72
copy = g_strndup (hbeg, hend - hbeg);
73
lines = g_strsplit (copy, "\n", 0);
76
for (l = lines; l && *l; ++l) {
80
/* Look for the break between name: value */
81
value = strchr (line, ':');
86
value = g_strdup (value + 1);
89
name = g_strdup (line);
93
*result = egg_armor_headers_new ();
94
g_hash_table_replace (*result, name, value);
101
armor_find_begin (const gchar *data,
106
const gchar *pref, *suff;
109
/* Look for a prefix */
110
pref = g_strstr_len ((gchar*)data, n_data, ARMOR_PREF_BEGIN);
114
n_data -= (pref - data) + ARMOR_PREF_BEGIN_L;
115
data = pref + ARMOR_PREF_BEGIN_L;
117
/* Look for the end of that begin */
118
suff = g_strstr_len ((gchar*)data, n_data, ARMOR_SUFF);
122
/* Make sure on the same line */
123
if (memchr (pref, '\n', suff - pref))
131
pref += ARMOR_PREF_BEGIN_L;
132
g_assert (suff > pref);
133
stype = g_alloca (suff - pref + 1);
134
memcpy (stype, pref, suff - pref);
135
stype[suff - pref] = 0;
136
*type = g_quark_from_string (stype);
139
/* The byte after this ---BEGIN--- */
140
return suff + ARMOR_SUFF_L;
144
armor_find_end (const gchar *data,
154
/* Look for a prefix */
155
pref = g_strstr_len (data, n_data, ARMOR_PREF_END);
159
n_data -= (pref - data) + ARMOR_PREF_END_L;
160
data = pref + ARMOR_PREF_END_L;
162
/* Next comes the type string */
163
stype = g_quark_to_string (type);
164
n_type = strlen (stype);
165
if (strncmp ((gchar*)data, stype, n_type) != 0)
171
/* Next comes the suffix */
172
if (strncmp ((gchar*)data, ARMOR_SUFF, ARMOR_SUFF_L) != 0)
176
* Check if there's a OpenPGP style armor checksum line. OpenPGP
177
* does not insist that we validate this line, and is more useful
178
* for PGP messages, rather than the keys we usually see.
180
line = memrchr (data, '\n', (pref - 1) - data);
181
if (line && line[1] == '=')
185
data += ARMOR_SUFF_L;
186
if (isspace (data[0]))
191
/* The end of the data */
196
armor_parse_block (const gchar *data,
200
GHashTable **headers)
202
const gchar *x, *hbeg, *hend;
203
const gchar *p, *end;
211
g_assert (n_decoded);
218
/* Try and find a pair of blank lines with only white space between */
219
while (hend == NULL) {
220
x = memchr (p, '\n', end - p);
224
while (isspace (*x)) {
225
/* Found a second line, with only spaces between */
230
/* Found a space between two lines */
246
*n_decoded = (n_data * 3) / 4 + 1;
247
if (egg_secure_check (data))
248
*decoded = egg_secure_alloc (*n_decoded);
250
*decoded = g_malloc0 (*n_decoded);
251
g_return_val_if_fail (*decoded, FALSE);
253
*n_decoded = g_base64_decode_step (data, n_data, *decoded, &state, &save);
255
egg_secure_free (*decoded);
259
if (headers && hbeg && hend)
260
parse_header_lines (hbeg, hend, headers);
266
egg_armor_headers_new (void)
268
return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
272
egg_armor_parse (gconstpointer data,
274
EggArmorCallback callback,
277
const gchar *beg, *end;
278
const gchar *outer_beg, *outer_end;
280
guchar *decoded = NULL;
282
GHashTable *headers = NULL;
285
g_return_val_if_fail (data, 0);
286
g_return_val_if_fail (n_data, 0);
290
/* This returns the first character after the PEM BEGIN header */
291
beg = armor_find_begin ((const gchar*)data, n_data, &type, &outer_beg);
297
/* This returns the character position before the PEM END header */
298
end = armor_find_end ((const gchar*)beg,
299
n_data - ((const gchar*)beg - (const gchar *)data),
305
if (armor_parse_block (beg, end - beg, &decoded, &n_decoded, &headers)) {
306
g_assert (outer_end > outer_beg);
307
if (callback != NULL)
310
outer_beg, outer_end - outer_beg,
313
egg_secure_free (decoded);
315
g_hash_table_remove_all (headers);
319
/* Try for another block */
321
n_data -= (const gchar*)end - (const gchar*)data;
326
g_hash_table_destroy (headers);
332
append_each_header (gpointer key, gpointer value, gpointer user_data)
334
GString *string = (GString*)user_data;
336
g_string_append (string, (gchar*)key);
337
g_string_append (string, ": ");
338
g_string_append (string, (gchar*)value);
339
g_string_append_c (string, '\n');
343
egg_armor_write (const guchar *data,
352
gsize n_prefix, estimate;
354
g_return_val_if_fail (data || !n_data, NULL);
355
g_return_val_if_fail (type, NULL);
356
g_return_val_if_fail (n_result, NULL);
358
string = g_string_sized_new (4096);
361
g_string_append_len (string, ARMOR_PREF_BEGIN, ARMOR_PREF_BEGIN_L);
362
g_string_append (string, g_quark_to_string (type));
363
g_string_append_len (string, ARMOR_SUFF, ARMOR_SUFF_L);
364
g_string_append_c (string, '\n');
367
if (headers && g_hash_table_size (headers) > 0) {
368
g_hash_table_foreach (headers, append_each_header, string);
369
g_string_append_c (string, '\n');
372
/* Resize string to fit the base64 data. Algorithm from Glib reference */
373
estimate = n_data * 4 / 3 + n_data * 4 / (3 * 65) + 7;
374
n_prefix = string->len;
375
g_string_set_size (string, n_prefix + estimate);
377
/* The actual base64 data, without line breaks */
379
length = g_base64_encode_step (data, n_data, FALSE,
380
string->str + n_prefix, &state, &save);
381
length += g_base64_encode_close (TRUE, string->str + n_prefix + length,
384
g_assert (length <= estimate);
385
g_string_set_size (string, n_prefix + length);
388
* OpenSSL is absolutely certain that it wants its PEM base64
389
* lines to be 64 characters in length. So go through and break
393
for (i = 64; i < length; i += 64) {
394
g_string_insert_c (string, n_prefix + i, '\n');
400
g_string_append_len (string, ARMOR_PREF_END, ARMOR_PREF_END_L);
401
g_string_append (string, g_quark_to_string (type));
402
g_string_append_len (string, ARMOR_SUFF, ARMOR_SUFF_L);
403
g_string_append_c (string, '\n');
405
*n_result = string->len;
406
return (guchar*)g_string_free (string, FALSE);