common: New module to compute openpgp fingerprints

* common/openpgp-fpr.c: New.
* common/Makefile.am (common_sources): Add it.
--

This function is targeted to handle keys on smartcards.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2021-04-16 12:39:24 +02:00
parent 283ccbc824
commit 2f2bdd9c08
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 304 additions and 0 deletions

View File

@ -97,6 +97,7 @@ common_sources = \
name-value.c name-value.h \
recsel.c recsel.h \
ksba-io-support.c ksba-io-support.h \
openpgp-fpr.c \
compliance.c compliance.h \
pkscreening.c pkscreening.h

283
common/openpgp-fpr.c Normal file
View File

@ -0,0 +1,283 @@
/* openpgp-fpr.c - OpenPGP Fingerprint computation
* Copyright (C) 2021 g10 Code GmbH
*
* This file is part of GnuPG.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - 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.
*
* or both in parallel, as here.
*
* This file 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later)
*/
#include <config.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include "util.h"
#include "openpgpdefs.h"
/* Count the number of bits, assuming the A represents an unsigned big
* integer of length LEN bytes. */
static unsigned int
count_bits (const unsigned char *a, size_t len)
{
unsigned int n = len * 8;
int i;
for (; len && !*a; len--, a++, n -=8)
;
if (len)
{
for (i=7; i && !(*a & (1<<i)); i--)
n--;
}
return n;
}
/* Variant of count_bits for simple octet strings. */
static unsigned int
count_sos_bits (const unsigned char *a, size_t len)
{
unsigned int n = len * 8;
int i;
if (len == 0 || *a == 0)
return n;
for (i=7; i && !(*a & (1<<i)); i--)
n--;
return n;
}
gpg_error_t
compute_openpgp_fpr (int keyversion, int pgpalgo, unsigned long timestamp,
gcry_buffer_t *iov, int iovcnt,
unsigned char *result, unsigned int *r_resultlen)
{
gpg_error_t err;
int hashalgo;
unsigned char prefix[15];
size_t n;
int i;
if (r_resultlen)
*r_resultlen = 0;
if (iovcnt < 2)
return gpg_error (GPG_ERR_INV_ARG);
/* Note that iov[0] is reserved. */
for (n=0, i=1; i < iovcnt; i++)
n += iov[i].len;
i = 0;
if (keyversion == 5)
{
hashalgo = GCRY_MD_SHA256;
n += 10; /* Add the prefix length. */
prefix[i++] = 0x9a;
prefix[i++] = (n >> 24);
prefix[i++] = (n >> 16);
}
else if (keyversion == 4)
{
hashalgo = GCRY_MD_SHA1;
n += 6; /* Add the prefix length. */
prefix[i++] = 0x99;
}
else
return gpg_error (GPG_ERR_UNKNOWN_VERSION);
prefix[i++] = (n >> 8);
prefix[i++] = n;
prefix[i++] = keyversion;
prefix[i++] = (timestamp >> 24);
prefix[i++] = (timestamp >> 16);
prefix[i++] = (timestamp >> 8);
prefix[i++] = (timestamp);
prefix[i++] = pgpalgo;
if (keyversion == 5)
{
prefix[i++] = ((n-10) >> 24);
prefix[i++] = ((n-10) >> 16);
prefix[i++] = ((n-10) >> 8);
prefix[i++] = (n-10);
}
log_assert (i <= sizeof prefix);
/* The first element is reserved for our use; set it. */
iov[0].size = 0;
iov[0].off = 0;
iov[0].len = i;
iov[0].data = prefix;
/* for (i=0; i < iovcnt; i++) */
/* log_printhex (iov[i].data, iov[i].len, "cmpfpr i=%d: ", i); */
err = gcry_md_hash_buffers (hashalgo, 0, result, iov, iovcnt);
/* log_printhex (result, 20, "fingerpint: "); */
/* Better clear the first element because it was set by us. */
iov[0].size = 0;
iov[0].off = 0;
iov[0].len = 0;
iov[0].data = NULL;
if (!err && r_resultlen)
*r_resultlen = (hashalgo == GCRY_MD_SHA1)? 20 : 32;
return err;
}
gpg_error_t
compute_openpgp_fpr_rsa (int keyversion, unsigned long timestamp,
const unsigned char *m, unsigned int mlen,
const unsigned char *e, unsigned int elen,
unsigned char *result, unsigned int *r_resultlen)
{
gcry_buffer_t iov[5] = { {0} };
unsigned char nbits_m[2], nbits_e[2];
unsigned int n;
/* Strip leading zeroes. */
for (; mlen && !*m; mlen--, m++)
;
for (; elen && !*e; elen--, e++)
;
/* Count bits. */
n = count_bits (m, mlen);
nbits_m[0] = n >> 8;
nbits_m[1] = n;
n = count_bits (e, elen);
nbits_e[0] = n >> 8;
nbits_e[1] = n;
/* Put parms into the array. Note that iov[0] is reserved. */
iov[1].len = 2;
iov[1].data = nbits_m;
iov[2].len = mlen;
iov[2].data = (void*)m;
iov[3].len = 2;
iov[3].data = nbits_e;
iov[4].len = elen;
iov[4].data = (void*)e;
return compute_openpgp_fpr (keyversion, PUBKEY_ALGO_RSA, timestamp,
iov, 5, result, r_resultlen);
}
/* Determine KDF hash algorithm and KEK encryption algorithm by CURVE.
* The returned buffer has a length of 4.
* Note: This needs to be kept in sync with the table in g10/ecdh.c */
static const unsigned char*
default_ecdh_params (unsigned int nbits)
{
/* See RFC-6637 for those constants.
0x03: Number of bytes
0x01: Version for this parameter format
KEK digest algorithm
KEK cipher algorithm
*/
if (nbits <= 256)
return (const unsigned char*)"\x03\x01\x08\x07";
else if (nbits <= 384)
return (const unsigned char*)"\x03\x01\x09\x09";
else
return (const unsigned char*)"\x03\x01\x0a\x09";
}
gpg_error_t
compute_openpgp_fpr_ecc (int keyversion, unsigned long timestamp,
const char *curvename, int for_encryption,
const unsigned char *q, unsigned int qlen,
const unsigned char *kdf, unsigned int kdflen,
unsigned char *result, unsigned int *r_resultlen)
{
gpg_error_t err;
const char *curveoidstr;
gcry_mpi_t curveoid = NULL;
unsigned int curvebits;
int pgpalgo;
const unsigned char *oidraw;
size_t oidrawlen;
gcry_buffer_t iov[5] = { {0} };
unsigned int iovlen;
unsigned char nbits_q[2];
unsigned int n;
curveoidstr = openpgp_curve_to_oid (curvename, &curvebits, &pgpalgo);
err = openpgp_oid_from_str (curveoidstr, &curveoid);
if (err)
goto leave;
oidraw = gcry_mpi_get_opaque (curveoid, &n);
if (!oidraw)
{
err = gpg_error_from_syserror ();
goto leave;
}
oidrawlen = (n+7)/8;
/* If the curve does not enforce a certain algorithm, we use the
* for_encryption flag to decide which algo to use. */
if (!pgpalgo)
pgpalgo = for_encryption? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA;
/* Count bits. */
n = count_sos_bits (q, qlen);
nbits_q[0] = n >> 8;
nbits_q[1] = n;
/* Put parms into the array. Note that iov[0] is reserved. */
iov[1].len = oidrawlen;
iov[1].data = (void*)oidraw;
iov[2].len = 2;
iov[2].data = nbits_q;
iov[3].len = qlen;
iov[3].data = (void*)q;
iovlen = 4;
if (pgpalgo == PUBKEY_ALGO_ECDH)
{
if (!kdf || !kdflen || !kdf[0])
{
/* No KDF givem - use the default. */
kdflen = 4;
kdf = default_ecdh_params (curvebits);
}
iov[4].len = kdflen;
iov[4].data = (void*)kdf;
iovlen++;
}
err = compute_openpgp_fpr (keyversion, pgpalgo, timestamp,
iov, iovlen, result, r_resultlen);
leave:
gcry_mpi_release (curveoid);
return err;
}

View File

@ -213,6 +213,26 @@ compress_algo_t;
/*-- openpgp-s2k.c --*/
unsigned char encode_s2k_iterations (int iterations);
/*-- openpgp-fpr.c --*/
gpg_error_t compute_openpgp_fpr (int keyversion, int pgpalgo,
unsigned long timestamp,
gcry_buffer_t *iov, int iovcnt,
unsigned char *result,
unsigned int *r_resultlen);
gpg_error_t compute_openpgp_fpr_rsa (int keyversion,
unsigned long timestamp,
const unsigned char *m, unsigned int mlen,
const unsigned char *e, unsigned int elen,
unsigned char *result,
unsigned int *r_resultlen);
gpg_error_t compute_openpgp_fpr_ecc (int keyversion,
unsigned long timestamp,
const char *curvename, int for_encryption,
const unsigned char *q, unsigned int qlen,
const unsigned char *kdf,
unsigned int kdflen,
unsigned char *result,
unsigned int *r_resultlen);
/*-- openpgp-oid.c --*/
pubkey_algo_t map_gcry_pk_to_openpgp (enum gcry_pk_algos algo);