diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index bdd982afe..22cbdd2bb 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -1,629 +1,591 @@ /* pkdecrypt.c - public key decryption (well, actually using a secret key) * Copyright (C) 2001, 2003 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG 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 3 of the License, or * (at your option) any later version. * * GnuPG 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 . */ #include #include #include #include #include #include #include #include #include "agent.h" /* DECRYPT the stuff in ciphertext which is expected to be a S-Exp. Try to get the key from CTRL and write the decoded stuff back to OUTFP. The padding information is stored at R_PADDING with -1 for not known. */ gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, const unsigned char *ciphertext, size_t ciphertextlen, membuf_t *outbuf, int *r_padding) { gcry_sexp_t s_skey = NULL, s_cipher = NULL, s_plain = NULL; unsigned char *shadow_info = NULL; gpg_error_t err = 0; int no_shadow_info = 0; char *buf = NULL; size_t len; *r_padding = -1; if (!ctrl->have_keygrip) { log_error ("speculative decryption not yet supported\n"); err = gpg_error (GPG_ERR_NO_SECKEY); goto leave; } err = gcry_sexp_sscan (&s_cipher, NULL, (char*)ciphertext, ciphertextlen); if (err) { log_error ("failed to convert ciphertext: %s\n", gpg_strerror (err)); err = gpg_error (GPG_ERR_INV_DATA); goto leave; } if (DBG_CRYPTO) { log_printhex (ctrl->keygrip, 20, "keygrip:"); log_printhex (ciphertext, ciphertextlen, "cipher: "); } err = agent_key_from_file (ctrl, NULL, desc_text, NULL, &shadow_info, CACHE_MODE_NORMAL, NULL, &s_skey, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_NO_SECKEY) no_shadow_info = 1; else if (err) { log_error ("failed to read the secret key\n"); goto leave; } if (shadow_info || no_shadow_info) { /* divert operation to the smartcard */ if (!gcry_sexp_canon_len (ciphertext, ciphertextlen, NULL, NULL)) { err = gpg_error (GPG_ERR_INV_SEXP); goto leave; } if (s_skey && agent_is_tpm2_key (s_skey)) err = divert_tpm2_pkdecrypt (ctrl, ciphertext, shadow_info, &buf, &len, r_padding); else err = divert_pkdecrypt (ctrl, ctrl->keygrip, ciphertext, &buf, &len, r_padding); if (err) { /* We restore the original error (ie. no seckey) is no card * has been found and we have no shadow key. This avoids a * surprising "card removed" error code. */ if ((gpg_err_code (err) == GPG_ERR_CARD_REMOVED || gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) && no_shadow_info) err = gpg_error (GPG_ERR_NO_SECKEY); else log_error ("smartcard decryption failed: %s\n", gpg_strerror (err)); goto leave; } put_membuf_printf (outbuf, "(5:value%u:", (unsigned int)len); put_membuf (outbuf, buf, len); put_membuf (outbuf, ")", 2); } else { /* No smartcard, but a private key */ /* if (DBG_CRYPTO ) */ /* { */ /* log_debug ("skey: "); */ /* gcry_sexp_dump (s_skey); */ /* } */ err = gcry_pk_decrypt (&s_plain, s_cipher, s_skey); if (err) { log_error ("decryption failed: %s\n", gpg_strerror (err)); goto leave; } if (DBG_CRYPTO) { log_debug ("plain: "); gcry_sexp_dump (s_plain); } len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, NULL, 0); log_assert (len); buf = xmalloc (len); len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, buf, len); log_assert (len); if (*buf == '(') put_membuf (outbuf, buf, len); else { /* Old style libgcrypt: This is only an S-expression part. Turn it into a complete S-expression. */ put_membuf (outbuf, "(5:value", 8); put_membuf (outbuf, buf, len); put_membuf (outbuf, ")", 2); } } leave: gcry_sexp_release (s_skey); gcry_sexp_release (s_plain); gcry_sexp_release (s_cipher); xfree (buf); xfree (shadow_info); return err; } static void reverse_buffer (unsigned char *buffer, unsigned int length) { unsigned int tmp, i; for (i=0; i < length/2; i++) { tmp = buffer[i]; buffer[i] = buffer[length-1-i]; buffer[length-1-i] = tmp; } } static gpg_error_t agent_hybrid_kem_decap (ctrl_t ctrl, const char *desc_text, int kemid, gcry_sexp_t s_cipher, membuf_t *outbuf) { gcry_sexp_t s_skey0 = NULL; gcry_sexp_t s_skey1 = NULL; unsigned char *shadow_info = NULL; gpg_error_t err = 0; int no_shadow_info = 0; unsigned int nbits; const unsigned char *p; size_t len; gcry_mpi_t encrypted_sessionkey_mpi; const unsigned char *encrypted_sessionkey; size_t encrypted_sessionkey_len; gcry_mpi_t ecc_sk_mpi; unsigned char ecc_sk[32]; gcry_mpi_t ecc_pk_mpi; unsigned char ecc_pk[32]; gcry_mpi_t ecc_ct_mpi; const unsigned char *ecc_ct; size_t ecc_ct_len; unsigned char ecc_ecdh[32]; unsigned char ecc_ss[32]; gcry_mpi_t mlkem_sk_mpi; gcry_mpi_t mlkem_ct_mpi; const unsigned char *mlkem_sk; const unsigned char *mlkem_ct; unsigned char mlkem_ss[GCRY_KEM_MLKEM768_SHARED_LEN]; - gcry_buffer_t iov[13]; - unsigned char head136[2]; - unsigned char headK[3]; - const unsigned char pad[94] = { 0 }; - unsigned char right_encode_L[3]; + gcry_buffer_t iov[6]; unsigned char kekkey[32]; size_t kekkeylen = 32; /* AES-256 is mandatory */ gcry_cipher_hd_t hd; unsigned char sessionkey_encoded[256]; size_t sessionkey_encoded_len; const unsigned char fixedinfo[1] = { 105 }; (void)kemid; /* For now, only PGP. */ /* (enc-val(pqc(s%m)(e%m)(k%m)))) */ err = agent_key_from_file (ctrl, NULL, desc_text, NULL, &shadow_info, CACHE_MODE_NORMAL, NULL, &s_skey0, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_NO_SECKEY) no_shadow_info = 1; else if (err) { log_error ("failed to read the secret key\n"); goto leave; } if (shadow_info || no_shadow_info) { /* divert operation to the smartcard */ err = gpg_error (GPG_ERR_NO_SECKEY); /* Not implemented yet. */ goto leave; } err = agent_key_from_file (ctrl, NULL, desc_text, ctrl->keygrip1, &shadow_info, CACHE_MODE_NORMAL, NULL, &s_skey1, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_NO_SECKEY) no_shadow_info = 1; else if (err) { log_error ("failed to read the secret key\n"); goto leave; } if (shadow_info || no_shadow_info) { /* divert operation to the smartcard */ err = gpg_error (GPG_ERR_NO_SECKEY); /* Not implemented yet. */ goto leave; } /* No smartcard, but private keys */ gcry_sexp_extract_param (s_cipher, NULL, "/e/k/s", &ecc_ct_mpi, &mlkem_ct_mpi, &encrypted_sessionkey_mpi, NULL); encrypted_sessionkey = gcry_mpi_get_opaque (encrypted_sessionkey_mpi, &nbits); encrypted_sessionkey_len = (nbits+7)/8; encrypted_sessionkey_len--; if (encrypted_sessionkey[0] != encrypted_sessionkey_len) { err = GPG_ERR_INV_DATA; goto leave; } encrypted_sessionkey++; /* skip sym algo */ encrypted_sessionkey_len--; encrypted_sessionkey++; /* Fistly, ECC. */ gcry_sexp_extract_param (s_skey0, NULL, "/q/d", &ecc_pk_mpi, &ecc_sk_mpi, NULL); p = gcry_mpi_get_opaque (ecc_pk_mpi, &nbits); len = (nbits+7)/8; memcpy (ecc_pk, p+1, 32); /* Remove 0x40 prefix */ p = gcry_mpi_get_opaque (ecc_sk_mpi, &nbits); len = (nbits+7)/8; memset (ecc_sk, 0, 32); memcpy (ecc_sk + 32 - len, p, len); reverse_buffer (ecc_sk, 32); mpi_release (ecc_pk_mpi); mpi_release (ecc_sk_mpi); ecc_ct = gcry_mpi_get_opaque (ecc_ct_mpi, &nbits); ecc_ct_len = (nbits+7)/8; /*FIXME make sure the lengths are all correct. */ /*FIXME: check the internal of optional to determine the KEK-algo and KEKKEYLEN. */ err = gcry_kem_decap (GCRY_KEM_RAW_X25519, ecc_sk, 32, ecc_ct, ecc_ct_len, ecc_ecdh, 32, NULL, 0); iov[0].data = ecc_ecdh; iov[0].off = 0; iov[0].len = 32; iov[1].data = (unsigned char *)ecc_ct; iov[1].off = 0; iov[1].len = 32; iov[2].data = ecc_pk; iov[2].off = 0; iov[2].len = 32; gcry_md_hash_buffers (GCRY_MD_SHA3_256, 0, ecc_ss, iov, 3); /* Secondly, ML-KEM */ gcry_sexp_extract_param (s_skey1, NULL, "/s", &mlkem_sk_mpi, NULL); mlkem_sk = gcry_mpi_get_opaque (mlkem_sk_mpi, &nbits); len = (nbits+7)/8; mlkem_ct = gcry_mpi_get_opaque (mlkem_ct_mpi, &nbits); len = (nbits+7)/8; err = gcry_kem_decap (GCRY_KEM_MLKEM768, mlkem_sk, GCRY_KEM_MLKEM768_SECKEY_LEN, mlkem_ct, GCRY_KEM_MLKEM768_ENCAPS_LEN, mlkem_ss, GCRY_KEM_MLKEM768_SHARED_LEN, NULL, 0); mpi_release (mlkem_sk_mpi); /* Then, combine two shared secrets into one */ // multiKeyCombine(eccKeyShare, eccCipherText, // mlkemKeyShare, mlkemCipherText, // fixedInfo, oBits) // // Input: // eccKeyShare - the ECC key share encoded as an octet string // eccCipherText - the ECC ciphertext encoded as an octet string // mlkemKeyShare - the ML-KEM key share encoded as an octet string // mlkemCipherText - the ML-KEM ciphertext encoded as an octet string // fixedInfo - the fixed information octet string // oBits - the size of the output keying material in bits // // Constants: // domSeparation - the UTF-8 encoding of the string // "OpenPGPCompositeKeyDerivationFunction" // counter - the 4 byte value 00 00 00 01 // customizationString - the UTF-8 encoding of the string "KDF" // // eccData = eccKeyShare || eccCipherText // mlkemData = mlkemKeyShare || mlkemCipherText // encData = counter || eccData || mlkemData || fixedInfo // // KEK = KMAC256(domSeparation, encData, oBits, customizationString) // return KEK // // fixedInfo = algID (105 for ML-KEM-768-x25519kem) // // KMAC256(K,X,L,S): // newX = bytepad(encode_string(K), 136) || X || right_encode(L) // cSHAKE256(newX,L,"KMAC",S) - iov[0].data = "KMAC"; + iov[0].data = "\x00\x00\x00\x01"; /* Counter */ iov[0].off = 0; iov[0].len = 4; - iov[1].data = "KDF"; + iov[1].data = ecc_ss; iov[1].off = 0; - iov[1].len = 3; + iov[1].len = 32; - head136[0] = 1; - head136[1] = 136; - iov[2].data = head136; + iov[2].data = (unsigned char *)ecc_ct; iov[2].off = 0; - iov[2].len = 2; + iov[2].len = 32; - headK[0] = 2; - headK[1] = (37*8)>>8; - headK[2] = (37*8)&0xff; - iov[3].data = headK; + iov[3].data = mlkem_ss; iov[3].off = 0; - iov[3].len = 3; + iov[3].len = GCRY_KEM_MLKEM768_SHARED_LEN; - iov[4].data = "OpenPGPCompositeKeyDerivationFunction"; + iov[4].data = (unsigned char *)mlkem_ct; iov[4].off = 0; - iov[4].len = 37; + iov[4].len = GCRY_KEM_MLKEM768_ENCAPS_LEN; - iov[5].data = (unsigned char *)pad; + iov[5].data = (unsigned char *)fixedinfo; iov[5].off = 0; - iov[5].len = sizeof (pad); - - iov[6].data = "\x00\x00\x00\x01"; /* Counter */ - iov[6].off = 0; - iov[6].len = 4; - - iov[7].data = ecc_ss; - iov[7].off = 0; - iov[7].len = 32; - - iov[8].data = (unsigned char *)ecc_ct; - iov[8].off = 0; - iov[8].len = 32; - - iov[9].data = mlkem_ss; - iov[9].off = 0; - iov[9].len = GCRY_KEM_MLKEM768_SHARED_LEN; - - iov[10].data = (unsigned char *)mlkem_ct; - iov[10].off = 0; - iov[10].len = GCRY_KEM_MLKEM768_ENCAPS_LEN; - - iov[11].data = (unsigned char *)fixedinfo; - iov[11].off = 0; - iov[11].len = 1; + iov[5].len = 1; - right_encode_L[0] = (kekkeylen * 8) >> 8; - right_encode_L[1] = (kekkeylen * 8) & 0xff; - right_encode_L[2] = 2; - iov[12].data = right_encode_L; - iov[12].off = 0; - iov[12].len = 3; + err = compute_kmac256 (kekkey, kekkeylen, + "OpenPGPCompositeKeyDerivationFunction", 37, + "KDF", 3, iov, 6); - err = gcry_md_hash_buffers_extract (GCRY_MD_CSHAKE256, 0, kekkey, kekkeylen, - iov, 13); mpi_release (ecc_ct_mpi); mpi_release (mlkem_ct_mpi); if (DBG_CRYPTO) { log_printhex (kekkey, kekkeylen, "KEK key: "); } err = gcry_cipher_open (&hd, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) { log_error ("ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (err)); mpi_release (encrypted_sessionkey_mpi); goto leave; } err = gcry_cipher_setkey (hd, kekkey, kekkeylen); sessionkey_encoded_len = encrypted_sessionkey_len - 8; err = gcry_cipher_decrypt (hd, sessionkey_encoded, sessionkey_encoded_len, encrypted_sessionkey, encrypted_sessionkey_len); gcry_cipher_close (hd); mpi_release (encrypted_sessionkey_mpi); if (err) { log_error ("KEM decap failed: %s\n", gpg_strerror (err)); goto leave; } put_membuf_printf (outbuf, "(5:value%u:", (unsigned int)sessionkey_encoded_len); put_membuf (outbuf, sessionkey_encoded, sessionkey_encoded_len); put_membuf (outbuf, ")", 2); leave: gcry_sexp_release (s_skey0); gcry_sexp_release (s_skey1); xfree (shadow_info); return err; } gpg_error_t agent_kem_decap (ctrl_t ctrl, const char *desc_text, int kemid, const unsigned char *ciphertext, size_t ciphertextlen, membuf_t *outbuf, const unsigned char *option, size_t optionlen) { gcry_sexp_t s_skey = NULL, s_cipher = NULL; unsigned char *shadow_info = NULL; gpg_error_t err = 0; int no_shadow_info = 0; if (!ctrl->have_keygrip) { log_error ("speculative decryption not yet supported\n"); err = gpg_error (GPG_ERR_NO_SECKEY); goto leave; } err = gcry_sexp_sscan (&s_cipher, NULL, (char*)ciphertext, ciphertextlen); if (err) { log_error ("failed to convert ciphertext: %s\n", gpg_strerror (err)); err = gpg_error (GPG_ERR_INV_DATA); goto leave; } if (DBG_CRYPTO) { log_printhex (ctrl->keygrip, 20, "keygrip:"); log_printhex (ciphertext, ciphertextlen, "cipher: "); } if (ctrl->have_keygrip1) { err = agent_hybrid_kem_decap (ctrl, desc_text, kemid, s_cipher, outbuf); goto leave; } err = agent_key_from_file (ctrl, NULL, desc_text, NULL, &shadow_info, CACHE_MODE_NORMAL, NULL, &s_skey, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_NO_SECKEY) no_shadow_info = 1; else if (err) { log_error ("failed to read the secret key\n"); goto leave; } if (shadow_info || no_shadow_info) { /* divert operation to the smartcard */ err = gpg_error (GPG_ERR_NO_SECKEY); /* Not implemented yet. */ goto leave; } else { /* No smartcard, but a private key */ unsigned int nbits; gcry_mpi_t seckey_mpi; gcry_mpi_t ephemkey_mpi; gcry_mpi_t encrypted_sessionkey_mpi; const unsigned char *p; size_t len; unsigned char seckey[32]; size_t seckeylen; const unsigned char *ephemkey; size_t ephemkeylen; const unsigned char *encrypted_sessionkey; size_t encrypted_sessionkey_len; unsigned char kekkey[32]; /* FIXME */ size_t kekkeylen; gcry_cipher_hd_t hd; unsigned char sessionkey_encoded[256]; size_t sessionkey_encoded_len; gcry_sexp_extract_param (s_skey, NULL, "/d", &seckey_mpi, NULL); gcry_sexp_extract_param (s_cipher, NULL, "/e/s", &ephemkey_mpi, &encrypted_sessionkey_mpi, NULL); p = gcry_mpi_get_opaque (seckey_mpi, &nbits); len = (nbits+7)/8; memset (seckey, 0, 32); memcpy (seckey + 32 - len, p, len); seckeylen = 32; reverse_buffer (seckey, seckeylen); ephemkey = gcry_mpi_get_opaque (ephemkey_mpi, &nbits); ephemkeylen = (nbits+7)/8; /* Remove the 0x40 prefix*/ ephemkey++; ephemkeylen--; encrypted_sessionkey = gcry_mpi_get_opaque (encrypted_sessionkey_mpi, &nbits); encrypted_sessionkey_len = (nbits+7)/8; /*FIXME make sure the lengths are all correct. */ encrypted_sessionkey_len--; if (encrypted_sessionkey[0] != encrypted_sessionkey_len) { err = GPG_ERR_INV_DATA; goto leave; } encrypted_sessionkey++; /*FIXME: check the internal of optional to determine the KEK-algo and KEKKEYLEN. */ kekkeylen = 16; err = gcry_kem_decap (GCRY_KEM_PGP_X25519, seckey, seckeylen, ephemkey, ephemkeylen, kekkey, kekkeylen, option, optionlen); mpi_release (seckey_mpi); mpi_release (ephemkey_mpi); if (DBG_CRYPTO) { log_printhex (kekkey, kekkeylen, "KEK key: "); } /*FIXME*/ err = gcry_cipher_open (&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) { log_error ("ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (err)); mpi_release (encrypted_sessionkey_mpi); goto leave; } err = gcry_cipher_setkey (hd, kekkey, kekkeylen); sessionkey_encoded_len = encrypted_sessionkey_len - 8; gcry_cipher_decrypt (hd, sessionkey_encoded, sessionkey_encoded_len, encrypted_sessionkey, encrypted_sessionkey_len); gcry_cipher_close (hd); mpi_release (encrypted_sessionkey_mpi); if (err) { log_error ("KEM decap failed: %s\n", gpg_strerror (err)); goto leave; } put_membuf_printf (outbuf, "(5:value%u:", (unsigned int)sessionkey_encoded_len); put_membuf (outbuf, sessionkey_encoded, sessionkey_encoded_len); put_membuf (outbuf, ")", 2); } leave: gcry_sexp_release (s_skey); gcry_sexp_release (s_cipher); xfree (shadow_info); return err; } diff --git a/common/Makefile.am b/common/Makefile.am index d27603f96..e4dcf8ad6 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -1,227 +1,227 @@ # Makefile for common gnupg modules # Copyright (C) 2001, 2003, 2007, 2010 Free Software Foundation, Inc. # # This file is part of GnuPG. # # GnuPG 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 3 of the License, or # (at your option) any later version. # # GnuPG 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 . ## Process this file with automake to produce Makefile.in EXTRA_DIST = mkstrtable.awk exaudit.awk exstatus.awk ChangeLog-2011 \ audit-events.h status-codes.h ChangeLog.jnlib \ ChangeLog-2011.include w32info-rc.h.in gnupg.ico \ all-tests.scm noinst_LIBRARIES = libcommon.a libcommonpth.a libgpgrl.a noinst_LIBRARIES += libsimple-pwquery.a noinst_PROGRAMS = $(module_tests) $(module_maint_tests) if DISABLE_TESTS TESTS = else TESTS = $(module_tests) endif BUILT_SOURCES = audit-events.h status-codes.h MAINTAINERCLEANFILES = audit-events.h status-codes.h AM_CPPFLAGS = AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(KSBA_CFLAGS) include $(top_srcdir)/am/cmacros.am common_sources = \ common-defs.h \ util.h utilproto.h fwddecl.h i18n.c i18n.h \ types.h host2net.h dynload.h w32help.h \ mapstrings.c stringhelp.c stringhelp.h \ strlist.c strlist.h \ utf8conv.c utf8conv.h \ logging.h \ dotlock.c dotlock.h \ mischelp.c mischelp.h \ status.c status.h\ shareddefs.h \ openpgpdefs.h \ gc-opt-flags.h \ sexp-parse.h \ tlv.c tlv.h tlv-builder.c tlv-parser.c \ init.c init.h \ sexputil.c \ sysutils.c sysutils.h \ homedir.c \ gettime.c gettime.h \ yesno.c \ zb32.c zb32.h \ convert.c \ percent.c \ mbox-util.c mbox-util.h \ miscellaneous.c \ xasprintf.c \ xreadline.c \ membuf.c membuf.h \ ccparray.c ccparray.h \ iobuf.c iobuf.h \ ttyio.c ttyio.h \ asshelp.c asshelp2.c asshelp.h \ exechelp.h \ signal.c \ audit.c audit.h \ localename.c \ session-env.c session-env.h \ userids.c userids.h \ openpgp-oid.c openpgp-s2k.c \ ssh-utils.c ssh-utils.h \ agent-opt.c \ helpfile.c \ mkdir_p.c mkdir_p.h \ strlist.c strlist.h \ exectool.c exectool.h \ server-help.c server-help.h \ name-value.c name-value.h \ recsel.c recsel.h \ ksba-io-support.c ksba-io-support.h \ openpgp-fpr.c \ comopt.c comopt.h \ compliance.c compliance.h \ - pkscreening.c pkscreening.h - + pkscreening.c pkscreening.h \ + kmac.c if HAVE_W32_SYSTEM common_sources += w32-reg.c w32-cmdline.c endif # To make the code easier to read we have split home some code into # separate source files. if HAVE_W32_SYSTEM common_sources += exechelp-w32.c else common_sources += exechelp-posix.c endif # Sources only useful without NPTH. without_npth_sources = \ get-passphrase.c get-passphrase.h # Sources only useful with NPTH. with_npth_sources = \ call-gpg.c call-gpg.h libcommon_a_SOURCES = $(common_sources) $(without_npth_sources) libcommon_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) -DWITHOUT_NPTH=1 libcommonpth_a_SOURCES = $(common_sources) $(with_npth_sources) libcommonpth_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) libsimple_pwquery_a_SOURCES = \ simple-pwquery.c simple-pwquery.h asshelp.c asshelp.h libsimple_pwquery_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) libgpgrl_a_SOURCES = \ gpgrlhelp.c if MAINTAINER_MODE # Note: Due to the dependency on Makefile, the file will always be # rebuilt, so we allow this only in maintainer mode. # Create the audit-events.h include file from audit.h # Note: We create the target file in the source directory because it # is a distributed built source. If we would not do that we may end # up with two files and then it is not clear which version of the # files will be picked up. audit-events.h: Makefile.am mkstrtable.awk exaudit.awk audit.h $(AWK) -f $(srcdir)/exaudit.awk $(srcdir)/audit.h \ | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ -v pkg_namespace=eventstr_ > $(srcdir)/audit-events.h # Create the status-codes.h include file from status.h status-codes.h: Makefile.am mkstrtable.awk exstatus.awk status.h $(AWK) -f $(srcdir)/exstatus.awk $(srcdir)/status.h \ | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ -v pkg_namespace=statusstr_ > $(srcdir)/status-codes.h endif # # Module tests # module_tests = t-stringhelp t-timestuff \ t-convert t-percent t-gettime t-sysutils t-sexputil \ t-session-env t-openpgp-oid t-ssh-utils \ t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist \ t-name-value t-ccparray t-recsel t-w32-cmdline t-exechelp if HAVE_W32_SYSTEM module_tests += t-w32-reg else module_tests += t-exectool endif if MAINTAINER_MODE module_maint_tests = t-helpfile else module_maint_tests = endif t_extra_src = t-support.h t_common_cflags = $(KSBA_CFLAGS) $(LIBGCRYPT_CFLAGS) \ $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(INCICONV) t_common_ldadd = libcommon.a \ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ $(LIBINTL) $(LIBICONV) $(NETLIBS) # Common tests t_stringhelp_SOURCES = t-stringhelp.c $(t_extra_src) t_stringhelp_LDADD = $(t_common_ldadd) t_timestuff_SOURCES = t-timestuff.c $(t_extra_src) t_timestuff_LDADD = $(t_common_ldadd) t_convert_LDADD = $(t_common_ldadd) t_percent_LDADD = $(t_common_ldadd) t_gettime_LDADD = $(t_common_ldadd) t_sysutils_LDADD = $(t_common_ldadd) t_helpfile_LDADD = $(t_common_ldadd) t_sexputil_LDADD = $(t_common_ldadd) t_exechelp_LDADD = $(t_common_ldadd) t_exectool_LDADD = $(t_common_ldadd) t_session_env_LDADD = $(t_common_ldadd) t_openpgp_oid_LDADD = $(t_common_ldadd) t_ssh_utils_LDADD = $(t_common_ldadd) t_mapstrings_LDADD = $(t_common_ldadd) t_zb32_SOURCES = t-zb32.c $(t_extra_src) t_zb32_LDADD = $(t_common_ldadd) t_mbox_util_LDADD = $(t_common_ldadd) t_iobuf_LDADD = $(t_common_ldadd) t_strlist_LDADD = $(t_common_ldadd) t_name_value_LDADD = $(t_common_ldadd) t_ccparray_LDADD = $(t_common_ldadd) t_recsel_LDADD = $(t_common_ldadd) t_w32_cmdline_SOURCES = t-w32-cmdline.c w32-cmdline.c $(t_extra_src) t_w32_cmdline_LDADD = $(t_common_ldadd) # System specific test if HAVE_W32_SYSTEM t_w32_reg_SOURCES = t-w32-reg.c $(t_extra_src) t_w32_reg_LDADD = $(t_common_ldadd) endif # All programs should depend on the created libs. $(PROGRAMS) : libcommon.a libcommonpth.a diff --git a/common/kmac.c b/common/kmac.c new file mode 100644 index 000000000..0e8ff316b --- /dev/null +++ b/common/kmac.c @@ -0,0 +1,132 @@ +/* kmac.c - Keccak based MAC + * Copyright (C) 2024 g10 Code GmbH. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG 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. + * + * GnuPG 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 copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see . + */ + +#include +#include +#include +#include +#include +#include "mischelp.h" + +#define KECCAK512_BLOCKSIZE 136 +gpg_error_t +compute_kmac256 (void *digest, size_t digestlen, + const void *key, size_t keylen, + const void *custom, size_t customlen, + gcry_buffer_t *data_iov, int data_iovlen) +{ + gpg_error_t err; + gcry_buffer_t iov[20]; + const unsigned char headPAD[2] = { 1, KECCAK512_BLOCKSIZE }; + unsigned char headK[3]; + const unsigned char pad[KECCAK512_BLOCKSIZE] = { 0 }; + unsigned char right_encode_L[3]; + unsigned int len; + int iovcnt; + + if (data_iovlen >= DIM(iov) - 6) + return gpg_error (GPG_ERR_TOO_LARGE); + + /* Check the validity conditions of NIST SP 800-185 */ + if (keylen >= 255 || customlen >= 255 || digestlen >= 255) + return gpg_error (GPG_ERR_TOO_LARGE); + + iovcnt = 0; + iov[iovcnt].data = "KMAC"; + iov[iovcnt].off = 0; + iov[iovcnt].len = 4; + iovcnt++; + + iov[iovcnt].data = (void *)custom; + iov[iovcnt].off = 0; + iov[iovcnt].len = customlen; + iovcnt++; + + iov[iovcnt].data = (void *)headPAD; + iov[iovcnt].off = 0; + iov[iovcnt].len = sizeof (headPAD); + iovcnt++; + + if (keylen < 32) + { + headK[0] = 1; + headK[1] = (keylen*8)&0xff; + iov[iovcnt].data = headK; + iov[iovcnt].off = 0; + iov[iovcnt].len = 2; + } + else + { + headK[0] = 2; + headK[1] = (keylen*8)>>8; + headK[2] = (keylen*8)&0xff; + iov[iovcnt].data = headK; + iov[iovcnt].off = 0; + iov[iovcnt].len = 3; + } + iovcnt++; + + iov[iovcnt].data = (void *)key; + iov[iovcnt].off = 0; + iov[iovcnt].len = keylen; + iovcnt++; + + len = iov[2].len + iov[3].len + iov[4].len; + len %= KECCAK512_BLOCKSIZE; + + iov[iovcnt].data = (unsigned char *)pad; + iov[iovcnt].off = 0; + iov[iovcnt].len = sizeof (pad) - len; + iovcnt++; + + memcpy (&iov[iovcnt], data_iov, data_iovlen * sizeof (gcry_buffer_t)); + iovcnt += data_iovlen; + + if (digestlen < 32) + { + right_encode_L[0] = (digestlen * 8) & 0xff; + right_encode_L[1] = 1; + } + else + { + right_encode_L[0] = (digestlen * 8) >> 8; + right_encode_L[1] = (digestlen * 8) & 0xff; + right_encode_L[2] = 2; + } + + iov[iovcnt].data = right_encode_L; + iov[iovcnt].off = 0; + iov[iovcnt].len = 3; + iovcnt++; + + err = gcry_md_hash_buffers_extract (GCRY_MD_CSHAKE256, 0, + digest, digestlen, iov, iovcnt); + return err; +} diff --git a/common/util.h b/common/util.h index 2b46ec930..f3fbfc3c8 100644 --- a/common/util.h +++ b/common/util.h @@ -1,408 +1,414 @@ /* util.h - Utility functions for GnuPG * Copyright (C) 2001, 2002, 2003, 2004, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute and/or modify this * part of GnuPG 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. * * GnuPG 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 copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ #ifndef GNUPG_COMMON_UTIL_H #define GNUPG_COMMON_UTIL_H #include /* We need this for the memory function protos. */ #include /* We need errno. */ #include /* We need gpg_error_t and estream. */ /* These error codes are used but not defined in the required * libgpg-error version. Define them here. * Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21) */ #if GPG_ERROR_VERSION_NUMBER < 0x012f00 /* 1.47 */ # define GPG_ERR_BAD_PUK 320 # define GPG_ERR_NO_RESET_CODE 321 # define GPG_ERR_BAD_RESET_CODE 322 #endif #ifndef EXTERN_UNLESS_MAIN_MODULE # if !defined (INCLUDED_BY_MAIN_MODULE) # define EXTERN_UNLESS_MAIN_MODULE extern # else # define EXTERN_UNLESS_MAIN_MODULE # endif #endif /* Hash function used with libksba. */ #define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write) /* The length of the keygrip. This is a SHA-1 hash of the key * parameters as generated by gcry_pk_get_keygrip. */ #define KEYGRIP_LEN 20 /* The length of the unique blob identifier as used by the keyboxd. * This is the possible truncated fingerprint of the primary key. */ #define UBID_LEN 20 /* Get all the stuff from jnlib. */ #include "../common/logging.h" #include "../common/stringhelp.h" #include "../common/mischelp.h" #include "../common/strlist.h" #include "../common/dotlock.h" #include "../common/utf8conv.h" #include "../common/dynload.h" #include "../common/fwddecl.h" #include "../common/utilproto.h" #include "gettime.h" /* Redefine asprintf by our estream version which uses our own memory allocator.. */ #define asprintf gpgrt_asprintf #define vasprintf gpgrt_vasprintf /* Due to a bug in mingw32's snprintf related to the 'l' modifier and for increased portability we use our snprintf on all systems. */ #undef snprintf #define snprintf gpgrt_snprintf /* Replacements for macros not available with libgpg-error < 1.20. */ /* We need this type even if we are not using libreadline and or we did not include libreadline in the current file. */ #ifndef GNUPG_LIBREADLINE_H_INCLUDED typedef char **rl_completion_func_t (const char *, int, int); #endif /*!GNUPG_LIBREADLINE_H_INCLUDED*/ /* Handy malloc macros - please use only them. */ #define xtrymalloc(a) gcry_malloc ((a)) #define xtrymalloc_secure(a) gcry_malloc_secure ((a)) #define xtrycalloc(a,b) gcry_calloc ((a),(b)) #define xtrycalloc_secure(a,b) gcry_calloc_secure ((a),(b)) #define xtryrealloc(a,b) gcry_realloc ((a),(b)) #define xtryreallocarray(a,b,c,d) gpgrt_reallocarray ((a),(b),(c),(d)) #define xtrystrdup(a) gcry_strdup ((a)) #define xfree(a) gcry_free ((a)) #define xfree_fnc gcry_free #define xmalloc(a) gcry_xmalloc ((a)) #define xmalloc_secure(a) gcry_xmalloc_secure ((a)) #define xcalloc(a,b) gcry_xcalloc ((a),(b)) #define xcalloc_secure(a,b) gcry_xcalloc_secure ((a),(b)) #define xrealloc(a,b) gcry_xrealloc ((a),(b)) #define xstrdup(a) gcry_xstrdup ((a)) /* See also the xreallocarray prototype below. */ /* For compatibility with gpg 1.4 we also define these: */ #define xmalloc_clear(a) gcry_xcalloc (1, (a)) #define xmalloc_secure_clear(a) gcry_xcalloc_secure (1, (a)) /* The default error source of the application. This is different from GPG_ERR_SOURCE_DEFAULT in that it does not depend on the source file and thus is usable in code shared by applications. Defined by init.c. */ extern gpg_err_source_t default_errsource; /* Convenience function to return a gpg-error code for memory allocation failures. This function makes sure that an error will be returned even if accidentally ERRNO is not set. */ static inline gpg_error_t out_of_core (void) { return gpg_error_from_syserror (); } /*-- yesno.c --*/ int answer_is_yes (const char *s); int answer_is_yes_no_default (const char *s, int def_answer); int answer_is_yes_no_quit (const char *s); int answer_is_okay_cancel (const char *s, int def_answer); /*-- xreadline.c --*/ ssize_t read_line (FILE *fp, char **addr_of_buffer, size_t *length_of_buffer, size_t *max_length); /*-- sexputil.c */ char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen); void log_printcanon (const char *text, const unsigned char *sexp, size_t sexplen); void log_printsexp (const char *text, gcry_sexp_t sexp); gpg_error_t make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen); gpg_error_t make_canon_sexp_pad (gcry_sexp_t sexp, int secure, unsigned char **r_buffer, size_t *r_buflen); gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen, unsigned char *grip); int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b); int cmp_canon_sexp (const unsigned char *a, size_t alen, const unsigned char *b, size_t blen, int (*tcmp)(void *ctx, int depth, const unsigned char *aval, size_t avallen, const unsigned char *bval, size_t bvallen), void *tcmpctx); unsigned char *make_simple_sexp_from_hexstr (const char *line, size_t *nscanned); int hash_algo_from_sigval (const unsigned char *sigval); unsigned char *make_canon_sexp_from_rsa_pk (const void *m, size_t mlen, const void *e, size_t elen, size_t *r_len); gpg_error_t get_rsa_pk_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, unsigned char const **r_n, size_t *r_nlen, unsigned char const **r_e, size_t *r_elen); gpg_error_t get_ecc_q_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, unsigned char const **r_q, size_t *r_qlen); gpg_error_t uncompress_ecc_q_in_canon_sexp (const unsigned char *keydata, size_t keydatalen, unsigned char **r_newkeydata, size_t *r_newkeydatalen); int get_pk_algo_from_key (gcry_sexp_t key); int get_pk_algo_from_canon_sexp (const unsigned char *keydata, size_t keydatalen); char *pubkey_algo_string (gcry_sexp_t s_pkey, enum gcry_pk_algos *r_algoid); const char *pubkey_algo_to_string (int algo); const char *hash_algo_to_string (int algo); const char *cipher_mode_to_string (int mode); /*-- convert.c --*/ int hex2bin (const char *string, void *buffer, size_t length); int hexcolon2bin (const char *string, void *buffer, size_t length); char *bin2hex (const void *buffer, size_t length, char *stringbuf); char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf); const char *hex2str (const char *hexstring, char *buffer, size_t bufsize, size_t *buflen); char *hex2str_alloc (const char *hexstring, size_t *r_count); unsigned int hex2fixedbuf (const char *hexstr, void *buffer, size_t bufsize); /*-- percent.c --*/ char *percent_plus_escape (const char *string); char *percent_data_escape (int plus, const char *prefix, const void *data, size_t datalen); char *percent_plus_unescape (const char *string, int nulrepl); char *percent_unescape (const char *string, int nulrepl); size_t percent_plus_unescape_inplace (char *string, int nulrepl); size_t percent_unescape_inplace (char *string, int nulrepl); /*-- openpgp-oid.c --*/ gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi); char *openpgp_oidbuf_to_str (const unsigned char *buf, size_t len); char *openpgp_oid_to_str (gcry_mpi_t a); int openpgp_oidbuf_is_ed25519 (const void *buf, size_t len); int openpgp_oid_is_ed25519 (gcry_mpi_t a); int openpgp_oidbuf_is_cv25519 (const void *buf, size_t len); int openpgp_oid_is_cv25519 (gcry_mpi_t a); int openpgp_oid_is_cv448 (gcry_mpi_t a); int openpgp_oid_is_ed448 (gcry_mpi_t a); const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits, int *r_algo); const char *openpgp_oid_to_curve (const char *oid, int canon); const char *openpgp_oid_or_name_to_curve (const char *oidname, int canon); const char *openpgp_enum_curves (int *idxp); const char *openpgp_is_curve_supported (const char *name, int *r_algo, unsigned int *r_nbits); const char *get_keyalgo_string (enum gcry_pk_algos algo, unsigned int nbits, const char *curve); /*-- homedir.c --*/ const char *standard_homedir (void); void gnupg_set_homedir (const char *newdir); void gnupg_maybe_make_homedir (const char *fname, int quiet); const char *gnupg_homedir (void); int gnupg_default_homedir_p (void); const char *gnupg_daemon_rootdir (void); const char *gnupg_socketdir (void); const char *gnupg_sysconfdir (void); const char *gnupg_bindir (void); const char *gnupg_libexecdir (void); const char *gnupg_libdir (void); const char *gnupg_datadir (void); const char *gnupg_localedir (void); const char *gpg_agent_socket_name (void); const char *dirmngr_socket_name (void); const char *keyboxd_socket_name (void); char *_gnupg_socketdir_internal (int skip_checks, unsigned *r_info); /* All module names. We also include gpg and gpgsm for the sake for gpgconf. */ #define GNUPG_MODULE_NAME_AGENT 1 #define GNUPG_MODULE_NAME_PINENTRY 2 #define GNUPG_MODULE_NAME_SCDAEMON 3 #define GNUPG_MODULE_NAME_DIRMNGR 4 #define GNUPG_MODULE_NAME_PROTECT_TOOL 5 #define GNUPG_MODULE_NAME_CHECK_PATTERN 6 #define GNUPG_MODULE_NAME_GPGSM 7 #define GNUPG_MODULE_NAME_GPG 8 #define GNUPG_MODULE_NAME_CONNECT_AGENT 9 #define GNUPG_MODULE_NAME_GPGCONF 10 #define GNUPG_MODULE_NAME_DIRMNGR_LDAP 11 #define GNUPG_MODULE_NAME_GPGV 12 #define GNUPG_MODULE_NAME_KEYBOXD 13 #define GNUPG_MODULE_NAME_TPM2DAEMON 14 #define GNUPG_MODULE_NAME_CARD 15 #define GNUPG_MODULE_NAME_GPGTAR 16 const char *gnupg_module_name (int which); void gnupg_module_name_flush_some (void); void gnupg_set_builddir (const char *newdir); /* A list of constants to identify protocols. This is used by tools * which need to distinguish between the different protocols * implemented by GnuPG. May be used as bit flags. */ #define GNUPG_PROTOCOL_OPENPGP 1 /* The one and only (gpg). */ #define GNUPG_PROTOCOL_CMS 2 /* The core of S/MIME (gpgsm) */ #define GNUPG_PROTOCOL_SSH_AGENT 4 /* Out ssh-agent implementation */ /*-- gpgrlhelp.c --*/ void gnupg_rl_initialize (void); /*-- helpfile.c --*/ char *gnupg_get_help_string (const char *key, int only_current_locale); /*-- localename.c --*/ const char *gnupg_messages_locale_name (void); +/*-- kmac.c --*/ +gpg_error_t compute_kmac256 (void *digest, size_t digestlen, + const void *key, size_t keylen, + const void *custom, size_t customlen, + gcry_buffer_t *data_iov, int data_iovlen); + /*-- miscellaneous.c --*/ /* This function is called at startup to tell libgcrypt to use our own logging subsystem. */ void setup_libgcrypt_logging (void); /* Print an out of core message and die. */ void xoutofcore (void); /* Wrapper aroung gpgrt_reallocarray. Uses the gpgrt alloc function * which redirects to the Libgcrypt versions via * init_common_subsystems. Thus this can be used interchangeable with * the other alloc functions. */ void *xreallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size); /* Same as estream_asprintf but die on memory failure. */ char *xasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); /* This is now an alias to estream_asprintf. */ char *xtryasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); /* Replacement for gcry_cipher_algo_name. */ const char *gnupg_cipher_algo_name (int algo); void obsolete_option (const char *configname, unsigned int configlineno, const char *name); const char *print_fname_stdout (const char *s); const char *print_fname_stdin (const char *s); void print_utf8_buffer3 (estream_t fp, const void *p, size_t n, const char *delim); void print_utf8_buffer2 (estream_t fp, const void *p, size_t n, int delim); void print_utf8_buffer (estream_t fp, const void *p, size_t n); void print_utf8_string (estream_t stream, const char *p); void print_hexstring (FILE *fp, const void *buffer, size_t length, int reserved); char *try_make_printable_string (const void *p, size_t n, int delim); char *make_printable_string (const void *p, size_t n, int delim); char *decode_c_string (const char *src); int match_multistr (const char *multistr,const char *match); int gnupg_compare_version (const char *a, const char *b); struct debug_flags_s { unsigned int flag; const char *name; }; int parse_debug_flag (const char *string, unsigned int *debugvar, const struct debug_flags_s *flags); struct compatibility_flags_s { unsigned int flag; const char *name; const char *desc; }; int parse_compatibility_flags (const char *string, unsigned int *flagvar, const struct compatibility_flags_s *flags); gpg_error_t b64decode (const char *string, const char *title, void **r_buffer, size_t *r_buflen); /*-- Simple replacement functions. */ /* We use the gnupg_ttyname macro to be safe not to run into conflicts which an extisting but broken ttyname. */ #if !defined(HAVE_TTYNAME) || defined(HAVE_BROKEN_TTYNAME) # define gnupg_ttyname(n) _gnupg_ttyname ((n)) /* Systems without ttyname (W32) will merely return NULL. */ static inline char * _gnupg_ttyname (int fd) { (void)fd; return NULL; } #else /*HAVE_TTYNAME*/ # define gnupg_ttyname(n) ttyname ((n)) #endif /*HAVE_TTYNAME */ #define gnupg_isatty(a) isatty ((a)) /*-- Macros to replace ctype ones to avoid locale problems. --*/ #define spacep(p) (*(p) == ' ' || *(p) == '\t') #define digitp(p) (*(p) >= '0' && *(p) <= '9') #define alphap(p) ((*(p) >= 'A' && *(p) <= 'Z') \ || (*(p) >= 'a' && *(p) <= 'z')) #define alnump(p) (alphap (p) || digitp (p)) #define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f')) /* Note this isn't identical to a C locale isspace() without \f and \v, but works for the purposes used here. */ #define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t') /* The atoi macros assume that the buffer has only valid digits. */ #define atoi_1(p) (*(p) - '0' ) #define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) #define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2)) #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) #define xtoi_4(p) ((xtoi_2(p) * 256) + xtoi_2((p)+2)) #endif /*GNUPG_COMMON_UTIL_H*/