Files
libmicrohttpd/src/daemon/https/opencdk/pubkey.c
T

1381 lines
30 KiB
C

/* pubkey.c - Public key API
* Copyright (C) 2007 Free Software Foundation, Inc.
* Copyright (C) 2002, 2003, 2007 Timo Schulz
*
* This file is part of OpenCDK.
*
* OpenCDK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* OpenCDK 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 General Public License for more details.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <gcrypt.h>
#include "opencdk.h"
#include "main.h"
#include "packet.h"
/* Convert the given secret key into a gcrypt SEXP object. */
static int
seckey_to_sexp (gcry_sexp_t * r_skey, cdk_seckey_t sk)
{
gcry_sexp_t sexp = NULL;
gcry_mpi_t *mpk = NULL, *msk = NULL;
gcry_error_t err;
cdk_pubkey_t pk;
const char *fmt;
if (!r_skey || !sk || !sk->pk)
return CDK_Inv_Value;
pk = sk->pk;
mpk = pk->mpi;
msk = sk->mpi;
*r_skey = NULL;
if (is_RSA (sk->pubkey_algo))
{
fmt = "(private-key(openpgp-rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))";
err = gcry_sexp_build (&sexp, NULL, fmt, mpk[0], mpk[1],
msk[0], msk[1], msk[2], msk[3]);
}
else if (is_ELG (sk->pubkey_algo))
{
fmt = "(private-key(openpgp-elg(p%m)(g%m)(y%m)(x%m)))";
err = gcry_sexp_build (&sexp, NULL, fmt, mpk[0], mpk[1],
mpk[2], msk[0]);
}
else if (is_DSA (sk->pubkey_algo))
{
fmt = "(private-key(openpgp-dsa(p%m)(q%m)(g%m)(y%m)(x%m)))";
err = gcry_sexp_build (&sexp, NULL, fmt, mpk[0], mpk[1], mpk[2],
mpk[3], msk[0]);
}
else
return CDK_Inv_Algo;
if (err)
return map_gcry_error (err);
*r_skey = sexp;
return 0;
}
/* Convert the given public key to a gcrypt SEXP object. */
static cdk_error_t
pubkey_to_sexp (gcry_sexp_t * r_key_sexp, cdk_pubkey_t pk)
{
gcry_mpi_t *m;
gcry_error_t err;
const char *fmt = NULL;
cdk_error_t rc = 0;
if (!r_key_sexp || !pk)
return CDK_Inv_Value;
m = pk->mpi;
if (is_RSA (pk->pubkey_algo))
{
fmt = "(public-key(openpgp-rsa(n%m)(e%m)))";
err = gcry_sexp_build (r_key_sexp, NULL, fmt, m[0], m[1]);
if (err)
rc = map_gcry_error (err);
}
else if (is_ELG (pk->pubkey_algo))
{
fmt = "(public-key(openpgp-elg(p%m)(g%m)(y%m)))";
err = gcry_sexp_build (r_key_sexp, NULL, fmt, m[0], m[1], m[2]);
if (err)
rc = map_gcry_error (err);
}
else if (is_DSA (pk->pubkey_algo))
{
fmt = "(public-key(openpgp-dsa(p%m)(q%m)(g%m)(y%m)))";
err = gcry_sexp_build (r_key_sexp, NULL, fmt, m[0], m[1], m[2], m[3]);
if (err)
rc = map_gcry_error (err);
}
else
rc = CDK_Inv_Algo;
return rc;
}
static cdk_error_t
enckey_to_sexp (gcry_sexp_t * r_sexp, gcry_mpi_t esk)
{
gcry_error_t err;
if (!r_sexp || !esk)
return CDK_Inv_Value;
err = gcry_sexp_build (r_sexp, NULL, "%m", esk);
if (err)
return map_gcry_error (err);
return 0;
}
static cdk_error_t
digest_to_sexp (gcry_sexp_t * r_md_sexp, int digest_algo,
const byte * md, size_t mdlen)
{
gcry_mpi_t m;
gcry_error_t err;
if (!r_md_sexp || !md)
return CDK_Inv_Value;
if (!mdlen)
mdlen = gcry_md_get_algo_dlen (digest_algo);
if (!mdlen)
return CDK_Inv_Algo;
err = gcry_mpi_scan (&m, GCRYMPI_FMT_USG, md, mdlen, &mdlen);
if (err)
return map_gcry_error (err);
err = gcry_sexp_build (r_md_sexp, NULL, "%m", m);
gcry_mpi_release (m);
if (err)
return map_gcry_error (err);
return 0;
}
static cdk_error_t
sexp_to_mpi (gcry_sexp_t sexp, const char *val, gcry_mpi_t * ret_buf)
{
gcry_sexp_t list;
if (!sexp || !val || !ret_buf)
return CDK_Inv_Value;
list = gcry_sexp_find_token (sexp, val, 0);
if (!list)
return CDK_Inv_Value;
*ret_buf = gcry_sexp_nth_mpi (list, 1, 0);
gcry_sexp_release (list);
if (!*ret_buf)
return CDK_Inv_Value;
return 0;
}
static cdk_error_t
sexp_to_sig (cdk_pkt_signature_t sig, gcry_sexp_t sexp)
{
if (!sig || !sexp)
return CDK_Inv_Value;
/* ElGamal signatures are not supported any longer. */
if (is_ELG (sig->pubkey_algo))
{
_cdk_log_debug ("sexp_to_sig: unsupported signature type (ElGamal)\n");
return CDK_Not_Implemented;
}
if (is_RSA (sig->pubkey_algo))
return sexp_to_mpi (sexp, "s", &sig->mpi[0]);
else if (is_DSA (sig->pubkey_algo))
{
cdk_error_t rc;
rc = sexp_to_mpi (sexp, "r", &sig->mpi[0]);
if (!rc)
rc = sexp_to_mpi (sexp, "s", &sig->mpi[1]);
return rc;
}
return CDK_Inv_Algo;
}
static cdk_error_t
sig_to_sexp (gcry_sexp_t * r_sig_sexp, cdk_pkt_signature_t sig)
{
gcry_error_t err;
cdk_error_t rc;
const char *fmt;
if (!r_sig_sexp || !sig)
return CDK_Inv_Value;
if (is_ELG (sig->pubkey_algo))
return CDK_Not_Implemented;
rc = 0;
if (is_RSA (sig->pubkey_algo))
{
fmt = "(sig-val(openpgp-rsa(s%m)))";
err = gcry_sexp_build (r_sig_sexp, NULL, fmt, sig->mpi[0]);
if (err)
rc = map_gcry_error (err);
}
else if (is_DSA (sig->pubkey_algo))
{
fmt = "(sig-val(openpgp-dsa(r%m)(s%m)))";
err = gcry_sexp_build (r_sig_sexp, NULL, fmt, sig->mpi[0], sig->mpi[1]);
if (err)
rc = map_gcry_error (err);
}
else
rc = CDK_Inv_Algo;
return rc;
}
static cdk_error_t
sexp_to_pubenc (cdk_pkt_pubkey_enc_t enc, gcry_sexp_t sexp)
{
if (!sexp || !enc)
return CDK_Inv_Value;
if (is_RSA (enc->pubkey_algo))
return sexp_to_mpi (sexp, "a", &enc->mpi[0]);
else if (is_ELG (enc->pubkey_algo))
{
cdk_error_t rc = sexp_to_mpi (sexp, "a", &enc->mpi[0]);
if (!rc)
rc = sexp_to_mpi (sexp, "b", &enc->mpi[1]);
return rc;
}
return CDK_Inv_Algo;
}
static cdk_error_t
pubenc_to_sexp (gcry_sexp_t * r_sexp, cdk_pkt_pubkey_enc_t enc)
{
gcry_sexp_t sexp = NULL;
gcry_error_t err;
const char *fmt;
if (!r_sexp || !enc)
return CDK_Inv_Value;
*r_sexp = NULL;
if (is_RSA (enc->pubkey_algo))
{
fmt = "(enc-val(openpgp-rsa((a%m))))";
err = gcry_sexp_build (&sexp, NULL, fmt, enc->mpi[0]);
}
else if (is_ELG (enc->pubkey_algo))
{
fmt = "(enc-val(openpgp-elg((a%m)(b%m))))";
err = gcry_sexp_build (&sexp, NULL, fmt, enc->mpi[0], enc->mpi[1]);
}
else
return CDK_Inv_Algo;
if (err)
return map_gcry_error (err);
*r_sexp = sexp;
return 0;
}
static int
is_unprotected (cdk_seckey_t sk)
{
if (sk->is_protected && !sk->mpi[0])
return 0;
return 1;
}
/**
* cdk_pk_encrypt:
* @pk: the public key
* @pke: the public key encrypted packet
* @esk: the actual session key
*
* Encrypt the session key in @esk and write its encrypted content
* into the @pke struct.
**/
cdk_error_t
cdk_pk_encrypt (cdk_pubkey_t pk, cdk_pkt_pubkey_enc_t pke, gcry_mpi_t esk)
{
gcry_sexp_t s_data = NULL, s_pkey = NULL, s_ciph = NULL;
gcry_error_t err;
cdk_error_t rc;
if (!pk || !esk || !pke)
return CDK_Inv_Value;
if (!KEY_CAN_ENCRYPT (pk->pubkey_algo))
return CDK_Inv_Algo;
rc = enckey_to_sexp (&s_data, esk);
if (!rc)
rc = pubkey_to_sexp (&s_pkey, pk);
if (!rc)
{
err = gcry_pk_encrypt (&s_ciph, s_data, s_pkey);
if (err)
return map_gcry_error (err);
}
if (!rc)
rc = sexp_to_pubenc (pke, s_ciph);
gcry_sexp_release (s_data);
gcry_sexp_release (s_pkey);
gcry_sexp_release (s_ciph);
return rc;
}
/**
* cdk_pk_decrypt:
* @sk: the secret key
* @pke: public key encrypted packet
* @r_sk: the object to store the plain session key
*
* Decrypt the encrypted session key from @pke into @r_sk.
**/
cdk_error_t
cdk_pk_decrypt (cdk_seckey_t sk, cdk_pkt_pubkey_enc_t pke, gcry_mpi_t * r_sk)
{
gcry_sexp_t s_data = NULL, s_skey = NULL, s_plain = NULL;
cdk_error_t rc;
gcry_error_t err;
if (!sk || !r_sk || !pke)
return CDK_Inv_Value;
if (!is_unprotected (sk))
return CDK_Inv_Mode;
*r_sk = NULL;
rc = seckey_to_sexp (&s_skey, sk);
if (rc)
return rc;
rc = pubenc_to_sexp (&s_data, pke);
if (rc)
{
gcry_sexp_release (s_skey);
return rc;
}
err = gcry_pk_decrypt (&s_plain, s_data, s_skey);
if (err)
rc = map_gcry_error (err);
else
*r_sk = gcry_sexp_nth_mpi (s_plain, 0, 0);
gcry_sexp_release (s_data);
gcry_sexp_release (s_skey);
gcry_sexp_release (s_plain);
return rc;
}
/**
* cdk_pk_sign:
* @sk: secret key
* @sig: signature
* @md: the message digest
*
* Sign the message digest from @md and write the result into @sig.
**/
cdk_error_t
cdk_pk_sign (cdk_seckey_t sk, cdk_pkt_signature_t sig, const byte * md)
{
gcry_sexp_t s_skey = NULL, s_sig = NULL, s_hash = NULL;
byte *encmd = NULL;
size_t enclen = 0;
int nbits;
cdk_error_t rc;
gcry_error_t err;
if (!sk || !sk->pk || !sig || !md)
return CDK_Inv_Value;
if (!is_unprotected (sk))
return CDK_Inv_Mode;
if (!KEY_CAN_SIGN (sig->pubkey_algo))
return CDK_Inv_Algo;
nbits = cdk_pk_get_nbits (sk->pk);
rc = _cdk_digest_encode_pkcs1 (&encmd, &enclen, sk->pk->pubkey_algo, md,
sig->digest_algo, nbits);
if (rc)
return rc;
rc = seckey_to_sexp (&s_skey, sk);
if (!rc)
rc = digest_to_sexp (&s_hash, sig->digest_algo, encmd, enclen);
if (rc)
{
cdk_free (encmd);
gcry_sexp_release (s_skey);
return rc;
}
err = gcry_pk_sign (&s_sig, s_hash, s_skey);
if (err)
rc = map_gcry_error (err);
else
{
rc = sexp_to_sig (sig, s_sig);
if (!rc)
{
sig->digest_start[0] = md[0];
sig->digest_start[1] = md[1];
}
}
gcry_sexp_release (s_skey);
gcry_sexp_release (s_hash);
gcry_sexp_release (s_sig);
cdk_free (encmd);
return rc;
}
/**
* cdk_pk_verify:
* @pk: the public key
* @sig: signature
* @md: the message digest
*
* Verify the signature in @sig and compare it with the message digest in @md.
**/
cdk_error_t
cdk_pk_verify (cdk_pubkey_t pk, cdk_pkt_signature_t sig, const byte * md)
{
gcry_sexp_t s_pkey = NULL, s_sig = NULL, s_hash = NULL;
byte *encmd = NULL;
size_t enclen;
cdk_error_t rc;
if (!pk || !sig || !md)
return CDK_Inv_Value;
rc = pubkey_to_sexp (&s_pkey, pk);
if (rc)
return rc;
rc = sig_to_sexp (&s_sig, sig);
if (rc)
goto leave;
rc = _cdk_digest_encode_pkcs1 (&encmd, &enclen, pk->pubkey_algo, md,
sig->digest_algo, cdk_pk_get_nbits (pk));
if (rc)
goto leave;
rc = digest_to_sexp (&s_hash, sig->digest_algo, encmd, enclen);
if (rc)
goto leave;
if (gcry_pk_verify (s_sig, s_hash, s_pkey))
rc = CDK_Bad_Sig;
leave:
gcry_sexp_release (s_sig);
gcry_sexp_release (s_hash);
gcry_sexp_release (s_pkey);
cdk_free (encmd);
return rc;
}
/**
* cdk_pk_get_nbits:
* @pk: the public key
*
* Return the length of the public key in bits.
* The meaning of length is actually the size of the 'prime'
* object in the key. For RSA keys the modulus, for ElG/DSA
* the size of the public prime.
**/
int
cdk_pk_get_nbits (cdk_pubkey_t pk)
{
if (!pk || !pk->mpi[0])
return 0;
return gcry_mpi_get_nbits (pk->mpi[0]);
}
/**
* cdk_pk_get_npkey:
* @algo: The public key algorithm.
*
* Return the number of multiprecison integer forming an public
* key with the given algorithm.
*/
int
cdk_pk_get_npkey (int algo)
{
size_t bytes;
if (algo == 16)
algo = 20; /* FIXME: libgcrypt returns 0 for 16 */
if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &bytes))
return 0;
return bytes;
}
/**
* cdk_pk_get_nskey:
* @algo: the public key algorithm
*
* Return the number of multiprecision integers forming an
* secret key with the given algorithm.
**/
int
cdk_pk_get_nskey (int algo)
{
size_t bytes;
if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &bytes))
return 0;
bytes -= cdk_pk_get_npkey (algo);
return bytes;
}
/**
* cdk_pk_get_nbits:
* @algo: the public key algorithm
*
* Return the number of MPIs a signature consists of.
**/
int
cdk_pk_get_nsig (int algo)
{
size_t bytes;
if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NSIGN, NULL, &bytes))
return 0;
return bytes;
}
/**
* cdk_pk_get_nenc:
* @algo: the public key algorithm
*
* Return the number of MPI's the encrypted data consists of.
**/
int
cdk_pk_get_nenc (int algo)
{
size_t bytes;
if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NENCR, NULL, &bytes))
return 0;
return bytes;
}
int
_cdk_pk_algo_usage (int algo)
{
int usage;
/* The ElGamal sign+encrypt algorithm is not supported any longer. */
switch (algo)
{
case CDK_PK_RSA:
usage = CDK_KEY_USG_SIGN | CDK_KEY_USG_ENCR;
break;
case CDK_PK_RSA_E:
usage = CDK_KEY_USG_ENCR;
break;
case CDK_PK_RSA_S:
usage = CDK_KEY_USG_SIGN;
break;
case CDK_PK_ELG_E:
usage = CDK_KEY_USG_ENCR;
break;
case CDK_PK_DSA:
usage = CDK_KEY_USG_SIGN;
break;
default:
usage = 0;
}
return usage;
}
static cdk_error_t
mpi_to_buffer (gcry_mpi_t a, byte * buf, size_t buflen,
size_t * r_nwritten, size_t * r_nbits)
{
size_t nbits;
if (!a || !buf || !r_nwritten)
return CDK_Inv_Value;
nbits = gcry_mpi_get_nbits (a);
if (r_nbits)
*r_nbits = nbits;
if ((nbits + 7) / 8 + 2 > buflen)
return CDK_Too_Short;
*r_nwritten = (nbits + 7) / 8 + 2;
if (gcry_mpi_print (GCRYMPI_FMT_PGP, buf, buflen, r_nwritten, a))
return CDK_Wrong_Format;
return 0;
}
/**
* cdk_pk_get_mpi:
* @pk: public key
* @idx: index of the MPI to retrieve
* @buf: buffer to hold the raw data
* @r_nwritten: output how large the raw data is
* @r_nbits: size of the MPI in bits.
*
* Return the MPI with the given index of the public key.
**/
cdk_error_t
cdk_pk_get_mpi (cdk_pubkey_t pk, size_t idx,
byte * buf, size_t buflen, size_t * r_nwritten,
size_t * r_nbits)
{
if (!pk || !r_nwritten)
return CDK_Inv_Value;
if (idx > cdk_pk_get_npkey (pk->pubkey_algo))
return CDK_Inv_Value;
return mpi_to_buffer (pk->mpi[idx], buf, buflen, r_nwritten, r_nbits);
}
/**
* cdk_sk_get_mpi:
* @sk: secret key
* @idx: index of the MPI to retrieve
* @buf: buffer to hold the raw data
* @r_nwritten: output length of the raw data
* @r_nbits: length of the MPI data in bits.
*
* Return the MPI of the given secret key with the
* index @idx. It is important to check if the key
* is protected and thus no real MPI data will be returned then.
**/
cdk_error_t
cdk_sk_get_mpi (cdk_pkt_seckey_t sk, size_t idx,
byte * buf, size_t buflen, size_t * r_nwritten,
size_t * r_nbits)
{
if (!sk || !r_nwritten)
return CDK_Inv_Value;
if (idx > cdk_pk_get_nskey (sk->pubkey_algo))
return CDK_Inv_Value;
return mpi_to_buffer (sk->mpi[idx], buf, buflen, r_nwritten, r_nbits);
}
static u16
checksum_mpi (gcry_mpi_t m)
{
byte buf[MAX_MPI_BYTES + 2];
size_t nread;
int i;
u16 chksum = 0;
if (!m)
return 0;
if (gcry_mpi_print (GCRYMPI_FMT_PGP, buf, DIM (buf), &nread, m))
return 0;
for (i = 0; i < nread; i++)
chksum += buf[i];
return chksum;
}
/**
* cdk_sk_unprotect:
* @sk: the secret key
* @pw: the passphrase
*
* Unprotect the given secret key with the passphrase.
**/
cdk_error_t
cdk_sk_unprotect (cdk_pkt_seckey_t sk, const char *pw)
{
gcry_cipher_hd_t hd;
cdk_dek_t dek = NULL;
byte *data = NULL;
u16 chksum = 0;
size_t ndata, nbits, nbytes;
int i, dlen, pos = 0, nskey;
cdk_error_t rc;
gcry_error_t err;
if (!sk)
return CDK_Inv_Value;
nskey = cdk_pk_get_nskey (sk->pubkey_algo);
if (!sk->is_protected)
{
chksum = 0;
for (i = 0; i < nskey; i++)
chksum += checksum_mpi (sk->mpi[i]);
if (chksum != sk->csum)
return CDK_Chksum_Error;
}
rc = cdk_dek_from_passphrase (&dek, sk->protect.algo,
sk->protect.s2k, 0, pw);
if (rc)
return rc;
err = gcry_cipher_open (&hd, sk->protect.algo, GCRY_CIPHER_MODE_CFB,
GCRY_CIPHER_ENABLE_SYNC);
if (!err)
err = gcry_cipher_setiv (hd, sk->protect.iv, sk->protect.ivlen);
if (!err)
err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
if (err)
{
cdk_free (dek);
return map_gcry_error (err);
}
cdk_dek_free (dek);
chksum = 0;
if (sk->version == 4)
{
ndata = sk->enclen;
data = cdk_salloc (ndata, 1);
if (!data)
return CDK_Out_Of_Core;
gcry_cipher_decrypt (hd, data, ndata, sk->encdata, ndata);
if (sk->protect.sha1chk)
{
/* This is the new SHA1 checksum method to detect tampering
with the key as used by the Klima/Rosa attack */
sk->csum = 0;
chksum = 1;
dlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1);
if (ndata < dlen)
{
cdk_free (data);
return CDK_Inv_Packet;
}
else
{
byte mdcheck[20];
gcry_md_hash_buffer (GCRY_MD_SHA1, mdcheck, data, ndata - dlen);
if (!memcmp (mdcheck, data + ndata - dlen, dlen))
chksum = 0; /* Digest does match */
}
}
else
{
for (i = 0; i < ndata - 2; i++)
chksum += data[i];
sk->csum = data[ndata - 2] << 8 | data[ndata - 1];
}
if (sk->csum == chksum)
{
for (i = 0; i < nskey; i++)
{
nbits = data[pos] << 8 | data[pos + 1];
if (gcry_mpi_scan (&sk->mpi[i], GCRYMPI_FMT_PGP, data,
(nbits + 7) / 8 + 2, &nbytes))
{
wipemem (data, sk->enclen);
cdk_free (data);
return CDK_Wrong_Format;
}
gcry_mpi_set_flag (sk->mpi[i], GCRYMPI_FLAG_SECURE);
pos += (nbits + 7) / 8 + 2;
}
}
wipemem (data, sk->enclen);
cdk_free (data);
}
else
{
byte buf[MAX_MPI_BYTES + 2];
chksum = 0;
for (i = 0; i < nskey; i++)
{
gcry_cipher_sync (hd);
gcry_mpi_print (GCRYMPI_FMT_PGP, buf, DIM (buf),
&nbytes, sk->mpi[i]);
gcry_cipher_decrypt (hd, buf + 2, nbytes - 2, NULL, 0);
gcry_mpi_release (sk->mpi[i]);
if (gcry_mpi_scan (&sk->mpi[i], GCRYMPI_FMT_PGP,
buf, nbytes, &nbytes))
return CDK_Wrong_Format;
chksum += checksum_mpi (sk->mpi[i]);
}
}
gcry_cipher_close (hd);
if (chksum != sk->csum)
return CDK_Chksum_Error;
sk->is_protected = 0;
return 0;
}
/**
* cdk_sk_protect:
* @sk: the secret key
* @pw: the passphrase to use
*
* Protect the given secret key with a passphrase.
**/
cdk_error_t
cdk_sk_protect (cdk_pkt_seckey_t sk, const char *pw)
{
gcry_cipher_hd_t hd = NULL;
cdk_dek_t dek = NULL;
cdk_s2k_t s2k;
byte *p = NULL, buf[MAX_MPI_BYTES + 2];
size_t enclen = 0, nskey, i, nbytes;
size_t dlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1);
gcry_error_t err;
cdk_error_t rc;
nskey = cdk_pk_get_nskey (sk->pubkey_algo);
if (!nskey)
return CDK_Inv_Algo;
rc = cdk_s2k_new (&s2k, CDK_S2K_ITERSALTED, GCRY_MD_SHA256, NULL);
if (!rc)
rc = cdk_dek_from_passphrase (&dek, GCRY_CIPHER_AES, s2k, 1, pw);
if (rc)
{
cdk_s2k_free (s2k);
return rc;
}
for (i = 0; i < nskey; i++)
{
enclen += 2;
enclen += (gcry_mpi_get_nbits (sk->mpi[i]) + 7) / 8;
}
p = sk->encdata = cdk_calloc (1, enclen + dlen + 1);
if (!p)
{
cdk_s2k_free (s2k);
return CDK_Out_Of_Core;
}
enclen = 0;
for (i = 0; i < nskey; i++)
{
if (gcry_mpi_print (GCRYMPI_FMT_PGP, buf,
DIM (buf), &nbytes, sk->mpi[i]))
{
cdk_free (p);
cdk_s2k_free (s2k);
return CDK_Wrong_Format;
}
memcpy (p + enclen, buf, nbytes);
enclen += nbytes;
}
enclen += dlen;
sk->enclen = enclen;
sk->protect.s2k = s2k;
sk->protect.algo = GCRY_CIPHER_AES;
sk->protect.ivlen = gcry_cipher_get_algo_blklen (sk->protect.algo);
gcry_randomize (sk->protect.iv, sk->protect.ivlen, GCRY_STRONG_RANDOM);
err = gcry_cipher_open (&hd, sk->protect.algo, GCRY_CIPHER_MODE_CFB,
GCRY_CIPHER_ENABLE_SYNC);
if (err)
{
cdk_dek_free (dek);
rc = map_gcry_error (err);
goto leave;
}
err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
if (!err)
err = gcry_cipher_setiv (hd, sk->protect.iv, sk->protect.ivlen);
cdk_dek_free (dek);
if (err)
{
rc = map_gcry_error (err);
goto leave;
}
sk->protect.sha1chk = 1;
sk->is_protected = 1;
sk->csum = 0;
gcry_md_hash_buffer (GCRY_MD_SHA1, buf, p, enclen - dlen);
memcpy (p + enclen - dlen, buf, dlen);
gcry_cipher_encrypt (hd, p, enclen, NULL, 0);
/* FIXME: We should release all MPI's and set the elements to NULL. */
leave:
gcry_cipher_close (hd);
return rc;
}
/**
* cdk_pk_from_secret_key:
* @sk: the secret key
* @ret_pk: the new public key
*
* Create a new public key from a secret key.
**/
cdk_error_t
cdk_pk_from_secret_key (cdk_pkt_seckey_t sk, cdk_pubkey_t * ret_pk)
{
if (!sk)
return CDK_Inv_Value;
return _cdk_copy_pubkey (ret_pk, sk->pk);
}
#if 0 /* FIXME: Code is not finished yet. */
cdk_error_t
cdk_pk_revoke_cert_create (cdk_pkt_seckey_t sk, int code, const char *inf,
char **ret_revcert)
{
gcry_md_hd_t md;
cdk_subpkt_t node;
cdk_pkt_signature_t sig;
char *p = NULL, *dat;
gcry_error_t err;
cdk_error_t rc = 0;
size_t n;
if (!sk || !ret_revcert)
return CDK_Inv_Value;
if (code < 0 || code > 3)
return CDK_Inv_Value;
sig = cdk_calloc (1, sizeof *sig);
if (!sig)
return CDK_Out_Of_Core;
_cdk_sig_create (sk->pk, sig);
n = 1;
if (inf)
{
n += strlen (p);
p = cdk_utf8_encode (inf);
}
dat = cdk_calloc (1, n + 1);
if (!dat)
{
_cdk_free_signature (sig);
return CDK_Out_Of_Core;
}
dat[0] = code;
if (inf)
memcpy (dat + 1, p, strlen (p));
cdk_free (p);
node = cdk_subpkt_new (n);
if (node)
{
cdk_subpkt_init (node, CDK_SIGSUBPKT_REVOC_REASON, dat, n);
cdk_subpkt_add (sig->hashed, node);
}
cdk_free (dat);
err = gcry_md_open (&md, GCRY_MD_SHA1, 0);
if (err)
rc = map_gcry_error (err);
else
_cdk_hash_pubkey (sk->pk, md, 0);
_cdk_free_signature (sig);
return rc;
}
#endif
int
_cdk_sk_get_csum (cdk_pkt_seckey_t sk)
{
u16 csum = 0, i;
if (!sk)
return 0;
for (i = 0; i < cdk_pk_get_nskey (sk->pubkey_algo); i++)
csum += checksum_mpi (sk->mpi[i]);
return csum;
}
/**
* cdk_pk_get_fingerprint:
* @pk: the public key
* @fpr: the buffer to hold the fingerprint
*
* Return the fingerprint of the given public key.
* The buffer must be at least 20 octets.
* This function should be considered deprecated and
* the new cdk_pk_to_fingerprint() should be used whenever
* possible to avoid overflows.
**/
cdk_error_t
cdk_pk_get_fingerprint (cdk_pubkey_t pk, byte * fpr)
{
gcry_md_hd_t hd;
int md_algo;
int dlen = 0;
gcry_error_t err;
if (!pk || !fpr)
return CDK_Inv_Value;
if (pk->version < 4 && is_RSA (pk->pubkey_algo))
md_algo = GCRY_MD_MD5; /* special */
else
md_algo = GCRY_MD_SHA1;
dlen = gcry_md_get_algo_dlen (md_algo);
err = gcry_md_open (&hd, md_algo, 0);
if (err)
return map_gcry_error (err);
_cdk_hash_pubkey (pk, hd, 1);
gcry_md_final (hd);
memcpy (fpr, gcry_md_read (hd, md_algo), dlen);
gcry_md_close (hd);
if (dlen == 16)
memset (fpr + 16, 0, 4);
return 0;
}
/**
* cdk_pk_to_fingerprint:
* @pk: the public key
* @fprbuf: buffer to save the fingerprint
* @fprbuflen: buffer size
* @r_nout: actual length of the fingerprint.
*
* Calculate a fingerprint of the given key and
* return it in the given byte array.
**/
cdk_error_t
cdk_pk_to_fingerprint (cdk_pubkey_t pk,
byte * fprbuf, size_t fprbuflen, size_t * r_nout)
{
size_t key_fprlen;
cdk_error_t err;
if (!pk)
return CDK_Inv_Value;
if (pk->version < 4)
key_fprlen = 16;
else
key_fprlen = 20;
/* Only return the required buffer size for the fingerprint. */
if (!fprbuf && !fprbuflen && r_nout)
{
*r_nout = key_fprlen;
return 0;
}
if (!fprbuf || key_fprlen > fprbuflen)
return CDK_Too_Short;
err = cdk_pk_get_fingerprint (pk, fprbuf);
if (r_nout)
*r_nout = key_fprlen;
return err;
}
/**
* cdk_pk_fingerprint_get_keyid:
* @fpr: the key fingerprint
* @fprlen: the length of the fingerprint
*
* Derive the key ID from the key fingerprint.
* For version 3 keys, this is not working.
**/
u32
cdk_pk_fingerprint_get_keyid (const byte * fpr, size_t fprlen, u32 * keyid)
{
u32 lowbits = 0;
/* In this case we say the key is a V3 RSA key and we can't
use the fingerprint to get the keyid. */
if (fpr && fprlen == 16)
{
keyid[0] = 0;
keyid[1] = 0;
return 0;
}
else if (keyid && fpr)
{
keyid[0] = _cdk_buftou32 (fpr + 12);
keyid[1] = _cdk_buftou32 (fpr + 16);
lowbits = keyid[1];
}
else if (fpr)
lowbits = _cdk_buftou32 (fpr + 16);
return lowbits;
}
/**
* cdk_pk_get_keyid:
* @pk: the public key
* @keyid: buffer to store the key ID
*
* Calculate the key ID of the given public key.
**/
u32
cdk_pk_get_keyid (cdk_pubkey_t pk, u32 * keyid)
{
u32 lowbits = 0;
byte buf[24];
if (pk && (!pk->keyid[0] || !pk->keyid[1]))
{
if (pk->version < 4 && is_RSA (pk->pubkey_algo))
{
byte p[MAX_MPI_BYTES];
size_t n;
gcry_mpi_print (GCRYMPI_FMT_USG, p, MAX_MPI_BYTES, &n, pk->mpi[0]);
pk->keyid[0] =
p[n - 8] << 24 | p[n - 7] << 16 | p[n - 6] << 8 | p[n - 5];
pk->keyid[1] =
p[n - 4] << 24 | p[n - 3] << 16 | p[n - 2] << 8 | p[n - 1];
}
else if (pk->version == 4)
{
cdk_pk_get_fingerprint (pk, buf);
pk->keyid[0] = _cdk_buftou32 (buf + 12);
pk->keyid[1] = _cdk_buftou32 (buf + 16);
}
}
lowbits = pk ? pk->keyid[1] : 0;
if (keyid && pk)
{
keyid[0] = pk->keyid[0];
keyid[1] = pk->keyid[1];
}
return lowbits;
}
/**
* cdk_sk_get_keyid:
* @sk: the secret key
* @keyid: buffer to hold the key ID
*
* Calculate the key ID of the secret key, actually the public key.
**/
u32
cdk_sk_get_keyid (cdk_pkt_seckey_t sk, u32 * keyid)
{
u32 lowbits = 0;
if (sk && sk->pk)
{
lowbits = cdk_pk_get_keyid (sk->pk, keyid);
sk->keyid[0] = sk->pk->keyid[0];
sk->keyid[1] = sk->pk->keyid[1];
}
return lowbits;
}
/**
* cdk_sig_get_keyid:
* @sig: the signature
* @keyid: buffer to hold the key ID
*
* Retrieve the key ID from the given signature.
**/
u32
cdk_sig_get_keyid (cdk_pkt_signature_t sig, u32 * keyid)
{
u32 lowbits = sig ? sig->keyid[1] : 0;
if (keyid && sig)
{
keyid[0] = sig->keyid[0];
keyid[1] = sig->keyid[1];
}
return lowbits;
}
/* Return the key ID from the given packet.
If this is not possible, 0 is returned */
u32
_cdk_pkt_get_keyid (cdk_packet_t pkt, u32 * keyid)
{
u32 lowbits;
if (!pkt)
return 0;
switch (pkt->pkttype)
{
case CDK_PKT_PUBLIC_KEY:
case CDK_PKT_PUBLIC_SUBKEY:
lowbits = cdk_pk_get_keyid (pkt->pkt.public_key, keyid);
break;
case CDK_PKT_SECRET_KEY:
case CDK_PKT_SECRET_SUBKEY:
lowbits = cdk_sk_get_keyid (pkt->pkt.secret_key, keyid);
break;
case CDK_PKT_SIGNATURE:
lowbits = cdk_sig_get_keyid (pkt->pkt.signature, keyid);
break;
default:
lowbits = 0;
break;
}
return lowbits;
}
/* Get the fingerprint of the packet if possible. */
int
_cdk_pkt_get_fingerprint (cdk_packet_t pkt, byte * fpr)
{
if (!pkt || !fpr)
return CDK_Inv_Value;
switch (pkt->pkttype)
{
case CDK_PKT_PUBLIC_KEY:
case CDK_PKT_PUBLIC_SUBKEY:
return cdk_pk_get_fingerprint (pkt->pkt.public_key, fpr);
case CDK_PKT_SECRET_KEY:
case CDK_PKT_SECRET_SUBKEY:
return cdk_pk_get_fingerprint (pkt->pkt.secret_key->pk, fpr);
default:
return CDK_Inv_Mode;
}
return 0;
}
/**
* cdk_pubkey_to_sexp:
* @pk: the public key
* @sexp: where to store the S-expression
* @len: the length of sexp
*
* Convert a public key to an S-expression. sexp is allocated by this
* function, but you have to cdk_free() it yourself. The S-expression
* is stored in canonical format as used by libgcrypt
* (GCRYSEXP_FMT_CANON).
**/
cdk_error_t
cdk_pubkey_to_sexp (cdk_pubkey_t pk, char **sexp, size_t * len)
{
char *buf;
size_t sexp_len;
gcry_sexp_t pk_sexp;
cdk_error_t rc;
if (!pk || !sexp)
return CDK_Inv_Value;
rc = pubkey_to_sexp (&pk_sexp, pk);
if (rc)
return rc;
sexp_len = gcry_sexp_sprint (pk_sexp, GCRYSEXP_FMT_CANON, NULL, 0);
if (!sexp_len)
return CDK_Wrong_Format;
buf = (char *) cdk_malloc (sexp_len);
if (!buf)
{
gcry_sexp_release (pk_sexp);
return CDK_Out_Of_Core;
}
sexp_len = gcry_sexp_sprint (pk_sexp, GCRYSEXP_FMT_CANON, buf, sexp_len);
gcry_sexp_release (pk_sexp);
if (!sexp_len)
{
cdk_free (buf);
return CDK_Wrong_Format;
}
if (len)
*len = sexp_len;
*sexp = buf;
return CDK_Success;
}
/**
* cdk_seckey_to_sexp:
* @sk: the secret key
* @sexp: where to store the S-expression
* @len: the length of sexp
*
* Convert a public key to an S-expression. sexp is allocated by this
* function, but you have to cdk_free() it yourself. The S-expression
* is stored in canonical format as used by libgcrypt
* (GCRYSEXP_FMT_CANON).
**/
cdk_error_t
cdk_seckey_to_sexp (cdk_pkt_seckey_t sk, char **sexp, size_t * len)
{
char *buf;
size_t sexp_len;
gcry_sexp_t sk_sexp;
cdk_error_t rc;
if (!sk || !sexp)
return CDK_Inv_Value;
rc = seckey_to_sexp (&sk_sexp, sk);
if (rc)
return rc;
sexp_len = gcry_sexp_sprint (sk_sexp, GCRYSEXP_FMT_CANON, NULL, 0);
if (!sexp_len)
return CDK_Wrong_Format;
buf = (char *) cdk_malloc (sexp_len);
if (!buf)
{
gcry_sexp_release (sk_sexp);
return CDK_Out_Of_Core;
}
sexp_len = gcry_sexp_sprint (sk_sexp, GCRYSEXP_FMT_CANON, buf, sexp_len);
gcry_sexp_release (sk_sexp);
if (!sexp_len)
{
cdk_free (buf);
return CDK_Wrong_Format;
}
if (len)
*len = sexp_len;
*sexp = buf;
return CDK_Success;
}