556 lines
13 KiB
C
556 lines
13 KiB
C
#include "system.h"
|
|
|
|
#include <gcrypt.h>
|
|
|
|
#include <rpm/rpmcrypto.h>
|
|
#include "rpmio/rpmpgp_internal.h"
|
|
#include "rpmio/rpmio_internal.h"
|
|
#include "debug.h"
|
|
|
|
/**
|
|
* MD5/SHA1 digest private data.
|
|
*/
|
|
struct DIGEST_CTX_s {
|
|
rpmDigestFlags flags; /*!< Bit(s) to control digest operation. */
|
|
int algo; /*!< Used hash algorithm */
|
|
gcry_md_hd_t h;
|
|
};
|
|
|
|
|
|
/**************************** init ************************************/
|
|
|
|
int rpmInitCrypto(void) {
|
|
gcry_check_version (NULL);
|
|
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
|
|
return 0;
|
|
}
|
|
|
|
int rpmFreeCrypto(void) {
|
|
return 0;
|
|
}
|
|
|
|
/**************************** digest ************************************/
|
|
|
|
size_t rpmDigestLength(int hashalgo)
|
|
{
|
|
switch (hashalgo) {
|
|
case RPM_HASH_MD5:
|
|
return 16;
|
|
case RPM_HASH_SHA1:
|
|
return 20;
|
|
case RPM_HASH_SHA224:
|
|
return 28;
|
|
case RPM_HASH_SHA256:
|
|
return 32;
|
|
case RPM_HASH_SHA384:
|
|
return 48;
|
|
case RPM_HASH_SHA512:
|
|
return 64;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int hashalgo2gcryalgo(int hashalgo)
|
|
{
|
|
switch (hashalgo) {
|
|
case RPM_HASH_MD5:
|
|
return GCRY_MD_MD5;
|
|
case RPM_HASH_SHA1:
|
|
return GCRY_MD_SHA1;
|
|
case RPM_HASH_SHA224:
|
|
return GCRY_MD_SHA224;
|
|
case RPM_HASH_SHA256:
|
|
return GCRY_MD_SHA256;
|
|
case RPM_HASH_SHA384:
|
|
return GCRY_MD_SHA384;
|
|
case RPM_HASH_SHA512:
|
|
return GCRY_MD_SHA512;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
DIGEST_CTX rpmDigestInit(int hashalgo, rpmDigestFlags flags)
|
|
{
|
|
gcry_md_hd_t h;
|
|
DIGEST_CTX ctx;
|
|
int gcryalgo = hashalgo2gcryalgo(hashalgo);
|
|
|
|
if (!gcryalgo || gcry_md_open(&h, gcryalgo, 0) != 0)
|
|
return NULL;
|
|
|
|
ctx = xcalloc(1, sizeof(*ctx));
|
|
ctx->flags = flags;
|
|
ctx->algo = hashalgo;
|
|
ctx->h = h;
|
|
return ctx;
|
|
}
|
|
|
|
int rpmDigestUpdate(DIGEST_CTX ctx, const void * data, size_t len)
|
|
{
|
|
if (ctx == NULL)
|
|
return -1;
|
|
gcry_md_write(ctx->h, data, len);
|
|
return 0;
|
|
}
|
|
|
|
int rpmDigestFinal(DIGEST_CTX ctx, void ** datap, size_t *lenp, int asAscii)
|
|
{
|
|
unsigned char *digest;
|
|
int digestlen;
|
|
if (ctx == NULL)
|
|
return -1;
|
|
digest = gcry_md_read(ctx->h, 0);
|
|
digestlen = rpmDigestLength(ctx->algo);
|
|
if (!asAscii) {
|
|
if (lenp)
|
|
*lenp = digestlen;
|
|
if (datap) {
|
|
*datap = xmalloc(digestlen);
|
|
memcpy(*datap, digest, digestlen);
|
|
}
|
|
} else {
|
|
if (lenp)
|
|
*lenp = 2 * digestlen + 1;
|
|
if (datap) {
|
|
*datap = rpmhex((const uint8_t *)digest, digestlen);
|
|
}
|
|
}
|
|
gcry_md_close(ctx->h);
|
|
free(ctx);
|
|
return 0;
|
|
}
|
|
|
|
DIGEST_CTX rpmDigestDup(DIGEST_CTX octx)
|
|
{
|
|
DIGEST_CTX nctx = NULL;
|
|
if (octx) {
|
|
gcry_md_hd_t h;
|
|
if (gcry_md_copy(&h, octx->h))
|
|
return NULL;
|
|
nctx = memcpy(xcalloc(1, sizeof(*nctx)), octx, sizeof(*nctx));
|
|
nctx->h = h;
|
|
}
|
|
return nctx;
|
|
}
|
|
|
|
|
|
/****************************** RSA **************************************/
|
|
|
|
struct pgpDigSigRSA_s {
|
|
gcry_mpi_t s;
|
|
};
|
|
|
|
struct pgpDigKeyRSA_s {
|
|
gcry_mpi_t n;
|
|
gcry_mpi_t e;
|
|
};
|
|
|
|
static int pgpSetSigMpiRSA(pgpDigAlg pgpsig, int num, const uint8_t *p)
|
|
{
|
|
struct pgpDigSigRSA_s *sig = pgpsig->data;
|
|
int mlen = pgpMpiLen(p);
|
|
int rc = 1;
|
|
|
|
if (!sig)
|
|
sig = pgpsig->data = xcalloc(1, sizeof(*sig));
|
|
|
|
switch (num) {
|
|
case 0:
|
|
if (!gcry_mpi_scan(&sig->s, GCRYMPI_FMT_PGP, p, mlen, NULL))
|
|
rc = 0;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int pgpSetKeyMpiRSA(pgpDigAlg pgpkey, int num, const uint8_t *p)
|
|
{
|
|
struct pgpDigKeyRSA_s *key = pgpkey->data;
|
|
int mlen = pgpMpiLen(p);
|
|
int rc = 1;
|
|
|
|
if (!key)
|
|
key = pgpkey->data = xcalloc(1, sizeof(*key));
|
|
|
|
switch (num) {
|
|
case 0:
|
|
if (!gcry_mpi_scan(&key->n, GCRYMPI_FMT_PGP, p, mlen, NULL))
|
|
rc = 0;
|
|
break;
|
|
case 1:
|
|
if (!gcry_mpi_scan(&key->e, GCRYMPI_FMT_PGP, p, mlen, NULL))
|
|
rc = 0;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int pgpVerifySigRSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig, uint8_t *hash, size_t hashlen, int hash_algo)
|
|
{
|
|
struct pgpDigKeyRSA_s *key = pgpkey->data;
|
|
struct pgpDigSigRSA_s *sig = pgpsig->data;
|
|
gcry_sexp_t sexp_sig = NULL, sexp_data = NULL, sexp_pkey = NULL;
|
|
const char *hash_algo_name;
|
|
int rc = 1;
|
|
|
|
if (!sig || !key)
|
|
return rc;
|
|
|
|
hash_algo_name = gcry_md_algo_name(hashalgo2gcryalgo(hash_algo));
|
|
gcry_sexp_build(&sexp_sig, NULL, "(sig-val (rsa (s %M)))", sig->s);
|
|
gcry_sexp_build(&sexp_data, NULL, "(data (flags pkcs1) (hash %s %b))", hash_algo_name, (int)hashlen, (const char *)hash);
|
|
gcry_sexp_build(&sexp_pkey, NULL, "(public-key (rsa (n %M) (e %M)))", key->n, key->e);
|
|
if (sexp_sig && sexp_data && sexp_pkey)
|
|
rc = gcry_pk_verify(sexp_sig, sexp_data, sexp_pkey) == 0 ? 0 : 1;
|
|
gcry_sexp_release(sexp_sig);
|
|
gcry_sexp_release(sexp_data);
|
|
gcry_sexp_release(sexp_pkey);
|
|
return rc;
|
|
}
|
|
|
|
static void pgpFreeSigRSA(pgpDigAlg pgpsig)
|
|
{
|
|
struct pgpDigSigRSA_s *sig = pgpsig->data;
|
|
if (sig) {
|
|
gcry_mpi_release(sig->s);
|
|
pgpsig->data = _free(sig);
|
|
}
|
|
}
|
|
|
|
static void pgpFreeKeyRSA(pgpDigAlg pgpkey)
|
|
{
|
|
struct pgpDigKeyRSA_s *key = pgpkey->data;
|
|
if (key) {
|
|
gcry_mpi_release(key->n);
|
|
gcry_mpi_release(key->e);
|
|
pgpkey->data = _free(key);
|
|
}
|
|
}
|
|
|
|
|
|
/****************************** DSA **************************************/
|
|
|
|
struct pgpDigSigDSA_s {
|
|
gcry_mpi_t r;
|
|
gcry_mpi_t s;
|
|
};
|
|
|
|
struct pgpDigKeyDSA_s {
|
|
gcry_mpi_t p;
|
|
gcry_mpi_t q;
|
|
gcry_mpi_t g;
|
|
gcry_mpi_t y;
|
|
};
|
|
|
|
static int pgpSetSigMpiDSA(pgpDigAlg pgpsig, int num, const uint8_t *p)
|
|
{
|
|
struct pgpDigSigDSA_s *sig = pgpsig->data;
|
|
int mlen = pgpMpiLen(p);
|
|
int rc = 1;
|
|
|
|
if (!sig)
|
|
sig = pgpsig->data = xcalloc(1, sizeof(*sig));
|
|
|
|
switch (num) {
|
|
case 0:
|
|
if (!gcry_mpi_scan(&sig->r, GCRYMPI_FMT_PGP, p, mlen, NULL))
|
|
rc = 0;
|
|
break;
|
|
case 1:
|
|
if (!gcry_mpi_scan(&sig->s, GCRYMPI_FMT_PGP, p, mlen, NULL))
|
|
rc = 0;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int pgpSetKeyMpiDSA(pgpDigAlg pgpkey, int num, const uint8_t *p)
|
|
{
|
|
struct pgpDigKeyDSA_s *key = pgpkey->data;
|
|
int mlen = pgpMpiLen(p);
|
|
int rc = 1;
|
|
|
|
if (!key)
|
|
key = pgpkey->data = xcalloc(1, sizeof(*key));
|
|
|
|
switch (num) {
|
|
case 0:
|
|
if (!gcry_mpi_scan(&key->p, GCRYMPI_FMT_PGP, p, mlen, NULL))
|
|
rc = 0;
|
|
break;
|
|
case 1:
|
|
if (!gcry_mpi_scan(&key->q, GCRYMPI_FMT_PGP, p, mlen, NULL))
|
|
rc = 0;
|
|
break;
|
|
case 2:
|
|
if (!gcry_mpi_scan(&key->g, GCRYMPI_FMT_PGP, p, mlen, NULL))
|
|
rc = 0;
|
|
break;
|
|
case 3:
|
|
if (!gcry_mpi_scan(&key->y, GCRYMPI_FMT_PGP, p, mlen, NULL))
|
|
rc = 0;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int pgpVerifySigDSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig, uint8_t *hash, size_t hashlen, int hash_algo)
|
|
{
|
|
struct pgpDigKeyDSA_s *key = pgpkey->data;
|
|
struct pgpDigSigDSA_s *sig = pgpsig->data;
|
|
gcry_sexp_t sexp_sig = NULL, sexp_data = NULL, sexp_pkey = NULL;
|
|
int rc = 1;
|
|
size_t qlen;
|
|
|
|
if (!sig || !key)
|
|
return rc;
|
|
|
|
qlen = (mpi_get_nbits(key->q) + 7) / 8;
|
|
if (qlen < 20)
|
|
qlen = 20; /* sanity */
|
|
if (hashlen > qlen)
|
|
hashlen = qlen; /* dsa2: truncate hash to qlen */
|
|
gcry_sexp_build(&sexp_sig, NULL, "(sig-val (dsa (r %M) (s %M)))", sig->r, sig->s);
|
|
gcry_sexp_build(&sexp_data, NULL, "(data (flags raw) (value %b))", (int)hashlen, (const char *)hash);
|
|
gcry_sexp_build(&sexp_pkey, NULL, "(public-key (dsa (p %M) (q %M) (g %M) (y %M)))", key->p, key->q, key->g, key->y);
|
|
if (sexp_sig && sexp_data && sexp_pkey)
|
|
rc = gcry_pk_verify(sexp_sig, sexp_data, sexp_pkey) == 0 ? 0 : 1;
|
|
gcry_sexp_release(sexp_sig);
|
|
gcry_sexp_release(sexp_data);
|
|
gcry_sexp_release(sexp_pkey);
|
|
return rc;
|
|
}
|
|
|
|
static void pgpFreeSigDSA(pgpDigAlg pgpsig)
|
|
{
|
|
struct pgpDigSigDSA_s *sig = pgpsig->data;
|
|
if (sig) {
|
|
gcry_mpi_release(sig->r);
|
|
gcry_mpi_release(sig->s);
|
|
pgpsig->data = _free(sig);
|
|
}
|
|
}
|
|
|
|
static void pgpFreeKeyDSA(pgpDigAlg pgpkey)
|
|
{
|
|
struct pgpDigKeyDSA_s *key = pgpkey->data;
|
|
if (key) {
|
|
gcry_mpi_release(key->p);
|
|
gcry_mpi_release(key->q);
|
|
gcry_mpi_release(key->g);
|
|
gcry_mpi_release(key->y);
|
|
pgpkey->data = _free(key);
|
|
}
|
|
}
|
|
|
|
|
|
/****************************** EDDSA **************************************/
|
|
|
|
struct pgpDigSigEDDSA_s {
|
|
gcry_mpi_t r;
|
|
gcry_mpi_t s;
|
|
};
|
|
|
|
struct pgpDigKeyEDDSA_s {
|
|
gcry_mpi_t q;
|
|
};
|
|
|
|
static int pgpSetSigMpiEDDSA(pgpDigAlg pgpsig, int num, const uint8_t *p)
|
|
{
|
|
struct pgpDigSigEDDSA_s *sig = pgpsig->data;
|
|
int mlen = pgpMpiLen(p);
|
|
int rc = 1;
|
|
|
|
if (!sig)
|
|
sig = pgpsig->data = xcalloc(1, sizeof(*sig));
|
|
|
|
switch (num) {
|
|
case 0:
|
|
if (!gcry_mpi_scan(&sig->r, GCRYMPI_FMT_PGP, p, mlen, NULL))
|
|
rc = 0;
|
|
break;
|
|
case 1:
|
|
if (!gcry_mpi_scan(&sig->s, GCRYMPI_FMT_PGP, p, mlen, NULL))
|
|
rc = 0;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int pgpSetKeyMpiEDDSA(pgpDigAlg pgpkey, int num, const uint8_t *p)
|
|
{
|
|
struct pgpDigKeyEDDSA_s *key = pgpkey->data;
|
|
int mlen = pgpMpiLen(p);
|
|
int rc = 1;
|
|
|
|
if (!key)
|
|
key = pgpkey->data = xcalloc(1, sizeof(*key));
|
|
|
|
switch (num) {
|
|
case 0:
|
|
if (!gcry_mpi_scan(&key->q, GCRYMPI_FMT_PGP, p, mlen, NULL))
|
|
rc = 0;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
ed25519_zero_extend(gcry_mpi_t x, unsigned char *buf, int bufl)
|
|
{
|
|
int n = (gcry_mpi_get_nbits(x) + 7) / 8;
|
|
if (n == 0 || n > bufl)
|
|
return 1;
|
|
n = bufl - n;
|
|
if (n)
|
|
memset(buf, 0, n);
|
|
gcry_mpi_print(GCRYMPI_FMT_USG, buf + n, bufl - n, NULL, x);
|
|
return 0;
|
|
}
|
|
|
|
static int pgpVerifySigEDDSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig, uint8_t *hash, size_t hashlen, int hash_algo)
|
|
{
|
|
struct pgpDigKeyEDDSA_s *key = pgpkey->data;
|
|
struct pgpDigSigEDDSA_s *sig = pgpsig->data;
|
|
gcry_sexp_t sexp_sig = NULL, sexp_data = NULL, sexp_pkey = NULL;
|
|
int rc = 1;
|
|
unsigned char buf_r[32], buf_s[32];
|
|
|
|
if (!sig || !key)
|
|
return rc;
|
|
if (pgpkey->curve != PGPCURVE_ED25519)
|
|
return rc;
|
|
if (ed25519_zero_extend(sig->r, buf_r, 32) || ed25519_zero_extend(sig->s, buf_s, 32))
|
|
return rc;
|
|
gcry_sexp_build(&sexp_sig, NULL, "(sig-val (eddsa (r %b) (s %b)))", 32, (const char *)buf_r, 32, (const char *)buf_s, 32);
|
|
gcry_sexp_build(&sexp_data, NULL, "(data (flags eddsa) (hash-algo sha512) (value %b))", (int)hashlen, (const char *)hash);
|
|
gcry_sexp_build(&sexp_pkey, NULL, "(public-key (ecc (curve \"Ed25519\") (flags eddsa) (q %M)))", key->q);
|
|
if (sexp_sig && sexp_data && sexp_pkey)
|
|
rc = gcry_pk_verify(sexp_sig, sexp_data, sexp_pkey) == 0 ? 0 : 1;
|
|
gcry_sexp_release(sexp_sig);
|
|
gcry_sexp_release(sexp_data);
|
|
gcry_sexp_release(sexp_pkey);
|
|
return rc;
|
|
}
|
|
|
|
static void pgpFreeSigEDDSA(pgpDigAlg pgpsig)
|
|
{
|
|
struct pgpDigSigEDDSA_s *sig = pgpsig->data;
|
|
if (sig) {
|
|
gcry_mpi_release(sig->r);
|
|
gcry_mpi_release(sig->s);
|
|
pgpsig->data = _free(sig);
|
|
}
|
|
}
|
|
|
|
static void pgpFreeKeyEDDSA(pgpDigAlg pgpkey)
|
|
{
|
|
struct pgpDigKeyEDDSA_s *key = pgpkey->data;
|
|
if (key) {
|
|
gcry_mpi_release(key->q);
|
|
pgpkey->data = _free(key);
|
|
}
|
|
}
|
|
|
|
|
|
/****************************** NULL **************************************/
|
|
|
|
static int pgpSetMpiNULL(pgpDigAlg pgpkey, int num, const uint8_t *p)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static int pgpVerifyNULL(pgpDigAlg pgpkey, pgpDigAlg pgpsig,
|
|
uint8_t *hash, size_t hashlen, int hash_algo)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static int pgpSupportedCurve(int curve)
|
|
{
|
|
if (curve == PGPCURVE_ED25519) {
|
|
static int supported_ed25519;
|
|
if (!supported_ed25519) {
|
|
gcry_sexp_t sexp = NULL;
|
|
unsigned int nbits;
|
|
gcry_sexp_build(&sexp, NULL, "(public-key (ecc (curve \"Ed25519\")))");
|
|
nbits = gcry_pk_get_nbits(sexp);
|
|
gcry_sexp_release(sexp);
|
|
supported_ed25519 = nbits > 0 ? 1 : -1;
|
|
}
|
|
return supported_ed25519 > 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
pgpDigAlg pgpPubkeyNew(int algo, int curve)
|
|
{
|
|
pgpDigAlg ka = xcalloc(1, sizeof(*ka));;
|
|
|
|
switch (algo) {
|
|
case PGPPUBKEYALGO_RSA:
|
|
ka->setmpi = pgpSetKeyMpiRSA;
|
|
ka->free = pgpFreeKeyRSA;
|
|
ka->mpis = 2;
|
|
break;
|
|
case PGPPUBKEYALGO_DSA:
|
|
ka->setmpi = pgpSetKeyMpiDSA;
|
|
ka->free = pgpFreeKeyDSA;
|
|
ka->mpis = 4;
|
|
break;
|
|
case PGPPUBKEYALGO_EDDSA:
|
|
if (!pgpSupportedCurve(curve)) {
|
|
ka->setmpi = pgpSetMpiNULL;
|
|
ka->mpis = -1;
|
|
break;
|
|
}
|
|
ka->setmpi = pgpSetKeyMpiEDDSA;
|
|
ka->free = pgpFreeKeyEDDSA;
|
|
ka->mpis = 1;
|
|
ka->curve = curve;
|
|
break;
|
|
default:
|
|
ka->setmpi = pgpSetMpiNULL;
|
|
ka->mpis = -1;
|
|
break;
|
|
}
|
|
|
|
ka->verify = pgpVerifyNULL; /* keys can't be verified */
|
|
|
|
return ka;
|
|
}
|
|
|
|
pgpDigAlg pgpSignatureNew(int algo)
|
|
{
|
|
pgpDigAlg sa = xcalloc(1, sizeof(*sa));
|
|
|
|
switch (algo) {
|
|
case PGPPUBKEYALGO_RSA:
|
|
sa->setmpi = pgpSetSigMpiRSA;
|
|
sa->free = pgpFreeSigRSA;
|
|
sa->verify = pgpVerifySigRSA;
|
|
sa->mpis = 1;
|
|
break;
|
|
case PGPPUBKEYALGO_DSA:
|
|
sa->setmpi = pgpSetSigMpiDSA;
|
|
sa->free = pgpFreeSigDSA;
|
|
sa->verify = pgpVerifySigDSA;
|
|
sa->mpis = 2;
|
|
break;
|
|
case PGPPUBKEYALGO_EDDSA:
|
|
sa->setmpi = pgpSetSigMpiEDDSA;
|
|
sa->free = pgpFreeSigEDDSA;
|
|
sa->verify = pgpVerifySigEDDSA;
|
|
sa->mpis = 2;
|
|
break;
|
|
default:
|
|
sa->setmpi = pgpSetMpiNULL;
|
|
sa->verify = pgpVerifyNULL;
|
|
sa->mpis = -1;
|
|
break;
|
|
}
|
|
return sa;
|
|
}
|