/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* gkm-data-der.c - parsing and serializing of common crypto DER structures
Copyright (C) 2007 Stefan Walter
The Gnome Keyring Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The Gnome Keyring Library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the Gnome Library; see the file COPYING.LIB. If not,
.
Author: Stef Walter
*/
#include "config.h"
#include "gkm-data-asn1.h"
#include "gkm-data-der.h"
#include "gkm-data-types.h"
#include "gkm-sexp.h"
#include "egg/egg-asn1x.h"
#include "egg/egg-asn1-defs.h"
#include "egg/egg-secure-memory.h"
#include "egg/egg-symkey.h"
#include
#include
EGG_SECURE_DECLARE (data_der);
/* -----------------------------------------------------------------------------
* QUARKS
*/
static GQuark OID_PKIX1_RSA;
static GQuark OID_PKIX1_DSA;
static GQuark OID_PKIX1_ECDSA;
static GQuark OID_PKCS12_PBE_3DES_SHA1;
static GQuark OID_ANSI_SECP256R1;
static GQuark OID_ANSI_SECP384R1;
static GQuark OID_ANSI_SECP521R1;
static void
init_quarks (void)
{
static volatile gsize quarks_inited = 0;
if (g_once_init_enter (&quarks_inited)) {
#define QUARK(name, value) \
name = g_quark_from_static_string(value)
QUARK (OID_PKIX1_RSA, "1.2.840.113549.1.1.1");
QUARK (OID_PKIX1_DSA, "1.2.840.10040.4.1");
QUARK (OID_PKIX1_ECDSA, "1.2.840.10045.2.1");
QUARK (OID_PKCS12_PBE_3DES_SHA1, "1.2.840.113549.1.12.1.3");
QUARK (OID_ANSI_SECP256R1, "1.2.840.10045.3.1.7");
QUARK (OID_ANSI_SECP384R1, "1.3.132.0.34");
QUARK (OID_ANSI_SECP521R1, "1.3.132.0.35");
#undef QUARK
g_once_init_leave (&quarks_inited, 1);
}
}
const gchar *
gkm_data_der_oid_to_curve (GQuark oid)
{
if (oid == OID_ANSI_SECP256R1)
return "NIST P-256";
else if (oid == OID_ANSI_SECP384R1)
return "NIST P-384";
else if (oid == OID_ANSI_SECP521R1)
return "NIST P-521";
return NULL;
}
/*
* Convert ecc->curve values from libgcrypt representation to internal one.
* Ignore duplicates and alternative names, since S-expressions are created
* only by us throught this code.
*/
static GQuark
gkm_data_der_curve_to_oid (const gchar *curve)
{
if (g_str_equal (curve, "NIST P-256"))
return OID_ANSI_SECP256R1;
else if (g_str_equal (curve, "NIST P-384"))
return OID_ANSI_SECP384R1;
else if (g_str_equal (curve, "NIST P-521"))
return OID_ANSI_SECP521R1;
return 0;
}
GQuark
gkm_data_der_oid_from_ec_params (GBytes *params)
{
GNode *asn;
GQuark oid;
init_quarks ();
asn = egg_asn1x_create_and_decode (pk_asn1_tab, "Parameters", params);
if (!asn)
return 0;
oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "namedCurve", NULL));
egg_asn1x_destroy (asn);
return oid;
}
GBytes *
gkm_data_der_get_ec_params (GQuark oid)
{
GNode *asn;
GBytes *params = NULL;
GNode *named_curve;
asn = egg_asn1x_create (pk_asn1_tab, "Parameters");
if (!asn)
goto done;
named_curve = egg_asn1x_node (asn, "namedCurve", NULL);
if (!egg_asn1x_set_oid_as_quark (named_curve, oid))
goto done;
if (!egg_asn1x_set_choice (asn, named_curve))
goto done;
params = egg_asn1x_encode (asn, NULL);
done:
egg_asn1x_destroy (asn);
return params;
}
/* wrapper so we do not have to export the GQuark magic */
GBytes *
gkm_data_der_curve_to_ec_params (const gchar *curve_name)
{
GQuark oid;
init_quarks ();
oid = gkm_data_der_curve_to_oid (curve_name);
if (oid == 0)
return NULL;
return gkm_data_der_get_ec_params (oid);
}
GBytes *
gkm_data_der_encode_ecdsa_q_str (const guchar *data, gsize data_len)
{
GNode *asn = NULL;
GBytes *bytes, *result = NULL;
asn = egg_asn1x_create (pk_asn1_tab, "ECKeyQ");
g_return_val_if_fail (asn, FALSE);
bytes = g_bytes_new_static (data, data_len);
/* "consumes" bytes */
if (!gkm_data_asn1_write_string (asn, bytes))
goto done;
result = egg_asn1x_encode (asn, g_realloc);
if (result == NULL)
g_warning ("couldn't encode Q into the PKCS#11 structure: %s", egg_asn1x_message (asn));
done:
egg_asn1x_destroy (asn);
return result;
}
gboolean
gkm_data_der_encode_ecdsa_q (gcry_mpi_t q, GBytes **result)
{
gcry_error_t gcry;
guchar data[1024];
gsize data_len = 1024;
gboolean rv = TRUE;
g_assert (q);
g_assert (result);
gcry = gcry_mpi_print (GCRYMPI_FMT_USG, data, data_len, &data_len, q);
g_return_val_if_fail (gcry == 0, FALSE);
*result = gkm_data_der_encode_ecdsa_q_str (data, data_len);
if (*result == NULL)
rv = FALSE;
return rv;
}
gboolean
gkm_data_der_decode_ecdsa_q (GBytes *data, GBytes **result)
{
GNode *asn = NULL;
gboolean rv = TRUE;
g_assert (data);
g_assert (result);
asn = egg_asn1x_create_and_decode (pk_asn1_tab, "ECKeyQ", data);
/* workaround a bug in gcr (not DER encoding the MPI) */
if (!asn) {
*result = data;
return rv;
}
rv = gkm_data_asn1_read_string (asn, result);
egg_asn1x_destroy (asn);
return rv;
}
/* -----------------------------------------------------------------------------
* KEY PARSING
*/
#define SEXP_PUBLIC_RSA \
"(public-key" \
" (rsa" \
" (n %m)" \
" (e %m)))"
GkmDataResult
gkm_data_der_read_public_key_rsa (GBytes *data,
gcry_sexp_t *s_key)
{
GkmDataResult ret = GKM_DATA_UNRECOGNIZED;
GNode *asn = NULL;
gcry_mpi_t n, e;
int res;
n = e = NULL;
asn = egg_asn1x_create_and_decode (pk_asn1_tab, "RSAPublicKey", data);
if (!asn)
goto done;
ret = GKM_DATA_FAILURE;
if (!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "modulus", NULL), &n) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "publicExponent", NULL), &e))
goto done;
res = gcry_sexp_build (s_key, NULL, SEXP_PUBLIC_RSA, n, e);
if (res)
goto done;
g_assert (*s_key);
ret = GKM_DATA_SUCCESS;
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (n);
gcry_mpi_release (e);
if (ret == GKM_DATA_FAILURE)
g_message ("invalid RSA public key");
return ret;
}
#define SEXP_PRIVATE_RSA \
"(private-key" \
" (rsa" \
" (n %m)" \
" (e %m)" \
" (d %m)" \
" (p %m)" \
" (q %m)" \
" (u %m)))"
GkmDataResult
gkm_data_der_read_private_key_rsa (GBytes *data,
gcry_sexp_t *s_key)
{
GkmDataResult ret = GKM_DATA_UNRECOGNIZED;
gcry_mpi_t n, e, d, p, q, u;
gcry_mpi_t tmp;
gulong version;
GNode *asn = NULL;
int res;
n = e = d = p = q = u = NULL;
asn = egg_asn1x_create_and_decode (pk_asn1_tab, "RSAPrivateKey", data);
if (!asn)
goto done;
ret = GKM_DATA_FAILURE;
if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (asn, "version", NULL), &version))
goto done;
/* We only support simple version */
if (version != 0) {
ret = GKM_DATA_UNRECOGNIZED;
g_message ("unsupported version of RSA key: %lu", version);
goto done;
}
if (!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "modulus", NULL), &n) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "publicExponent", NULL), &e) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "privateExponent", NULL), &d) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "prime1", NULL), &p) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "prime2", NULL), &q) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "coefficient", NULL), &u))
goto done;
/* Fix up the incoming key so gcrypt likes it */
if (gcry_mpi_cmp (p, q) > 0) {
/* P shall be smaller then Q! Swap primes. iqmp becomes u. */
tmp = p;
p = q;
q = tmp;
} else {
/* U needs to be recomputed. */
gcry_mpi_invm (u, p, q);
}
res = gcry_sexp_build (s_key, NULL, SEXP_PRIVATE_RSA, n, e, d, p, q, u);
if (res)
goto done;
g_assert (*s_key);
ret = GKM_DATA_SUCCESS;
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (n);
gcry_mpi_release (e);
gcry_mpi_release (d);
gcry_mpi_release (p);
gcry_mpi_release (q);
gcry_mpi_release (u);
if (ret == GKM_DATA_FAILURE)
g_message ("invalid RSA key");
return ret;
}
#define SEXP_PUBLIC_DSA \
"(public-key" \
" (dsa" \
" (p %m)" \
" (q %m)" \
" (g %m)" \
" (y %m)))"
GkmDataResult
gkm_data_der_read_public_key_dsa (GBytes *data,
gcry_sexp_t *s_key)
{
GkmDataResult ret = GKM_DATA_UNRECOGNIZED;
GNode *asn = NULL;
gcry_mpi_t p, q, g, y;
int res;
p = q = g = y = NULL;
asn = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAPublicKey", data);
if (!asn)
goto done;
ret = GKM_DATA_FAILURE;
if (!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "p", NULL), &p) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "q", NULL), &q) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "g", NULL), &g) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "Y", NULL), &y))
goto done;
res = gcry_sexp_build (s_key, NULL, SEXP_PUBLIC_DSA, p, q, g, y);
if (res)
goto done;
g_assert (*s_key);
ret = GKM_DATA_SUCCESS;
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (p);
gcry_mpi_release (q);
gcry_mpi_release (g);
gcry_mpi_release (y);
if (ret == GKM_DATA_FAILURE)
g_message ("invalid public DSA key");
return ret;
}
GkmDataResult
gkm_data_der_read_public_key_dsa_parts (GBytes *keydata,
GBytes *params,
gcry_sexp_t *s_key)
{
gcry_mpi_t p, q, g, y;
GkmDataResult ret = GKM_DATA_UNRECOGNIZED;
GNode *asn_params = NULL;
GNode *asn_key = NULL;
int res;
p = q = g = y = NULL;
asn_params = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAParameters", params);
asn_key = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAPublicPart", keydata);
if (!asn_params || !asn_key)
goto done;
ret = GKM_DATA_FAILURE;
if (!gkm_data_asn1_read_mpi (egg_asn1x_node (asn_params, "p", NULL), &p) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn_params, "q", NULL), &q) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn_params, "g", NULL), &g))
goto done;
if (!gkm_data_asn1_read_mpi (asn_key, &y))
goto done;
res = gcry_sexp_build (s_key, NULL, SEXP_PUBLIC_DSA, p, q, g, y);
if (res)
goto done;
g_assert (*s_key);
ret = GKM_DATA_SUCCESS;
done:
egg_asn1x_destroy (asn_key);
egg_asn1x_destroy (asn_params);
gcry_mpi_release (p);
gcry_mpi_release (q);
gcry_mpi_release (g);
gcry_mpi_release (y);
if (ret == GKM_DATA_FAILURE)
g_message ("invalid DSA key");
return ret;
}
#define SEXP_PRIVATE_DSA \
"(private-key" \
" (dsa" \
" (p %m)" \
" (q %m)" \
" (g %m)" \
" (y %m)" \
" (x %m)))"
GkmDataResult
gkm_data_der_read_private_key_dsa (GBytes *data,
gcry_sexp_t *s_key)
{
gcry_mpi_t p, q, g, y, x;
GkmDataResult ret = GKM_DATA_UNRECOGNIZED;
int res;
GNode *asn = NULL;
p = q = g = y = x = NULL;
asn = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAPrivateKey", data);
if (!asn)
goto done;
ret = GKM_DATA_FAILURE;
if (!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "p", NULL), &p) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "q", NULL), &q) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "g", NULL), &g) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "Y", NULL), &y) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn, "priv", NULL), &x))
goto done;
res = gcry_sexp_build (s_key, NULL, SEXP_PRIVATE_DSA, p, q, g, y, x);
if (res)
goto done;
g_assert (*s_key);
ret = GKM_DATA_SUCCESS;
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (p);
gcry_mpi_release (q);
gcry_mpi_release (g);
gcry_mpi_release (y);
gcry_mpi_release (x);
if (ret == GKM_DATA_FAILURE)
g_message ("invalid DSA key");
return ret;
}
GkmDataResult
gkm_data_der_read_private_key_dsa_parts (GBytes *keydata,
GBytes *params,
gcry_sexp_t *s_key)
{
gcry_mpi_t p, q, g, y, x;
GkmDataResult ret = GKM_DATA_UNRECOGNIZED;
int res;
GNode *asn_params = NULL;
GNode *asn_key = NULL;
p = q = g = y = x = NULL;
asn_params = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAParameters", params);
asn_key = egg_asn1x_create_and_decode (pk_asn1_tab, "DSAPrivatePart", keydata);
if (!asn_params || !asn_key)
goto done;
ret = GKM_DATA_FAILURE;
if (!gkm_data_asn1_read_mpi (egg_asn1x_node (asn_params, "p", NULL), &p) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn_params, "q", NULL), &q) ||
!gkm_data_asn1_read_mpi (egg_asn1x_node (asn_params, "g", NULL), &g))
goto done;
if (!gkm_data_asn1_read_mpi (asn_key, &x))
goto done;
/* Now we calculate y */
y = gcry_mpi_snew (1024);
gcry_mpi_powm (y, g, x, p);
res = gcry_sexp_build (s_key, NULL, SEXP_PRIVATE_DSA, p, q, g, y, x);
if (res)
goto done;
g_assert (*s_key);
ret = GKM_DATA_SUCCESS;
done:
egg_asn1x_destroy (asn_key);
egg_asn1x_destroy (asn_params);
gcry_mpi_release (p);
gcry_mpi_release (q);
gcry_mpi_release (g);
gcry_mpi_release (y);
gcry_mpi_release (x);
if (ret == GKM_DATA_FAILURE)
g_message ("invalid DSA key");
return ret;
}
#define SEXP_PUBLIC_ECDSA \
"(public-key" \
" (ecdsa" \
" (curve %s)" \
" (q %b)))"
GkmDataResult
gkm_data_der_read_public_key_ecdsa (GBytes *data,
gcry_sexp_t *s_key)
{
GkmDataResult ret = GKM_DATA_UNRECOGNIZED;
int res;
GNode *asn = NULL;
GBytes *q = NULL;
gsize q_bits;
GQuark oid;
const gchar *curve = NULL;
init_quarks ();
asn = egg_asn1x_create_and_decode (pk_asn1_tab, "ECPublicKey", data);
if (!asn)
goto done;
ret = GKM_DATA_FAILURE;
if (!gkm_data_asn1_read_oid (egg_asn1x_node (asn, "parameters", "namedCurve", NULL), &oid) ||
!gkm_data_asn1_read_bit_string (egg_asn1x_node (asn, "q", NULL), &q, &q_bits))
goto done;
curve = gkm_data_der_oid_to_curve (oid);
if (curve == NULL)
goto done;
res = gcry_sexp_build (s_key, NULL, SEXP_PUBLIC_ECDSA,
curve,
g_bytes_get_size(q), g_bytes_get_data(q, NULL));
if (res)
goto done;
g_assert (*s_key);
ret = GKM_DATA_SUCCESS;
done:
egg_asn1x_destroy (asn);
g_bytes_unref (q);
if (ret == GKM_DATA_FAILURE)
g_message ("invalid ECDSA key");
return ret;
}
#define SEXP_PRIVATE_ECDSA \
"(private-key" \
" (ecdsa" \
" (curve %s)" \
" (q %b)" \
" (d %m)))"
GkmDataResult
gkm_data_der_read_private_key_ecdsa (GBytes *data,
gcry_sexp_t *s_key)
{
gcry_mpi_t d = NULL;
GkmDataResult ret = GKM_DATA_UNRECOGNIZED;
int res;
GNode *asn = NULL;
GBytes *q = NULL;
gsize q_bits;
GQuark oid;
const gchar *curve = NULL;
init_quarks ();
asn = egg_asn1x_create_and_decode (pk_asn1_tab, "ECPrivateKey", data);
if (!asn)
goto done;
ret = GKM_DATA_FAILURE;
if (!gkm_data_asn1_read_string_mpi (egg_asn1x_node (asn, "d", NULL), &d) ||
!gkm_data_asn1_read_oid (egg_asn1x_node (asn, "parameters", "namedCurve", NULL), &oid) ||
!gkm_data_asn1_read_bit_string (egg_asn1x_node (asn, "q", NULL), &q, &q_bits))
goto done;
curve = gkm_data_der_oid_to_curve (oid);
if (curve == NULL)
goto done;
res = gcry_sexp_build (s_key, NULL, SEXP_PRIVATE_ECDSA,
curve,
g_bytes_get_size(q), g_bytes_get_data(q, NULL), d);
if (res)
goto done;
g_assert (*s_key);
ret = GKM_DATA_SUCCESS;
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (d);
g_bytes_unref (q);
if (ret == GKM_DATA_FAILURE)
g_message ("invalid ECDSA key");
return ret;
}
GkmDataResult
gkm_data_der_read_public_key (GBytes *data, gcry_sexp_t *s_key)
{
GkmDataResult res;
res = gkm_data_der_read_public_key_rsa (data, s_key);
if (res == GKM_DATA_UNRECOGNIZED)
res = gkm_data_der_read_public_key_ecdsa (data, s_key);
if (res == GKM_DATA_UNRECOGNIZED)
res = gkm_data_der_read_public_key_dsa (data, s_key);
return res;
}
GkmDataResult
gkm_data_der_read_public_key_info (GBytes *data,
gcry_sexp_t* s_key)
{
GkmDataResult ret = GKM_DATA_UNRECOGNIZED;
GQuark oid;
GNode *asn = NULL;
GBytes *params;
GBytes *key = NULL;
guint n_bits;
init_quarks ();
asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "SubjectPublicKeyInfo", data);
if (!asn)
goto done;
ret = GKM_DATA_FAILURE;
/* Figure out the algorithm */
oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "algorithm", "algorithm", NULL));
if (!oid)
goto done;
/* A bit string so we cannot process in place */
key = egg_asn1x_get_bits_as_raw (egg_asn1x_node (asn, "subjectPublicKey", NULL), &n_bits);
if (!key)
goto done;
if (n_bits % 8 != 0) {
g_message ("invalid bit length for public key: %u", n_bits);
goto done;
}
/* An RSA key is simple */
if (oid == OID_PKIX1_RSA) {
ret = gkm_data_der_read_public_key_rsa (key, s_key);
/* A DSA key paramaters are stored separately */
} else if (oid == OID_PKIX1_DSA) {
params = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "algorithm", "parameters", NULL));
if (!params)
goto done;
ret = gkm_data_der_read_public_key_dsa_parts (key, params, s_key);
g_bytes_unref (params);
/* A ECDSA key */
} else if (oid == OID_PKIX1_ECDSA) {
ret = gkm_data_der_read_public_key_ecdsa (key, s_key);
} else {
g_message ("unsupported key algorithm in certificate: %s", g_quark_to_string (oid));
ret = GKM_DATA_UNRECOGNIZED;
goto done;
}
done:
egg_asn1x_destroy (asn);
if (key)
g_bytes_unref (key);
if (ret == GKM_DATA_FAILURE)
g_message ("invalid subject public-key info");
return ret;
}
GkmDataResult
gkm_data_der_read_private_key (GBytes *data,
gcry_sexp_t *s_key)
{
GkmDataResult res;
res = gkm_data_der_read_private_key_rsa (data, s_key);
if (res == GKM_DATA_UNRECOGNIZED)
res = gkm_data_der_read_private_key_dsa (data, s_key);
if (res == GKM_DATA_UNRECOGNIZED)
res = gkm_data_der_read_private_key_ecdsa (data, s_key);
return res;
}
GkmDataResult
gkm_data_der_read_private_pkcs8_plain (GBytes *data,
gcry_sexp_t *s_key)
{
GNode *asn = NULL;
GkmDataResult ret;
int algorithm;
GQuark key_algo;
GBytes *keydata = NULL;
GBytes *params = NULL;
ret = GKM_DATA_UNRECOGNIZED;
init_quarks ();
asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-8-PrivateKeyInfo", data);
if (!asn)
goto done;
ret = GKM_DATA_FAILURE;
algorithm = 0;
key_algo = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "privateKeyAlgorithm", "algorithm", NULL));
if (!key_algo)
goto done;
else if (key_algo == OID_PKIX1_RSA)
algorithm = GCRY_PK_RSA;
else if (key_algo == OID_PKIX1_DSA)
algorithm = GCRY_PK_DSA;
else if (key_algo == OID_PKIX1_ECDSA)
algorithm = GCRY_PK_ECC;
if (!algorithm) {
ret = GKM_DATA_UNRECOGNIZED;
goto done;
}
keydata = egg_asn1x_get_string_as_bytes (egg_asn1x_node (asn, "privateKey", NULL));
if (!keydata)
goto done;
params = egg_asn1x_get_element_raw (egg_asn1x_node (asn, "privateKeyAlgorithm", "parameters", NULL));
ret = GKM_DATA_SUCCESS;
done:
if (ret == GKM_DATA_SUCCESS) {
switch (algorithm) {
case GCRY_PK_RSA:
ret = gkm_data_der_read_private_key_rsa (keydata, s_key);
break;
case GCRY_PK_DSA:
/* Try the normal one block format */
ret = gkm_data_der_read_private_key_dsa (keydata, s_key);
/* Otherwise try the two part format that everyone seems to like */
if (ret == GKM_DATA_UNRECOGNIZED && params)
ret = gkm_data_der_read_private_key_dsa_parts (keydata, params, s_key);
break;
case GCRY_PK_ECC:
ret = gkm_data_der_read_private_key_ecdsa (keydata, s_key);
break;
default:
g_message ("invalid or unsupported key type in PKCS#8 key");
ret = GKM_DATA_UNRECOGNIZED;
break;
};
} else if (ret == GKM_DATA_FAILURE) {
g_message ("invalid PKCS#8 key");
}
if (params)
g_bytes_unref (params);
if (keydata)
g_bytes_unref (keydata);
egg_asn1x_destroy (asn);
return ret;
}
GkmDataResult
gkm_data_der_read_private_pkcs8_crypted (GBytes *data,
const gchar *password,
gsize n_password,
gcry_sexp_t *s_key)
{
GNode *asn = NULL;
gcry_cipher_hd_t cih = NULL;
gcry_error_t gcry;
GkmDataResult ret, r;
GQuark scheme;
guchar *crypted = NULL;
GNode *params;
GBytes *bytes;
gsize n_crypted;
gint l;
init_quarks ();
ret = GKM_DATA_UNRECOGNIZED;
asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-8-EncryptedPrivateKeyInfo", data);
if (!asn)
goto done;
ret = GKM_DATA_FAILURE;
/* Figure out the type of encryption */
scheme = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "encryptionAlgorithm", "algorithm", NULL));
if (!scheme)
goto done;
params = egg_asn1x_node (asn, "encryptionAlgorithm", "parameters", NULL);
if (!params)
goto done;
/*
* Parse the encryption stuff into a cipher.
*/
r = egg_symkey_read_cipher (scheme, password, n_password, params, &cih);
if (r == GKM_DATA_UNRECOGNIZED) {
ret = GKM_DATA_FAILURE;
goto done;
} else if (r != GKM_DATA_SUCCESS) {
ret = r;
goto done;
}
crypted = egg_asn1x_get_string_as_raw (egg_asn1x_node (asn, "encryptedData", NULL),
egg_secure_realloc, &n_crypted);
if (!crypted)
goto done;
gcry = gcry_cipher_decrypt (cih, crypted, n_crypted, NULL, 0);
gcry_cipher_close (cih);
cih = NULL;
if (gcry != 0) {
g_warning ("couldn't decrypt pkcs8 data: %s", gcry_strerror (gcry));
goto done;
}
/* Unpad the DER data */
l = egg_asn1x_element_length (crypted, n_crypted);
if (l <= 0 || l > n_crypted) {
ret = GKM_DATA_LOCKED;
goto done;
}
n_crypted = l;
bytes = g_bytes_new_with_free_func (crypted, n_crypted, egg_secure_free, crypted);
crypted = NULL;
/* Try to parse the resulting key */
ret = gkm_data_der_read_private_pkcs8_plain (bytes, s_key);
g_bytes_unref (bytes);
/* If unrecognized we assume bad password */
if (ret == GKM_DATA_UNRECOGNIZED)
ret = GKM_DATA_LOCKED;
done:
if (cih)
gcry_cipher_close (cih);
egg_asn1x_destroy (asn);
egg_secure_free (crypted);
return ret;
}
GkmDataResult
gkm_data_der_read_private_pkcs8 (GBytes *data,
const gchar *password,
gsize n_password,
gcry_sexp_t *s_key)
{
GkmDataResult res;
res = gkm_data_der_read_private_pkcs8_crypted (data, password, n_password, s_key);
if (res == GKM_DATA_UNRECOGNIZED)
res = gkm_data_der_read_private_pkcs8_plain (data, s_key);
return res;
}
GBytes *
gkm_data_der_write_public_key_rsa (gcry_sexp_t s_key)
{
GNode *asn = NULL;
gcry_mpi_t n, e;
GBytes *result = NULL;
n = e = NULL;
asn = egg_asn1x_create (pk_asn1_tab, "RSAPublicKey");
g_return_val_if_fail (asn, NULL);
if (!gkm_sexp_extract_mpi (s_key, &n, "rsa", "n", NULL) ||
!gkm_sexp_extract_mpi (s_key, &e, "rsa", "e", NULL))
goto done;
if (!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "modulus", NULL), n) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "publicExponent", NULL), e))
goto done;
result = egg_asn1x_encode (asn, NULL);
if (result == NULL)
g_warning ("couldn't encode public rsa key: %s", egg_asn1x_message (asn));
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (n);
gcry_mpi_release (e);
return result;
}
GBytes *
gkm_data_der_write_private_key_rsa (gcry_sexp_t s_key)
{
GNode *asn = NULL;
gcry_mpi_t n, e, d, p, q, u, e1, e2, tmp;
GBytes *result = NULL;
n = e = d = p = q = u = e1 = e2 = tmp = NULL;
asn = egg_asn1x_create (pk_asn1_tab, "RSAPrivateKey");
g_return_val_if_fail (asn, NULL);
if (!gkm_sexp_extract_mpi (s_key, &n, "rsa", "n", NULL) ||
!gkm_sexp_extract_mpi (s_key, &e, "rsa", "e", NULL) ||
!gkm_sexp_extract_mpi (s_key, &d, "rsa", "d", NULL) ||
!gkm_sexp_extract_mpi (s_key, &p, "rsa", "p", NULL) ||
!gkm_sexp_extract_mpi (s_key, &q, "rsa", "q", NULL) ||
!gkm_sexp_extract_mpi (s_key, &u, "rsa", "u", NULL))
goto done;
if (!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "modulus", NULL), n) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "publicExponent", NULL), e) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "privateExponent", NULL), d) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "prime1", NULL), p) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "prime2", NULL), q) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "coefficient", NULL), u))
goto done;
/* Calculate e1 and e2 */
tmp = gcry_mpi_snew (1024);
gcry_mpi_sub_ui (tmp, p, 1);
e1 = gcry_mpi_snew (1024);
gcry_mpi_mod (e1, d, tmp);
gcry_mpi_sub_ui (tmp, q, 1);
e2 = gcry_mpi_snew (1024);
gcry_mpi_mod (e2, d, tmp);
/* Write out calculated */
if (!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "exponent1", NULL), e1) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "exponent2", NULL), e2))
goto done;
/* Write out the version */
egg_asn1x_set_integer_as_ulong (egg_asn1x_node (asn, "version", NULL), 0);
result = egg_asn1x_encode (asn, egg_secure_realloc);
if (result == NULL)
g_warning ("couldn't encode private rsa key: %s", egg_asn1x_message (asn));
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (n);
gcry_mpi_release (e);
gcry_mpi_release (d);
gcry_mpi_release (p);
gcry_mpi_release (q);
gcry_mpi_release (u);
gcry_mpi_release (tmp);
gcry_mpi_release (e1);
gcry_mpi_release (e2);
return result;
}
GBytes *
gkm_data_der_write_public_key_dsa (gcry_sexp_t s_key)
{
GNode *asn = NULL;
gcry_mpi_t p, q, g, y;
GBytes *result = NULL;
p = q = g = y = NULL;
asn = egg_asn1x_create (pk_asn1_tab, "DSAPublicKey");
g_return_val_if_fail (asn, NULL);
if (!gkm_sexp_extract_mpi (s_key, &p, "dsa", "p", NULL) ||
!gkm_sexp_extract_mpi (s_key, &q, "dsa", "q", NULL) ||
!gkm_sexp_extract_mpi (s_key, &g, "dsa", "g", NULL) ||
!gkm_sexp_extract_mpi (s_key, &y, "dsa", "y", NULL))
goto done;
if (!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "p", NULL), p) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "q", NULL), q) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "g", NULL), g) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "Y", NULL), y))
goto done;
egg_asn1x_set_integer_as_ulong (egg_asn1x_node (asn, "version", NULL), 0);
result = egg_asn1x_encode (asn, NULL);
if (result == NULL)
g_warning ("couldn't encode public dsa key: %s", egg_asn1x_message (asn));
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (p);
gcry_mpi_release (q);
gcry_mpi_release (g);
gcry_mpi_release (y);
return result;
}
GBytes *
gkm_data_der_write_private_key_dsa_part (gcry_sexp_t skey)
{
GNode *asn = NULL;
gcry_mpi_t x;
GBytes *result = NULL;
x = NULL;
asn = egg_asn1x_create (pk_asn1_tab, "DSAPrivatePart");
g_return_val_if_fail (asn, NULL);
if (!gkm_sexp_extract_mpi (skey, &x, "dsa", "x", NULL))
goto done;
if (!gkm_data_asn1_write_mpi (asn, x))
goto done;
result = egg_asn1x_encode (asn, egg_secure_realloc);
if (result == NULL)
g_warning ("couldn't encode private dsa key: %s", egg_asn1x_message (asn));
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (x);
return result;
}
GBytes *
gkm_data_der_write_private_key_dsa_params (gcry_sexp_t skey)
{
GNode *asn = NULL;
gcry_mpi_t p, q, g;
GBytes *result = NULL;
p = q = g = NULL;
asn = egg_asn1x_create (pk_asn1_tab, "DSAParameters");
g_return_val_if_fail (asn, NULL);
if (!gkm_sexp_extract_mpi (skey, &p, "dsa", "p", NULL) ||
!gkm_sexp_extract_mpi (skey, &q, "dsa", "q", NULL) ||
!gkm_sexp_extract_mpi (skey, &g, "dsa", "g", NULL))
goto done;
if (!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "p", NULL), p) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "q", NULL), q) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "g", NULL), g))
goto done;
result = egg_asn1x_encode (asn, egg_secure_realloc);
if (result == NULL)
g_warning ("couldn't encode private dsa params: %s", egg_asn1x_message (asn));
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (p);
gcry_mpi_release (q);
gcry_mpi_release (g);
return result;
}
GBytes *
gkm_data_der_write_private_key_dsa (gcry_sexp_t s_key)
{
GNode *asn = NULL;
gcry_mpi_t p, q, g, y, x;
GBytes *result = NULL;
p = q = g = y = x = NULL;
asn = egg_asn1x_create (pk_asn1_tab, "DSAPrivateKey");
g_return_val_if_fail (asn, NULL);
if (!gkm_sexp_extract_mpi (s_key, &p, "dsa", "p", NULL) ||
!gkm_sexp_extract_mpi (s_key, &q, "dsa", "q", NULL) ||
!gkm_sexp_extract_mpi (s_key, &g, "dsa", "g", NULL) ||
!gkm_sexp_extract_mpi (s_key, &y, "dsa", "y", NULL) ||
!gkm_sexp_extract_mpi (s_key, &x, "dsa", "x", NULL))
goto done;
if (!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "p", NULL), p) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "q", NULL), q) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "g", NULL), g) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "Y", NULL), y) ||
!gkm_data_asn1_write_mpi (egg_asn1x_node (asn, "priv", NULL), x))
goto done;
egg_asn1x_set_integer_as_ulong (egg_asn1x_node (asn, "version", NULL), 0);
result = egg_asn1x_encode (asn, egg_secure_realloc);
if (result == NULL)
g_warning ("couldn't encode private dsa key: %s", egg_asn1x_message (asn));
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (p);
gcry_mpi_release (q);
gcry_mpi_release (g);
gcry_mpi_release (y);
gcry_mpi_release (x);
return result;
}
GBytes *
gkm_data_der_write_public_key_ecdsa (gcry_sexp_t s_key)
{
GNode *asn = NULL, *named_curve;
gcry_mpi_t d = NULL;
GBytes *result = NULL, *q = NULL;
gchar *q_data = NULL;
GQuark oid;
gchar *curve = NULL;
gsize q_size;
init_quarks ();
asn = egg_asn1x_create (pk_asn1_tab, "ECPublicKey");
g_return_val_if_fail (asn, NULL);
if (!gkm_sexp_extract_buffer (s_key, &q_data, &q_size, "ecdsa", "q", NULL) ||
!gkm_sexp_extract_string (s_key, &curve, "ecdsa", "curve", NULL))
goto done;
oid = gkm_data_der_curve_to_oid (curve);
g_free (curve);
if (oid == 0)
goto done;
q = g_bytes_new_take (q_data, q_size);
if (q == NULL)
goto done;
named_curve = egg_asn1x_node (asn, "parameters", "namedCurve", NULL);
/* XXX the bit size does not have to match byte size * 8 exactly
* it would be good to cover this with more tests
*/
if (!gkm_data_asn1_write_bit_string (egg_asn1x_node (asn, "q", NULL), q, q_size*8) ||
!gkm_data_asn1_write_oid (named_curve, oid))
goto done;
if (!egg_asn1x_set_choice (egg_asn1x_node (asn, "parameters", NULL), named_curve))
goto done;
result = egg_asn1x_encode (asn, egg_secure_realloc);
if (result == NULL)
g_warning ("couldn't encode public ecdsa key: %s", egg_asn1x_message (asn));
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (d);
g_bytes_unref (q);
return result;
}
GBytes *
gkm_data_der_write_private_key_ecdsa (gcry_sexp_t s_key)
{
GNode *asn = NULL, *named_curve;
gcry_mpi_t d = NULL;
GBytes *result = NULL, *q = NULL;
gchar *q_data = NULL;
GQuark oid;
gchar *curve = NULL;
gsize q_size;
init_quarks ();
asn = egg_asn1x_create (pk_asn1_tab, "ECPrivateKey");
g_return_val_if_fail (asn, NULL);
if (!gkm_sexp_extract_mpi (s_key, &d, "ecdsa", "d", NULL) ||
!gkm_sexp_extract_buffer (s_key, &q_data, &q_size, "ecdsa", "q", NULL) ||
!gkm_sexp_extract_string (s_key, &curve, "ecdsa", "curve", NULL))
goto done;
oid = gkm_data_der_curve_to_oid (curve);
g_free (curve);
if (oid == 0)
goto done;
q = g_bytes_new_take (q_data, q_size);
if (q == NULL)
goto done;
named_curve = egg_asn1x_node (asn, "parameters", "namedCurve", NULL);
if (!gkm_data_asn1_write_string_mpi (egg_asn1x_node (asn, "d", NULL), d) ||
!gkm_data_asn1_write_bit_string (egg_asn1x_node (asn, "q", NULL), q, q_size*8) ||
!gkm_data_asn1_write_oid (named_curve, oid))
goto done;
if (!egg_asn1x_set_choice (egg_asn1x_node (asn, "parameters", NULL), named_curve))
goto done;
egg_asn1x_set_integer_as_ulong (egg_asn1x_node (asn, "version", NULL), 1);
result = egg_asn1x_encode (asn, egg_secure_realloc);
if (result == NULL)
g_warning ("couldn't encode private ecdsa key: %s", egg_asn1x_message (asn));
done:
egg_asn1x_destroy (asn);
gcry_mpi_release (d);
g_bytes_unref (q);
return result;
}
GBytes *
gkm_data_der_write_public_key (gcry_sexp_t s_key)
{
gboolean is_priv;
int algorithm;
g_return_val_if_fail (s_key != NULL, NULL);
if (!gkm_sexp_parse_key (s_key, &algorithm, &is_priv, NULL))
g_return_val_if_reached (NULL);
g_return_val_if_fail (!is_priv, NULL);
switch (algorithm) {
case GCRY_PK_RSA:
return gkm_data_der_write_public_key_rsa (s_key);
case GCRY_PK_DSA:
return gkm_data_der_write_public_key_dsa (s_key);
case GCRY_PK_ECC:
return gkm_data_der_write_public_key_ecdsa (s_key);
default:
g_return_val_if_reached (NULL);
}
}
GBytes *
gkm_data_der_write_private_key (gcry_sexp_t s_key)
{
gboolean is_priv;
int algorithm;
g_return_val_if_fail (s_key != NULL, NULL);
if (!gkm_sexp_parse_key (s_key, &algorithm, &is_priv, NULL))
g_return_val_if_reached (NULL);
g_return_val_if_fail (is_priv, NULL);
switch (algorithm) {
case GCRY_PK_RSA:
return gkm_data_der_write_private_key_rsa (s_key);
case GCRY_PK_DSA:
return gkm_data_der_write_private_key_dsa (s_key);
case GCRY_PK_ECC:
return gkm_data_der_write_private_key_ecdsa (s_key);
default:
g_return_val_if_reached (NULL);
}
}
static gcry_cipher_hd_t
prepare_and_encode_pkcs8_cipher (GNode *asn, const gchar *password,
gsize n_password, gsize *n_block)
{
GNode *asn1_params = NULL;
gcry_cipher_hd_t cih;
guchar *salt;
gsize n_salt;
gcry_error_t gcry;
guchar *key, *iv;
gsize n_key;
int iterations;
init_quarks ();
/* Make sure the encryption algorithm works */
g_return_val_if_fail (gcry_cipher_algo_info (gcry_cipher_map_name (g_quark_to_string (OID_PKCS12_PBE_3DES_SHA1)),
GCRYCTL_TEST_ALGO, NULL, 0) == 0, NULL);
/* The encryption algorithm */
if(!egg_asn1x_set_oid_as_quark (egg_asn1x_node (asn, "encryptionAlgorithm", "algorithm", NULL),
OID_PKCS12_PBE_3DES_SHA1))
g_return_val_if_reached (NULL);
/* Randomize some input for the password based secret */
iterations = g_random_int_range (1000, 4096);
n_salt = 8;
salt = g_malloc (n_salt);
gcry_create_nonce (salt, n_salt);
/* Allocate space for the key and iv */
n_key = gcry_cipher_get_algo_keylen (GCRY_CIPHER_3DES);
*n_block = gcry_cipher_get_algo_blklen (GCRY_MD_SHA1);
g_return_val_if_fail (n_key && *n_block, NULL);
if (!egg_symkey_generate_pkcs12 (GCRY_CIPHER_3DES, GCRY_MD_SHA1,
password, n_password, salt,
sizeof (salt), iterations, &key, &iv))
g_return_val_if_reached (NULL);
/* Now write out the parameters */
asn1_params = egg_asn1x_create (pkix_asn1_tab, "pkcs-12-PbeParams");
g_return_val_if_fail (asn1_params, NULL);
egg_asn1x_set_string_as_raw (egg_asn1x_node (asn1_params, "salt", NULL), salt, n_salt, g_free);
egg_asn1x_set_integer_as_ulong (egg_asn1x_node (asn1_params, "iterations", NULL), iterations);
egg_asn1x_set_any_from (egg_asn1x_node (asn, "encryptionAlgorithm", "parameters", NULL), asn1_params);
/* Now make a cipher that matches what we wrote out */
gcry = gcry_cipher_open (&cih, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0);
g_return_val_if_fail (gcry == 0, NULL);
g_return_val_if_fail (cih, NULL);
gcry_cipher_setiv (cih, iv, *n_block);
gcry_cipher_setkey (cih, key, n_key);
g_free (iv);
egg_secure_free (key);
egg_asn1x_destroy (asn1_params);
return cih;
}
GBytes *
gkm_data_der_write_private_pkcs8_plain (gcry_sexp_t skey)
{
GNode *asn = NULL;
int algorithm;
gboolean is_priv;
GQuark oid;
GBytes *params;
GBytes *key;
GBytes *data;
init_quarks ();
/* Parse and check that the key is for real */
if (!gkm_sexp_parse_key (skey, &algorithm, &is_priv, NULL))
g_return_val_if_reached (NULL);
g_return_val_if_fail (is_priv == TRUE, NULL);
asn = egg_asn1x_create (pkix_asn1_tab, "pkcs-8-PrivateKeyInfo");
g_return_val_if_fail (asn, NULL);
/* Write out the version */
egg_asn1x_set_integer_as_ulong (egg_asn1x_node (asn, "version", NULL), 0);
/* Per algorithm differences */
switch (algorithm)
{
/* RSA gets encoded in a standard simple way */
case GCRY_PK_RSA:
oid = OID_PKIX1_RSA;
params = NULL;
key = gkm_data_der_write_private_key_rsa (skey);
break;
/* DSA gets incoded with the params seperate */
case GCRY_PK_DSA:
oid = OID_PKIX1_DSA;
key = gkm_data_der_write_private_key_dsa_part (skey);
params = gkm_data_der_write_private_key_dsa_params (skey);
break;
case GCRY_PK_ECC:
oid = OID_PKIX1_ECDSA;
params = NULL;
key = gkm_data_der_write_private_key_ecdsa (skey);
break;
default:
g_warning ("trying to serialize unsupported private key algorithm: %d", algorithm);
return NULL;
};
/* Write out the algorithm */
if (!egg_asn1x_set_oid_as_quark (egg_asn1x_node (asn, "privateKeyAlgorithm", "algorithm", NULL), oid))
g_return_val_if_reached (NULL);
/* Write out the parameters */
if (params) {
egg_asn1x_set_any_raw (egg_asn1x_node (asn, "privateKeyAlgorithm", "parameters", NULL), params);
g_bytes_unref (params);
}
/* Write out the key portion */
egg_asn1x_set_string_as_bytes (egg_asn1x_node (asn, "privateKey", NULL), key);
g_bytes_unref (key);
data = egg_asn1x_encode (asn, egg_secure_realloc);
if (data == NULL)
g_warning ("couldn't encode private pkcs8 key: %s", egg_asn1x_message (asn));
egg_asn1x_destroy (asn);
return data;
}
GBytes *
gkm_data_der_write_private_pkcs8_crypted (gcry_sexp_t skey,
const gchar *password,
gsize n_password)
{
gcry_error_t gcry;
gcry_cipher_hd_t cih;
GNode *asn = NULL;
GBytes *key, *data;
guchar *raw;
gsize n_raw, n_key;
gsize block = 0;
/* Encode the key in normal pkcs8 fashion */
key = gkm_data_der_write_private_pkcs8_plain (skey);
if (key == NULL)
return NULL;
asn = egg_asn1x_create (pkix_asn1_tab, "pkcs-8-EncryptedPrivateKeyInfo");
g_return_val_if_fail (asn, NULL);
/* Create a and write out a cipher used for encryption */
cih = prepare_and_encode_pkcs8_cipher (asn, password, n_password, &block);
g_return_val_if_fail (cih, NULL);
n_key = g_bytes_get_size (key);
/* Pad the block of data */
if(block > 1) {
gsize n_pad = block - (n_key % block);
if (n_pad == 0)
n_pad = block;
raw = egg_secure_alloc (n_key + n_pad);
memcpy (raw, g_bytes_get_data (key, NULL), n_key);
memset (raw + n_key, (int)n_pad, n_pad);
n_raw = n_key + n_pad;
/* No padding, probably stream cipher */
} else {
raw = egg_secure_alloc (n_key);
memcpy (raw, g_bytes_get_data (key, NULL), n_key);
n_raw = n_key;
}
g_bytes_unref (key);
gcry = gcry_cipher_encrypt (cih, raw, n_raw, NULL, 0);
g_return_val_if_fail (gcry == 0, NULL);
gcry_cipher_close (cih);
key = g_bytes_new_with_free_func (raw, n_raw, egg_secure_free, raw);
egg_asn1x_set_string_as_bytes (egg_asn1x_node (asn, "encryptedData", NULL), key);
g_bytes_unref (key);
data = egg_asn1x_encode (asn, NULL);
if (data == NULL)
g_warning ("couldn't encode encrypted pkcs8 key: %s", egg_asn1x_message (asn));
egg_asn1x_destroy (asn);
return data;
}
/* -----------------------------------------------------------------------------
* CERTIFICATES
*/
GkmDataResult
gkm_data_der_read_certificate (GBytes *data,
GNode **asn1)
{
*asn1 = egg_asn1x_create_and_decode (pkix_asn1_tab, "Certificate", data);
if (!*asn1)
return GKM_DATA_UNRECOGNIZED;
return GKM_DATA_SUCCESS;
}
GkmDataResult
gkm_data_der_read_basic_constraints (GBytes *data,
gboolean *is_ca,
gint *path_len)
{
GkmDataResult ret = GKM_DATA_UNRECOGNIZED;
GNode *asn = NULL;
GNode *node;
gulong value;
asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "BasicConstraints", data);
if (!asn)
goto done;
ret = GKM_DATA_FAILURE;
if (path_len) {
node = egg_asn1x_node (asn, "pathLenConstraint", NULL);
if (!egg_asn1x_have (node))
*path_len = -1;
else if (!egg_asn1x_get_integer_as_ulong (node, &value))
goto done;
else
*path_len = value;
}
if (is_ca) {
node = egg_asn1x_node (asn, "cA", NULL);
if (!egg_asn1x_have (node))
*is_ca = FALSE;
else if (!egg_asn1x_get_boolean (node, is_ca))
goto done;
}
ret = GKM_DATA_SUCCESS;
done:
egg_asn1x_destroy (asn);
if (ret == GKM_DATA_FAILURE)
g_message ("invalid basic constraints");
return ret;
}
GkmDataResult
gkm_data_der_read_key_usage (GBytes *data,
gulong *key_usage)
{
GkmDataResult ret = GKM_DATA_UNRECOGNIZED;
GNode *asn = NULL;
guint n_bits;
asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "KeyUsage", data);
if (!asn)
goto done;
ret = GKM_DATA_FAILURE;
if (!egg_asn1x_get_bits_as_ulong (asn, key_usage, &n_bits))
goto done;
ret = GKM_DATA_SUCCESS;
done:
egg_asn1x_destroy (asn);
return ret;
}
GkmDataResult
gkm_data_der_read_enhanced_usage (GBytes *data,
GQuark **usage_oids)
{
GkmDataResult ret = GKM_DATA_UNRECOGNIZED;
GNode *asn = NULL;
GNode *node;
GArray *array;
GQuark oid;
int i;
asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "ExtKeyUsageSyntax", data);
if (!asn)
goto done;
ret = GKM_DATA_FAILURE;
array = g_array_new (TRUE, TRUE, sizeof (GQuark));
for (i = 0; TRUE; ++i) {
node = egg_asn1x_node (asn, i + 1, NULL);
if (node == NULL)
break;
oid = egg_asn1x_get_oid_as_quark (node);
g_array_append_val (array, oid);
}
*usage_oids = (GQuark*)g_array_free (array, FALSE);
ret = GKM_DATA_SUCCESS;
done:
egg_asn1x_destroy (asn);
return ret;
}
GBytes *
gkm_data_der_write_certificate (GNode *asn1)
{
GBytes *result;
g_return_val_if_fail (asn1, NULL);
result = egg_asn1x_encode (asn1, NULL);
if (result == NULL)
g_warning ("couldn't encode certificate: %s", egg_asn1x_message (asn1));
return result;
}