Blame gcr/gcr-openssh.c

Packit b00eeb
/*
Packit b00eeb
 * gnome-keyring
Packit b00eeb
 *
Packit b00eeb
 * Copyright (C) 2011 Collabora Ltd.
Packit b00eeb
 *
Packit b00eeb
 * This program is free software; you can redistribute it and/or modify
Packit b00eeb
 * it under the terms of the GNU Lesser General Public License as
Packit b00eeb
 * published by the Free Software Foundation; either version 2.1 of
Packit b00eeb
 * the License, or (at your option) any later version.
Packit b00eeb
 *
Packit b00eeb
 * This program is distributed in the hope that it will be useful, but
Packit b00eeb
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b00eeb
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit b00eeb
 * Lesser General Public License for more details.
Packit b00eeb
 *
Packit b00eeb
 * You should have received a copy of the GNU Lesser General Public
Packit b00eeb
 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
Packit b00eeb
 *
Packit b00eeb
 * Author: Stef Walter <stefw@collabora.co.uk>
Packit b00eeb
 */
Packit b00eeb
Packit b00eeb
#include "config.h"
Packit b00eeb
Packit b00eeb
#include "gcr-openssh.h"
Packit b00eeb
#include "gcr-internal.h"
Packit b00eeb
#include "gcr-types.h"
Packit b00eeb
Packit b00eeb
#include "gcr/gcr-oids.h"
Packit b00eeb
Packit b00eeb
#include "egg/egg-asn1x.h"
Packit b00eeb
#include "egg/egg-asn1-defs.h"
Packit b00eeb
#include "egg/egg-buffer.h"
Packit b00eeb
#include "egg/egg-decimal.h"
Packit b00eeb
Packit b00eeb
#include <p11-kit/pkcs11.h>
Packit b00eeb
Packit b00eeb
#include <string.h>
Packit b00eeb
Packit b00eeb
typedef struct {
Packit b00eeb
	GcrOpensshPubCallback callback;
Packit b00eeb
	gpointer user_data;
Packit b00eeb
} OpensshPubClosure;
Packit b00eeb
Packit b00eeb
static void
Packit b00eeb
skip_spaces (const gchar ** line,
Packit b00eeb
             gsize *n_line)
Packit b00eeb
{
Packit b00eeb
	while (*n_line > 0 && (*line)[0] == ' ') {
Packit b00eeb
		(*line)++;
Packit b00eeb
		(*n_line)--;
Packit b00eeb
	}
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
next_word (const gchar **line,
Packit b00eeb
           gsize *n_line,
Packit b00eeb
           const gchar **word,
Packit b00eeb
           gsize *n_word)
Packit b00eeb
{
Packit b00eeb
	const gchar *beg;
Packit b00eeb
	const gchar *end;
Packit b00eeb
	const gchar *at;
Packit b00eeb
	gboolean quotes;
Packit b00eeb
Packit b00eeb
	skip_spaces (line, n_line);
Packit b00eeb
Packit b00eeb
	if (!*n_line) {
Packit b00eeb
		*word = NULL;
Packit b00eeb
		*n_word = 0;
Packit b00eeb
		return FALSE;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	beg = at = *line;
Packit b00eeb
	end = beg + *n_line;
Packit b00eeb
	quotes = FALSE;
Packit b00eeb
Packit b00eeb
	do {
Packit b00eeb
		switch (*at) {
Packit b00eeb
		case '"':
Packit b00eeb
			quotes = !quotes;
Packit b00eeb
			at++;
Packit b00eeb
			break;
Packit b00eeb
		case ' ':
Packit b00eeb
			if (!quotes)
Packit b00eeb
				end = at;
Packit b00eeb
			else
Packit b00eeb
				at++;
Packit b00eeb
			break;
Packit b00eeb
		default:
Packit b00eeb
			at++;
Packit b00eeb
			break;
Packit b00eeb
		}
Packit b00eeb
	} while (at < end);
Packit b00eeb
Packit b00eeb
	*word = beg;
Packit b00eeb
	*n_word = end - beg;
Packit b00eeb
	(*line) += *n_word;
Packit b00eeb
	(*n_line) -= *n_word;
Packit b00eeb
	return TRUE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
match_word (const gchar *word,
Packit b00eeb
            gsize n_word,
Packit b00eeb
            const gchar *matches)
Packit b00eeb
{
Packit b00eeb
	gsize len = strlen (matches);
Packit b00eeb
	if (len != n_word)
Packit b00eeb
		return FALSE;
Packit b00eeb
	return memcmp (word, matches, n_word) == 0;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gulong
Packit b00eeb
keytype_to_algo (const gchar *algo,
Packit b00eeb
                 gsize length)
Packit b00eeb
{
Packit b00eeb
	if (!algo)
Packit b00eeb
		return G_MAXULONG;
Packit b00eeb
	else if (match_word (algo, length, "ssh-rsa"))
Packit b00eeb
		return CKK_RSA;
Packit b00eeb
	else if (match_word (algo, length, "ssh-dss"))
Packit b00eeb
		return CKK_DSA;
Packit b00eeb
	else if (length >= 6 && strncmp (algo, "ecdsa-", 6) == 0)
Packit b00eeb
		return CKK_ECDSA;
Packit b00eeb
	return G_MAXULONG;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
read_decimal_mpi (const gchar *decimal,
Packit b00eeb
                  gsize n_decimal,
Packit b00eeb
                  GckBuilder *builder,
Packit b00eeb
                  gulong attribute_type)
Packit b00eeb
{
Packit b00eeb
	gpointer data;
Packit b00eeb
	gsize n_data;
Packit b00eeb
Packit b00eeb
	data = egg_decimal_decode (decimal, n_decimal, &n_data);
Packit b00eeb
	if (data == NULL)
Packit b00eeb
		return FALSE;
Packit b00eeb
Packit b00eeb
	gck_builder_add_data (builder, attribute_type, data, n_data);
Packit b00eeb
	g_free (data);
Packit b00eeb
	return TRUE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gint
Packit b00eeb
atoin (const char *p, gint digits)
Packit b00eeb
{
Packit b00eeb
	gint ret = 0, base = 1;
Packit b00eeb
	while(--digits >= 0) {
Packit b00eeb
		if (p[digits] < '0' || p[digits] > '9')
Packit b00eeb
			return -1;
Packit b00eeb
		ret += (p[digits] - '0') * base;
Packit b00eeb
		base *= 10;
Packit b00eeb
	}
Packit b00eeb
	return ret;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static GcrDataError
Packit b00eeb
parse_v1_public_line (const gchar *line,
Packit b00eeb
                      gsize length,
Packit b00eeb
                      GBytes *backing,
Packit b00eeb
                      GcrOpensshPubCallback callback,
Packit b00eeb
                      gpointer user_data)
Packit b00eeb
{
Packit b00eeb
	const gchar *word_bits, *word_exponent, *word_modulus, *word_options, *outer;
Packit b00eeb
	gsize len_bits, len_exponent, len_modulus, len_options, n_outer;
Packit b00eeb
	GckBuilder builder = GCK_BUILDER_INIT;
Packit b00eeb
	GckAttributes *attrs;
Packit b00eeb
	gchar *label, *options;
Packit b00eeb
	GBytes *bytes;
Packit b00eeb
	gint bits;
Packit b00eeb
Packit b00eeb
	g_assert (line);
Packit b00eeb
Packit b00eeb
	outer = line;
Packit b00eeb
	n_outer = length;
Packit b00eeb
	options = NULL;
Packit b00eeb
	label = NULL;
Packit b00eeb
Packit b00eeb
	/* Eat space at the front */
Packit b00eeb
	skip_spaces (&line, &length);
Packit b00eeb
Packit b00eeb
	/* Blank line or comment */
Packit b00eeb
	if (length == 0 || line[0] == '#')
Packit b00eeb
		return GCR_ERROR_UNRECOGNIZED;
Packit b00eeb
Packit b00eeb
	/*
Packit b00eeb
	 * If the line starts with a digit, then no options:
Packit b00eeb
	 *
Packit b00eeb
	 * 2048 35 25213680043....93533757 Label
Packit b00eeb
	 *
Packit b00eeb
	 * If the line doesn't start with a digit, then have options:
Packit b00eeb
	 *
Packit b00eeb
	 * option,option 2048 35 25213680043....93533757 Label
Packit b00eeb
	 */
Packit b00eeb
	if (g_ascii_isdigit (line[0])) {
Packit b00eeb
		word_options = NULL;
Packit b00eeb
		len_options = 0;
Packit b00eeb
	} else {
Packit b00eeb
		if (!next_word (&line, &length, &word_options, &len_options))
Packit b00eeb
			return GCR_ERROR_UNRECOGNIZED;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	if (!next_word (&line, &length, &word_bits, &len_bits) ||
Packit b00eeb
	    !next_word (&line, &length, &word_exponent, &len_exponent) ||
Packit b00eeb
	    !next_word (&line, &length, &word_modulus, &len_modulus))
Packit b00eeb
		return GCR_ERROR_UNRECOGNIZED;
Packit b00eeb
Packit b00eeb
	bits = atoin (word_bits, len_bits);
Packit b00eeb
	if (bits <= 0)
Packit b00eeb
		return GCR_ERROR_UNRECOGNIZED;
Packit b00eeb
Packit b00eeb
	if (!read_decimal_mpi (word_exponent, len_exponent, &builder, CKA_PUBLIC_EXPONENT) ||
Packit b00eeb
	    !read_decimal_mpi (word_modulus, len_modulus, &builder, CKA_MODULUS)) {
Packit b00eeb
		gck_builder_clear (&builder);
Packit b00eeb
		return GCR_ERROR_UNRECOGNIZED;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	gck_builder_add_ulong (&builder, CKA_KEY_TYPE, CKK_RSA);
Packit b00eeb
	gck_builder_add_ulong (&builder, CKA_CLASS, CKO_PUBLIC_KEY);
Packit b00eeb
Packit b00eeb
	skip_spaces (&line, &length);
Packit b00eeb
	if (length > 0) {
Packit b00eeb
		label = g_strndup (line, length);
Packit b00eeb
		g_strstrip (label);
Packit b00eeb
		gck_builder_add_string (&builder, CKA_LABEL, label);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	if (word_options)
Packit b00eeb
		options = g_strndup (word_options, len_options);
Packit b00eeb
Packit b00eeb
	attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
Packit b00eeb
Packit b00eeb
	if (callback != NULL) {
Packit b00eeb
		bytes = g_bytes_new_with_free_func (outer, n_outer,
Packit b00eeb
		                                    (GDestroyNotify)g_bytes_unref,
Packit b00eeb
		                                    g_bytes_ref (backing));
Packit b00eeb
		(callback) (attrs, label, options, bytes, user_data);
Packit b00eeb
		g_bytes_unref (bytes);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	gck_attributes_unref (attrs);
Packit b00eeb
	g_free (options);
Packit b00eeb
	g_free (label);
Packit b00eeb
	return GCR_SUCCESS;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
read_buffer_mpi_to_der (EggBuffer *buffer,
Packit b00eeb
                        gsize *offset,
Packit b00eeb
                        GckBuilder *builder,
Packit b00eeb
                        gulong attribute_type)
Packit b00eeb
{
Packit b00eeb
	const guchar *data, *data_value;
Packit b00eeb
	GBytes *der_data = NULL;
Packit b00eeb
	gsize len, data_len;
Packit b00eeb
	GNode *asn = NULL;
Packit b00eeb
	gboolean rv = FALSE;
Packit b00eeb
Packit b00eeb
	if (!egg_buffer_get_byte_array (buffer, *offset, offset, &data, &len))
Packit b00eeb
		return FALSE;
Packit b00eeb
Packit b00eeb
	asn = egg_asn1x_create (pk_asn1_tab, "ECPoint");
Packit b00eeb
	if (!asn)
Packit b00eeb
		return FALSE;
Packit b00eeb
Packit b00eeb
	egg_asn1x_set_string_as_raw (asn, (guchar *)data, len, NULL);
Packit b00eeb
	der_data = egg_asn1x_encode (asn, g_realloc);
Packit b00eeb
	if (!der_data)
Packit b00eeb
		goto out;
Packit b00eeb
Packit b00eeb
	data_value = g_bytes_get_data (der_data, &data_len);
Packit b00eeb
	gck_builder_add_data (builder, attribute_type, data_value, data_len);
Packit b00eeb
	rv = TRUE;
Packit b00eeb
out:
Packit b00eeb
	g_bytes_unref (der_data);
Packit b00eeb
	egg_asn1x_destroy (asn);
Packit b00eeb
	return rv;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
read_buffer_mpi (EggBuffer *buffer,
Packit b00eeb
                 gsize *offset,
Packit b00eeb
                 GckBuilder *builder,
Packit b00eeb
                 gulong attribute_type)
Packit b00eeb
{
Packit b00eeb
	const guchar *data;
Packit b00eeb
	gsize len;
Packit b00eeb
Packit b00eeb
	if (!egg_buffer_get_byte_array (buffer, *offset, offset, &data, &len))
Packit b00eeb
		return FALSE;
Packit b00eeb
Packit b00eeb
	gck_builder_add_data (builder, attribute_type, data, len);
Packit b00eeb
	return TRUE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
read_v2_public_dsa (EggBuffer *buffer,
Packit b00eeb
                    gsize *offset,
Packit b00eeb
                    GckBuilder *builder)
Packit b00eeb
{
Packit b00eeb
	if (!read_buffer_mpi (buffer, offset, builder, CKA_PRIME) ||
Packit b00eeb
	    !read_buffer_mpi (buffer, offset, builder, CKA_SUBPRIME) ||
Packit b00eeb
	    !read_buffer_mpi (buffer, offset, builder, CKA_BASE) ||
Packit b00eeb
	    !read_buffer_mpi (buffer, offset, builder, CKA_VALUE)) {
Packit b00eeb
		return FALSE;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	gck_builder_add_ulong (builder, CKA_KEY_TYPE, CKK_DSA);
Packit b00eeb
	gck_builder_add_ulong (builder, CKA_CLASS, CKO_PUBLIC_KEY);
Packit b00eeb
Packit b00eeb
	return TRUE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
read_v2_public_rsa (EggBuffer *buffer,
Packit b00eeb
                    gsize *offset,
Packit b00eeb
                    GckBuilder *builder)
Packit b00eeb
{
Packit b00eeb
	if (!read_buffer_mpi (buffer, offset, builder, CKA_PUBLIC_EXPONENT) ||
Packit b00eeb
	    !read_buffer_mpi (buffer, offset, builder, CKA_MODULUS)) {
Packit b00eeb
		return FALSE;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	gck_builder_add_ulong (builder, CKA_KEY_TYPE, CKK_RSA);
Packit b00eeb
	gck_builder_add_ulong (builder, CKA_CLASS, CKO_PUBLIC_KEY);
Packit b00eeb
Packit b00eeb
	return TRUE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
read_v2_public_ecdsa (EggBuffer *buffer,
Packit b00eeb
                      gsize *offset,
Packit b00eeb
                      GckBuilder *builder)
Packit b00eeb
{
Packit b00eeb
	gconstpointer data;
Packit b00eeb
	GBytes *bytes;
Packit b00eeb
	GNode *asn;
Packit b00eeb
	GNode *node;
Packit b00eeb
	gchar *curve;
Packit b00eeb
	GQuark oid;
Packit b00eeb
	gsize len;
Packit b00eeb
Packit b00eeb
	/* The named curve */
Packit b00eeb
	if (!egg_buffer_get_string (buffer, *offset, offset,
Packit b00eeb
	                            &curve, (EggBufferAllocator)g_realloc))
Packit b00eeb
		return FALSE;
Packit b00eeb
Packit b00eeb
	if (g_strcmp0 (curve, "nistp256") == 0) {
Packit b00eeb
		oid = GCR_OID_EC_SECP256R1;
Packit b00eeb
	} else if (g_strcmp0 (curve, "nistp384") == 0) {
Packit b00eeb
		oid = GCR_OID_EC_SECP384R1;
Packit b00eeb
	} else if (g_strcmp0 (curve, "nistp521") == 0) {
Packit b00eeb
		oid = GCR_OID_EC_SECP521R1;
Packit b00eeb
	} else {
Packit b00eeb
		g_free (curve);
Packit b00eeb
		g_message ("unknown or unsupported curve in ssh public key");
Packit b00eeb
		return FALSE;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	g_free (curve);
Packit b00eeb
Packit b00eeb
	asn = egg_asn1x_create (pk_asn1_tab, "ECParameters");
Packit b00eeb
	g_return_val_if_fail (asn != NULL, FALSE);
Packit b00eeb
Packit b00eeb
	node = egg_asn1x_node (asn, "namedCurve", NULL);
Packit b00eeb
	if (!egg_asn1x_set_choice (asn, node))
Packit b00eeb
		g_return_val_if_reached (FALSE);
Packit b00eeb
Packit b00eeb
	if (!egg_asn1x_set_oid_as_quark (node, oid))
Packit b00eeb
		g_return_val_if_reached (FALSE);
Packit b00eeb
Packit b00eeb
	bytes = egg_asn1x_encode (asn, g_realloc);
Packit b00eeb
	g_return_val_if_fail (bytes != NULL, FALSE);
Packit b00eeb
	egg_asn1x_destroy (asn);
Packit b00eeb
Packit b00eeb
	data = g_bytes_get_data (bytes, &len;;
Packit b00eeb
	gck_builder_add_data (builder, CKA_EC_PARAMS, data, len);
Packit b00eeb
	g_bytes_unref (bytes);
Packit b00eeb
Packit b00eeb
	/* need to convert to DER encoded OCTET STRING */
Packit b00eeb
	if (!read_buffer_mpi_to_der (buffer, offset, builder, CKA_EC_POINT))
Packit b00eeb
		return FALSE;
Packit b00eeb
Packit b00eeb
	gck_builder_add_ulong (builder, CKA_KEY_TYPE, CKK_ECDSA);
Packit b00eeb
	gck_builder_add_ulong (builder, CKA_CLASS, CKO_PUBLIC_KEY);
Packit b00eeb
Packit b00eeb
	return TRUE;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
read_v2_public_key (gulong algo,
Packit b00eeb
                    gconstpointer data,
Packit b00eeb
                    gsize n_data,
Packit b00eeb
                    GckBuilder *builder)
Packit b00eeb
{
Packit b00eeb
	EggBuffer buffer;
Packit b00eeb
	gboolean ret;
Packit b00eeb
	gsize offset;
Packit b00eeb
	gchar *stype;
Packit b00eeb
	int alg;
Packit b00eeb
Packit b00eeb
	egg_buffer_init_static (&buffer, data, n_data);
Packit b00eeb
	offset = 0;
Packit b00eeb
Packit b00eeb
	/* The string algorithm */
Packit b00eeb
	if (!egg_buffer_get_string (&buffer, offset, &offset,
Packit b00eeb
	                            &stype, (EggBufferAllocator)g_realloc))
Packit b00eeb
		return FALSE;
Packit b00eeb
Packit b00eeb
	alg = keytype_to_algo (stype, stype ? strlen (stype) : 0);
Packit b00eeb
	g_free (stype);
Packit b00eeb
Packit b00eeb
	if (alg != algo) {
Packit b00eeb
		g_message ("invalid or mis-matched algorithm in ssh public key: %s", stype);
Packit b00eeb
		egg_buffer_uninit (&buffer);
Packit b00eeb
		return FALSE;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	switch (algo) {
Packit b00eeb
	case CKK_RSA:
Packit b00eeb
		ret = read_v2_public_rsa (&buffer, &offset, builder);
Packit b00eeb
		break;
Packit b00eeb
	case CKK_DSA:
Packit b00eeb
		ret = read_v2_public_dsa (&buffer, &offset, builder);
Packit b00eeb
		break;
Packit b00eeb
	case CKK_ECDSA:
Packit b00eeb
		ret = read_v2_public_ecdsa (&buffer, &offset, builder);
Packit b00eeb
		break;
Packit b00eeb
	default:
Packit b00eeb
		g_assert_not_reached ();
Packit b00eeb
		break;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	egg_buffer_uninit (&buffer);
Packit b00eeb
	return ret;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static gboolean
Packit b00eeb
decode_v2_public_key (gulong algo,
Packit b00eeb
                      const gchar *data,
Packit b00eeb
                      gsize n_data,
Packit b00eeb
                      GckBuilder *builder)
Packit b00eeb
{
Packit b00eeb
	gpointer decoded;
Packit b00eeb
	gsize n_decoded;
Packit b00eeb
	gboolean ret;
Packit b00eeb
	guint save;
Packit b00eeb
	gint state;
Packit b00eeb
Packit b00eeb
	/* Decode the base64 key */
Packit b00eeb
	save = state = 0;
Packit b00eeb
	decoded = g_malloc (n_data * 3 / 4);
Packit b00eeb
	n_decoded = g_base64_decode_step ((gchar*)data, n_data, decoded, &state, &save);
Packit b00eeb
Packit b00eeb
	if (!n_decoded) {
Packit b00eeb
		g_free (decoded);
Packit b00eeb
		return FALSE;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* Parse the actual key */
Packit b00eeb
	ret = read_v2_public_key (algo, decoded, n_decoded, builder);
Packit b00eeb
Packit b00eeb
	g_free (decoded);
Packit b00eeb
Packit b00eeb
	return ret;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
static GcrDataError
Packit b00eeb
parse_v2_public_line (const gchar *line,
Packit b00eeb
                      gsize length,
Packit b00eeb
                      GBytes *backing,
Packit b00eeb
                      GcrOpensshPubCallback callback,
Packit b00eeb
                      gpointer user_data)
Packit b00eeb
{
Packit b00eeb
	const gchar *word_options, *word_algo, *word_key;
Packit b00eeb
	gsize len_options, len_algo, len_key;
Packit b00eeb
	GckBuilder builder = GCK_BUILDER_INIT;
Packit b00eeb
	GckAttributes *attrs;
Packit b00eeb
	gchar *options;
Packit b00eeb
	gchar *label = NULL;
Packit b00eeb
	const gchar *outer = line;
Packit b00eeb
	gsize n_outer = length;
Packit b00eeb
	GBytes *bytes;
Packit b00eeb
	gulong algo;
Packit b00eeb
Packit b00eeb
	g_assert (line);
Packit b00eeb
Packit b00eeb
	/* Eat space at the front */
Packit b00eeb
	skip_spaces (&line, &length);
Packit b00eeb
Packit b00eeb
	/* Blank line or comment */
Packit b00eeb
	if (length == 0 || line[0] == '#')
Packit b00eeb
		return GCR_ERROR_UNRECOGNIZED;
Packit b00eeb
Packit b00eeb
	if (!next_word (&line, &length, &word_algo, &len_algo))
Packit b00eeb
		return GCR_ERROR_UNRECOGNIZED;
Packit b00eeb
Packit b00eeb
	/*
Packit b00eeb
	 * If the first word is not the algorithm, then we have options:
Packit b00eeb
	 *
Packit b00eeb
	 * option,option ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAI...EAz8Ji= Label here
Packit b00eeb
	 *
Packit b00eeb
	 * If the first word is the algorithm, then we have no options:
Packit b00eeb
	 *
Packit b00eeb
	 * ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAI...EAz8Ji= Label here
Packit b00eeb
	 */
Packit b00eeb
	algo = keytype_to_algo (word_algo, len_algo);
Packit b00eeb
	if (algo == G_MAXULONG) {
Packit b00eeb
		word_options = word_algo;
Packit b00eeb
		len_options = len_algo;
Packit b00eeb
		if (!next_word (&line, &length, &word_algo, &len_algo))
Packit b00eeb
			return GCR_ERROR_UNRECOGNIZED;
Packit b00eeb
		algo = keytype_to_algo (word_algo, len_algo);
Packit b00eeb
		if (algo == G_MAXULONG)
Packit b00eeb
			return GCR_ERROR_UNRECOGNIZED;
Packit b00eeb
	} else {
Packit b00eeb
		word_options = NULL;
Packit b00eeb
		len_options = 0;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	/* Must have at least two words */
Packit b00eeb
	if (!next_word (&line, &length, &word_key, &len_key))
Packit b00eeb
		return GCR_ERROR_FAILURE;
Packit b00eeb
Packit b00eeb
	if (!decode_v2_public_key (algo, word_key, len_key, &builder)) {
Packit b00eeb
		gck_builder_clear (&builder);
Packit b00eeb
		return GCR_ERROR_FAILURE;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	if (word_options)
Packit b00eeb
		options = g_strndup (word_options, len_options);
Packit b00eeb
	else
Packit b00eeb
		options = NULL;
Packit b00eeb
Packit b00eeb
	/* The remainder of the line is the label */
Packit b00eeb
	skip_spaces (&line, &length);
Packit b00eeb
	if (length > 0) {
Packit b00eeb
		label = g_strndup (line, length);
Packit b00eeb
		g_strstrip (label);
Packit b00eeb
		gck_builder_add_string (&builder, CKA_LABEL, label);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
Packit b00eeb
Packit b00eeb
	if (callback != NULL) {
Packit b00eeb
		bytes = g_bytes_new_with_free_func (outer, n_outer,
Packit b00eeb
		                                    (GDestroyNotify)g_bytes_unref,
Packit b00eeb
		                                    g_bytes_ref (backing));
Packit b00eeb
		(callback) (attrs, label, options, bytes, user_data);
Packit b00eeb
		g_bytes_unref (bytes);
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	gck_attributes_unref (attrs);
Packit b00eeb
	g_free (options);
Packit b00eeb
	g_free (label);
Packit b00eeb
	return GCR_SUCCESS;
Packit b00eeb
}
Packit b00eeb
Packit b00eeb
guint
Packit b00eeb
_gcr_openssh_pub_parse (GBytes *data,
Packit b00eeb
                        GcrOpensshPubCallback callback,
Packit b00eeb
                        gpointer user_data)
Packit b00eeb
{
Packit b00eeb
	const gchar *line;
Packit b00eeb
	const gchar *end;
Packit b00eeb
	gsize length;
Packit b00eeb
	gboolean last;
Packit b00eeb
	GcrDataError res;
Packit b00eeb
	guint num_parsed;
Packit b00eeb
Packit b00eeb
	g_return_val_if_fail (data != NULL, FALSE);
Packit b00eeb
Packit b00eeb
	line = g_bytes_get_data (data, NULL);
Packit b00eeb
	length = g_bytes_get_size (data);
Packit b00eeb
	last = FALSE;
Packit b00eeb
	num_parsed = 0;
Packit b00eeb
Packit b00eeb
	for (;;) {
Packit b00eeb
		end  = memchr (line, '\n', length);
Packit b00eeb
		if (end == NULL) {
Packit b00eeb
			end = line + length;
Packit b00eeb
			last = TRUE;
Packit b00eeb
		}
Packit b00eeb
Packit b00eeb
		if (line != end) {
Packit b00eeb
			res = parse_v2_public_line (line, end - line, data, callback, user_data);
Packit b00eeb
			if (res == GCR_ERROR_UNRECOGNIZED)
Packit b00eeb
				res = parse_v1_public_line (line, end - line, data, callback, user_data);
Packit b00eeb
			if (res == GCR_SUCCESS)
Packit b00eeb
				num_parsed++;
Packit b00eeb
		}
Packit b00eeb
Packit b00eeb
		if (last)
Packit b00eeb
			break;
Packit b00eeb
Packit b00eeb
		end++;
Packit b00eeb
		length -= (end - line);
Packit b00eeb
		line = end;
Packit b00eeb
	}
Packit b00eeb
Packit b00eeb
	return num_parsed;
Packit b00eeb
}