diff options
| author | Werner Koch <[email protected]> | 2026-09-11 11:13:02 +0200 |
|---|---|---|
| committer | Werner Koch <[email protected]> | 2026-09-11 11:13:02 +0200 |
| commit | 6803ce215ce5c2e9b0fcafd35afe4ffec75ba718 (patch) | |
| tree | 36b9cf98e6eb0c7eab89a1da2f0da7be0a9f0871 | |
| parent | gpgconf,w32: Show more registry key of GpgOL. (diff) | |
| parent | gpg: Implictly set allow-9980 in de-vs compliance mode. (diff) | |
| download | gnupg-6803ce215ce5c2e9b0fcafd35afe4ffec75ba718.tar.gz gnupg-6803ce215ce5c2e9b0fcafd35afe4ffec75ba718.zip | |
Merge branch 'wk/rfc9980'
--
After 25 years the NSA could even convince GnuPG that their idea of
using complex algorithm is better for someones(tm) security. But
fortunately there is no need to enable it. Thus all guards of pretty
good and gnu privacy won't enable --compliance=fips|de-vs or use the
option --allow-9980.
86 files changed, 4981 insertions, 767 deletions
diff --git a/agent/command.c b/agent/command.c index ca2cecb63..b59168caa 100644 --- a/agent/command.c +++ b/agent/command.c @@ -2959,8 +2959,11 @@ cmd_import_key (assuan_context_t ctx, char *line) } if (opt_unattended && keydata_type != KEYDATA_PGP_TRANSFER - && keydata_type != KEYDATA_COMPOSITE) + && keydata_type != KEYDATA_COMPOSITE + && !mode1003) { + /* Note that in mode1003 no passphrase is requested at all and thus we + implictly are in unattended mode. */ err = set_error (GPG_ERR_ASS_PARAMETER, "\"--unattended\" may only be used with OpenPGP keys"); goto leave; diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index 202623426..0dd784758 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -44,6 +44,7 @@ struct try_do_unprotect_arg_s byte *s2k_salt; u32 s2k_count; u16 desired_csum; + int no_sos; /* Hack for 9980 keys. */ gcry_mpi_t *skey; size_t skeysize; int skeyidx; @@ -313,6 +314,7 @@ get_npkey_nskey (int pubkey_algo, size_t *npkey, size_t *nskey) case GCRY_PK_ELG_E: *npkey = 3; *nskey = 4; break; case GCRY_PK_DSA: *npkey = 4; *nskey = 5; break; case GCRY_PK_ECC: *npkey = 1; *nskey = 2; break; + case GCRY_PK_KEM: *npkey = 1; *nskey = 2; break; default: *npkey = 0; *nskey = 0; break; } } @@ -423,7 +425,7 @@ do_unprotect (const char *passphrase, const char *curve, gcry_mpi_t *skey, size_t skeysize, int protect_algo, void *protect_iv, size_t protect_ivlen, int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count, - u16 desired_csum, gcry_sexp_t *r_key) + u16 desired_csum, int no_sos, gcry_sexp_t *r_key) { gpg_error_t err; unsigned int npkey, nskey, skeylen; @@ -459,19 +461,22 @@ do_unprotect (const char *passphrase, nbytes = (nbits+7)/8; nbits = nbytes * 8; - if (*buffer) - if (nbits >= 8 && !(*buffer & 0x80)) - if (--nbits >= 7 && !(*buffer & 0x40)) - if (--nbits >= 6 && !(*buffer & 0x20)) - if (--nbits >= 5 && !(*buffer & 0x10)) - if (--nbits >= 4 && !(*buffer & 0x08)) - if (--nbits >= 3 && !(*buffer & 0x04)) - if (--nbits >= 2 && !(*buffer & 0x02)) - if (--nbits >= 1 && !(*buffer & 0x01)) - --nbits; + if (!no_sos) + { + if (*buffer) + if (nbits >= 8 && !(*buffer & 0x80)) + if (--nbits >= 7 && !(*buffer & 0x40)) + if (--nbits >= 6 && !(*buffer & 0x20)) + if (--nbits >= 5 && !(*buffer & 0x10)) + if (--nbits >= 4 && !(*buffer & 0x08)) + if (--nbits >= 3 && !(*buffer & 0x04)) + if (--nbits >= 2 && !(*buffer & 0x02)) + if (--nbits >= 1 && !(*buffer & 0x01)) + --nbits; - actual_csum += (nbits >> 8); - actual_csum += (nbits & 0xff); + actual_csum += (nbits >> 8); + actual_csum += (nbits & 0xff); + } actual_csum += checksum (buffer, nbytes); } else @@ -752,7 +757,7 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi) arg->protect_algo, arg->iv, arg->ivlen, arg->s2k_mode, arg->s2k_algo, arg->s2k_salt, arg->s2k_count, - arg->desired_csum, arg->r_key); + arg->desired_csum, arg->no_sos, arg->r_key); /* SKEY may be modified now, thus we need to re-compute SKEYIDX. */ for (arg->skeyidx = 0; (arg->skeyidx < arg->skeysize && arg->skey[arg->skeyidx]); arg->skeyidx++) @@ -781,6 +786,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist, char *string; int idx; int is_v4, is_protected; + int no_sos = 0; int pubkey_algo; int protect_algo = 0; char iv[16]; @@ -876,14 +882,25 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist, string = gcry_sexp_nth_string (list, 1); if (!string) goto bad_seckey; - pubkey_algo = gcry_pk_map_name (string); + if (string && !strcmp (string, "ietf27")) + { + pubkey_algo = GCRY_PK_ECC; + no_sos = 1; + } + else if (string && !strcmp (string, "mlk768")) + { + pubkey_algo = GCRY_PK_KEM; + no_sos = 1; + } + else + pubkey_algo = gcry_pk_map_name (string); xfree (string); get_npkey_nskey (pubkey_algo, &npkey, &nskey); if (!npkey || !nskey || npkey >= nskey) goto bad_seckey; - if (npkey == 1) /* This is ECC */ + if (npkey == 1 && pubkey_algo != GCRY_PK_KEM) /* This is ECC */ { gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "curve", 0); @@ -1023,6 +1040,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist, pi_arg.s2k_salt = s2k_salt; pi_arg.s2k_count = s2k_count; pi_arg.desired_csum = desired_csum; + pi_arg.no_sos = no_sos; pi_arg.skey = skey; pi_arg.skeysize = DIM (skey); pi_arg.skeyidx = skeyidx; diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 85bca9bc9..827d5efc0 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -81,6 +81,7 @@ enum cmd_and_opt_values oDebugWait, oDebugQuickRandom, oDebugPinentry, + oDebugNoLibgcrypt, oNoOptions, oHomedir, oNoDetach, @@ -197,6 +198,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_i (oDebugWait, "debug-wait", "@"), ARGPARSE_s_n (oDebugQuickRandom, "debug-quick-random", "@"), ARGPARSE_s_n (oDebugPinentry, "debug-pinentry", "@"), + ARGPARSE_s_n (oDebugNoLibgcrypt, "debug-no-libgcrypt", "@"), ARGPARSE_s_s (oLogFile, "log-file", /* */ N_("|FILE|write server mode logs to FILE")), @@ -350,6 +352,15 @@ static struct debug_flags_s debug_flags [] = * parent process and homedir. Value is in seconds. */ #define CHECK_PROBLEMS_INTERVAL (4) + +/* Collection of options used only in this module. */ +/* These shall be used for new local options instead of all the + * separate file local variables. */ +static struct { + unsigned int no_libgcrypt_debug; +} mopt; + + /* Flag indicating that the ssh-agent subsystem has been enabled. */ static int ssh_support; @@ -647,9 +658,9 @@ set_debug (void) if (opt.debug && opt.quiet) opt.quiet = 0; - if (opt.debug & DBG_MPI_VALUE) + if ((opt.debug & DBG_MPI_VALUE) && !mopt.no_libgcrypt_debug) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2); - if (opt.debug & DBG_CRYPTO_VALUE ) + if ((opt.debug & DBG_CRYPTO_VALUE) && !mopt.no_libgcrypt_debug) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); @@ -880,6 +891,7 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread) opt.debug = 0; opt.no_grab = 1; opt.debug_pinentry = 0; + mopt.no_libgcrypt_debug = 0; xfree (opt.pinentry_program); opt.pinentry_program = NULL; opt.pinentry_touch_file = NULL; @@ -925,6 +937,7 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread) case oDebugAll: opt.debug = ~0; break; case oDebugLevel: debug_level = pargs->r.ret_str; break; case oDebugPinentry: opt.debug_pinentry = 1; break; + case oDebugNoLibgcrypt: mopt.no_libgcrypt_debug = 1; break; case oLogFile: if (!reread) diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index 043bcd611..260e3fda1 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -66,7 +66,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, if (DBG_CRYPTO) { log_printhex (ctrl->keygrip, 20, "keygrip:"); - log_printhex (ciphertext, ciphertextlen, "cipher: "); + gcry_log_debugsxp ("ciphr", s_cipher); } err = agent_key_from_file (ctrl, NULL, desc_text, NULL, &shadow_info, @@ -110,11 +110,6 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, } 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) @@ -124,10 +119,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, } if (DBG_CRYPTO) - { - log_debug ("plain: "); - gcry_sexp_dump (s_plain); - } + gcry_log_debugsxp ("plain", s_plain); len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, NULL, 0); log_assert (len); buf = xmalloc (len); @@ -191,11 +183,14 @@ ecc_extract_pk_from_key (const struct gnupg_ecc_params *ecc, p = gcry_mpi_get_opaque (ecc_pk_mpi, &nbits); len = (nbits+7)/8; - if (len != ecc->pubkey_len) + + if (!(len == ecc->pubkey_len + || (ecc->may_have_prefix && len+1 == ecc->pubkey_len))) { if (opt.verbose) - log_info ("%s: ECC public key length invalid (%zu)\n", __func__, len); - err = gpg_error (GPG_ERR_INV_DATA); + log_info ("%s: ECC public key length invalid (%zu != %zu)\n", + __func__, len, ecc->pubkey_len); + err = gpg_error (GPG_ERR_BAD_PUBKEY); goto leave; } else if (len == ecc->point_len) @@ -205,7 +200,7 @@ ecc_extract_pk_from_key (const struct gnupg_ecc_params *ecc, memcpy (ecc_pk, p+1, ecc->point_len); else { - err = gpg_error (GPG_ERR_BAD_SECKEY); + err = gpg_error (GPG_ERR_BAD_PUBKEY); goto leave; } @@ -217,8 +212,9 @@ ecc_extract_pk_from_key (const struct gnupg_ecc_params *ecc, return err; } + static gpg_error_t -ecc_extract_sk_from_key (const struct gnupg_ecc_params *ecc, +ecc_extract_sk_from_key (const struct gnupg_ecc_params *ecc, int no_reverse, gcry_sexp_t s_skey, unsigned char *ecc_sk) { gpg_error_t err; @@ -246,7 +242,7 @@ ecc_extract_sk_from_key (const struct gnupg_ecc_params *ecc, } memset (ecc_sk, 0, ecc->scalar_len - len); memcpy (ecc_sk + ecc->scalar_len - len, p, len); - if (ecc->scalar_reverse) + if (ecc->scalar_reverse && !no_reverse) reverse_buffer (ecc_sk, ecc->scalar_len); mpi_release (ecc_sk_mpi); ecc_sk_mpi = NULL; @@ -259,8 +255,12 @@ ecc_extract_sk_from_key (const struct gnupg_ecc_params *ecc, return err; } + +/* Note the NO_REVERSE is required because we cant change the info in + * ECC easily. */ static gpg_error_t -ecc_raw_kem (const struct gnupg_ecc_params *ecc, gcry_sexp_t s_skey, +ecc_raw_kem (const struct gnupg_ecc_params *ecc, int no_reverse, + gcry_sexp_t s_skey, const unsigned char *ecc_ct, unsigned char *ecc_ecdh) { gpg_error_t err = 0; @@ -275,7 +275,7 @@ ecc_raw_kem (const struct gnupg_ecc_params *ecc, gcry_sexp_t s_skey, goto leave; } - err = ecc_extract_sk_from_key (ecc, s_skey, ecc_sk); + err = ecc_extract_sk_from_key (ecc, no_reverse, s_skey, ecc_sk); if (err) goto leave; @@ -350,20 +350,22 @@ ecc_get_curve (ctrl_t ctrl, gcry_sexp_t s_skey, const char **r_curve) return err; } + /* Given a private key in SEXP by S_SKEY0 and a cipher text by ECC_CT * with length ECC_POINT_LEN, do ECC KEM decap (== raw ECDH) * operation. Result is returned in the memory referred by ECC_ECDH. * Public key is extracted and put into ECC_PK. The pointer to ECC * parameters is stored into R_ECC. SHADOW_INFO0 is used to determine * if the private key is actually on smartcard. CTRL is used to - * access smartcard, internally. */ + * access smartcard, internally. With NO_REVERSE set the possible + * reversing of the secret key is suppressed. */ static gpg_error_t ecc_kem_decap (ctrl_t ctrl, gcry_sexp_t s_skey0, const unsigned char *shadow_info0, const unsigned char *ecc_ct, size_t ecc_point_len, unsigned char ecc_ecdh[ECC_POINT_LEN_MAX], unsigned char ecc_pk[ECC_POINT_LEN_MAX], - const struct gnupg_ecc_params **r_ecc) + const struct gnupg_ecc_params **r_ecc, int no_reverse) { gpg_error_t err; const char *curve; @@ -397,6 +399,7 @@ ecc_kem_decap (ctrl_t ctrl, gcry_sexp_t s_skey0, if (ecc->may_have_prefix && ecc_point_len == ecc->point_len + 1 && *ecc_ct == 0x40) { + /* This point has the 0x40 prefix - remove it. */ ecc_ct++; ecc_point_len--; } @@ -440,7 +443,7 @@ ecc_kem_decap (ctrl_t ctrl, gcry_sexp_t s_skey0, } } else - err = ecc_raw_kem (ecc, s_skey0, ecc_ct, ecc_ecdh); + err = ecc_raw_kem (ecc, no_reverse, s_skey0, ecc_ct, ecc_ecdh); if (err) return err; @@ -456,6 +459,7 @@ ecc_kem_decap (ctrl_t ctrl, gcry_sexp_t s_skey0, should follow the format of: (enc-val(pqc(c%d)(e%m)(k%m)(s%m)(fixed-info%b))) + t: Optional combiner type: default or 1 = KDF256, 2 = SHA3-256 c: cipher identifier (of session key (wrapped key)) e: ECDH ciphertext k: ML-KEM ciphertext @@ -477,7 +481,7 @@ composite_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, unsigned int nbits; size_t len; - int algo; + int combiner_type, algo; gcry_mpi_t encrypted_sessionkey_mpi = NULL; const unsigned char *encrypted_sessionkey; size_t encrypted_sessionkey_len; @@ -529,7 +533,8 @@ composite_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, goto leave; } - err = gcry_sexp_extract_param (s_cipher, NULL, "%dc/eks&'fixed-info'", + err = gcry_sexp_extract_param (s_cipher, NULL, "%dt?c/eks&'fixed-info'", + &combiner_type, &algo, &ecc_ct_mpi, &mlkem_ct_mpi, &encrypted_sessionkey_mpi, &fixed_info, NULL); if (err) @@ -538,11 +543,16 @@ composite_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, log_info ("%s: extracting parameters failed\n", __func__); goto leave; } + if (!combiner_type) + combiner_type = 1; ecc_ct = gcry_mpi_get_opaque (ecc_ct_mpi, &nbits); ecc_ct_len = (nbits+7)/8; - len = gcry_cipher_get_algo_keylen (algo); + if (combiner_type == 2 && !algo) + len = 32; /* We don't have the algo but it must be AES256. */ + else + len = gcry_cipher_get_algo_keylen (algo); encrypted_sessionkey = gcry_mpi_get_opaque (encrypted_sessionkey_mpi, &nbits); encrypted_sessionkey_len = (nbits+7)/8; if (len == 0 || encrypted_sessionkey_len != len + 8) @@ -556,26 +566,33 @@ composite_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, } /* Firstly, ECC part. */ + /* For the rfc-9980 combiner we assume that cv25519 keys are not to + * be reversed. */ ecc_point_len = ecc_ct_len; err = ecc_kem_decap (ctrl, s_skey0, shadow_info0, ecc_ct, ecc_point_len, - ecc_ecdh, ecc_pk, &ecc); + ecc_ecdh, ecc_pk, &ecc, (combiner_type == 2)); if (err) goto leave; - ecc_hashalgo = ecc->hash_algo; - ecc_shared_len = gcry_md_get_algo_dlen (ecc_hashalgo); - err = gnupg_ecc_kem_simple_kdf (ecc_ss, ecc_shared_len, ecc_hashalgo, - ecc_ecdh, ecc_point_len, - ecc_ct, ecc_point_len, - ecc_pk, ecc_point_len); - if (err) + if (combiner_type == 1) /* BSI specified method. */ { - if (opt.verbose) - log_info ("%s: kdf for ECC failed\n", __func__); - goto leave; + ecc_hashalgo = ecc->hash_algo; + ecc_shared_len = gcry_md_get_algo_dlen (ecc_hashalgo); + err = gnupg_ecc_kem_simple_kdf (ecc_ss, ecc_shared_len, ecc_hashalgo, + ecc_ecdh, ecc_point_len, + ecc_ct, ecc_point_len, + ecc_pk, ecc_point_len); + if (err) + { + if (opt.verbose) + log_info ("%s: kdf for ECC failed\n", __func__); + goto leave; + } + wipememory (ecc_ecdh, sizeof ecc_ecdh); + if (DBG_CRYPTO) + log_printhex (ecc_ss, ecc_shared_len, "ECC shared:"); } - wipememory (ecc_ecdh, sizeof ecc_ecdh); - if (DBG_CRYPTO) - log_printhex (ecc_ss, ecc_shared_len, "ECC shared:"); + else + ecc_shared_len = 0; /* ECC_SS is not used. */ /* Secondly, PQC part. For now, we assume ML-KEM. */ err = gcry_sexp_extract_param (s_skey1, NULL, "/s", &mlkem_sk_mpi, NULL); @@ -632,19 +649,30 @@ composite_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, log_info ("%s: gcry_kem_decap for PQ failed\n", __func__); goto leave; } + if (DBG_CRYPTO) + log_printhex (mlkem_ss, mlkem_ss_len, "MLKEM shared:"); mpi_release (mlkem_sk_mpi); mlkem_sk_mpi = NULL; /* Then, combine two shared secrets and ciphertexts into one KEK */ - err = gnupg_kem_combiner (kek, kek_len, - ecc_ss, ecc_shared_len, ecc_ct, ecc_point_len, - mlkem_ss, mlkem_ss_len, mlkem_ct, mlkem_ct_len, - fixed_info.data, fixed_info.size); + if (combiner_type == 1) + err = gnupg_kem_combiner (kek, kek_len, + ecc_ss, ecc_shared_len, ecc_ct, ecc_point_len, + mlkem_ss, mlkem_ss_len, mlkem_ct, mlkem_ct_len, + fixed_info.data, fixed_info.size); + else if (combiner_type == 2) + err = gnupg_kem_combiner_sha3_256 (kek, kek_len, + ecc_ecdh, ecc_point_len, ecc_ct, ecc_point_len, + ecc_pk, ecc_point_len, + mlkem_ss, mlkem_ss_len, + fixed_info.data, fixed_info.size); + else + err = gpg_error (GPG_ERR_INV_PARAMETER); if (err) { if (opt.verbose) - log_info ("%s: KEM combiner failed\n", __func__); + log_info ("KEM combiner %d failed\n", combiner_type); goto leave; } @@ -655,7 +683,7 @@ composite_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, if (DBG_CRYPTO) { - log_printhex (kek, kek_len, "KEK key: "); + log_printhex (kek, kek_len, "KEK key: "); } err = gcry_cipher_open (&hd, GCRY_CIPHER_AES256, @@ -710,16 +738,17 @@ composite_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, /* For ECC PGP/CMS KEM, decrypt CIPHERTEXT using KEM API. CIPHERTEXT should follow the format of: - (enc-val(ecc(c%d)(h%d)(e%m)(s%m)(kdf-params%b))) - c: cipher identifier (of wrapping key) - h: hash identifier - e: ECDH ciphertext - s: encrypted session key + (enc-val(ecc(t%d)(c%d)(h%d)(e%m)(s%m)(kdf-params%b))) + t: Optional kdf variant: default or 1 = generic, 9580 = RFC9580 + c: cipher identifier of wrapping key (algo) + h: hash identifier (hashalgo) + e: ECDH ciphertext (ecc_ct) + s: encrypted session key (encrypted_sessionkey_mpi) kdf-params: A buffer with the KDF parameters. */ static gpg_error_t -ecc_kem_decrypt (int is_pgp, ctrl_t ctrl, const char *desc_text, +ecc_kem_decrypt (int kemid, ctrl_t ctrl, const char *desc_text, gcry_sexp_t s_cipher, membuf_t *outbuf) { gcry_sexp_t s_skey = NULL; @@ -730,9 +759,11 @@ ecc_kem_decrypt (int is_pgp, ctrl_t ctrl, const char *desc_text, int algo; int hashalgo; + int kdfalgo, kdfvariant; gcry_mpi_t encrypted_sessionkey_mpi = NULL; const unsigned char *encrypted_sessionkey; size_t encrypted_sessionkey_len; + int seskeyoff; gcry_mpi_t ecc_ct_mpi = NULL; const unsigned char *ecc_ct; @@ -759,8 +790,8 @@ ecc_kem_decrypt (int is_pgp, ctrl_t ctrl, const char *desc_text, goto leave; } - err = gcry_sexp_extract_param (s_cipher, NULL, "%dc%dh/es&'kdf-params'", - &algo, &hashalgo, &ecc_ct_mpi, + err = gcry_sexp_extract_param (s_cipher, NULL, "%dt?ch/es&'kdf-params'", + &kdfvariant, &algo, &hashalgo, &ecc_ct_mpi, &encrypted_sessionkey_mpi, &kdf_params, NULL); if (err) { @@ -768,15 +799,30 @@ ecc_kem_decrypt (int is_pgp, ctrl_t ctrl, const char *desc_text, log_info ("%s: extracting parameters failed\n", __func__); goto leave; } + if (!kdfvariant) + kdfvariant = 1; if (!kdf_params.data) { if (opt.verbose) - log_info ("%s: the KDF parameters is required\n", __func__); + log_info ("%s: the KDF parameters are required\n", __func__); err = gpg_error (GPG_ERR_INV_DATA); goto leave; } + seskeyoff = 0; + if (kemid == KEM_CMS) + kdfalgo = GCRY_KDF_X963_KDF; + else if (kdfvariant == 9580) + kdfalgo = GCRY_KDF_HKDF; + else + { + seskeyoff = 1; + kdfalgo = GCRY_KDF_ONESTEP_KDF; + } + if (DBG_CRYPTO) + log_debug ("kdfvariant=%d\n", kdfvariant); + ecc_ct = gcry_mpi_get_opaque (ecc_ct_mpi, &nbits); ecc_ct_len = (nbits+7)/8; @@ -797,27 +843,66 @@ ecc_kem_decrypt (int is_pgp, ctrl_t ctrl, const char *desc_text, goto leave; } + /* For the rfc-9580 variant we assume that key is not reversed. */ ecc_point_len = ecc_ct_len; err = ecc_kem_decap (ctrl, s_skey, shadow_info, ecc_ct, ecc_point_len, - ecc_ecdh, ecc_pk, &ecc); + ecc_ecdh, ecc_pk, &ecc, (kdfvariant == 9580)); if (err) goto leave; - err = gnupg_ecc_kem_kdf (kek, kek_len, is_pgp, hashalgo, - ecc->point_len > ecc->scalar_len ? - /* For Weierstrass curve, extract - x-component from the point. */ - ecc_ecdh + 1 : ecc_ecdh, - ecc->scalar_len, - (char *)kdf_params.data+kdf_params.off, - kdf_params.len); + if (kdfvariant == 9580) + { + gcry_kdf_hd_t kdfhd; + unsigned long kdfparam[1]; + unsigned char inputbuf[32+32+32]; + + if (ecc_point_len != 32) + err = gpg_error (GPG_ERR_INV_LENGTH); + else if (kdfalgo != GCRY_KDF_HKDF || hashalgo != GCRY_MAC_HMAC_SHA256) + err = gpg_error (GPG_ERR_INV_MAC); + else + { + kdfparam[0] = kek_len; + memcpy (inputbuf, ecc_ct, 32); + memcpy (inputbuf+32, ecc_pk, 32); + memcpy (inputbuf+64, ecc_ecdh, 32); + /* log_debug ("kdf: kdf_algo=%d hashalgo=%d kdfparam[0]=%lu\n", */ + /* kdfalgo, hashalgo, kdfparam[0]); */ + /* log_printhex (inputbuf, sizeof inputbuf, "kdf: input"); */ + /* log_printhex ((char *)kdf_params.data+kdf_params.off, */ + /* kdf_params.len, "kdf: param"); */ + err = gcry_kdf_open (&kdfhd, kdfalgo, hashalgo, + kdfparam, 1, + inputbuf, sizeof inputbuf, + NULL, 0, NULL, 0, + (char *)kdf_params.data+kdf_params.off, + kdf_params.len); + } + if (!err) + { + err = gcry_kdf_compute (kdfhd, NULL); + if (!err) + err = gcry_kdf_final (kdfhd, kek_len, kek); + gcry_kdf_close (kdfhd); + } + wipememory (inputbuf, sizeof inputbuf); + } + else + err = gnupg_ecc_kem_kdf (kek, kek_len, kdfalgo, hashalgo, + ecc->point_len > ecc->scalar_len ? + /* For Weierstrass curve, extract + x-component from the point. */ + ecc_ecdh + 1 : ecc_ecdh, + ecc->scalar_len, + (char *)kdf_params.data+kdf_params.off, + kdf_params.len); + wipememory (ecc_ecdh, sizeof ecc_ecdh); if (err) { if (opt.verbose) log_info ("%s: kdf for ECC failed\n", __func__); goto leave; } - wipememory (ecc_ecdh, sizeof ecc_ecdh); if (DBG_CRYPTO) { log_printhex (kek, kek_len, "KEK key: "); @@ -832,22 +917,23 @@ ecc_kem_decrypt (int is_pgp, ctrl_t ctrl, const char *desc_text, goto leave; } - if (is_pgp && encrypted_sessionkey[0] != encrypted_sessionkey_len - 1) + if (kemid != KEM_CMS && kdfvariant != 9580 + && encrypted_sessionkey[0] != encrypted_sessionkey_len - 1) { err = gpg_error (GPG_ERR_INV_DATA); goto leave; } err = gcry_cipher_setkey (hd, kek, kek_len); - sessionkey_len = encrypted_sessionkey_len - 8 - !!is_pgp; + sessionkey_len = encrypted_sessionkey_len - 8 - seskeyoff; if (!err) { if (sessionkey_len > sizeof sessionkey) err = gpg_error (GPG_ERR_TOO_LARGE); else err = gcry_cipher_decrypt (hd, sessionkey, sessionkey_len, - encrypted_sessionkey + !!is_pgp, - encrypted_sessionkey_len - !!is_pgp); + encrypted_sessionkey + seskeyoff, + encrypted_sessionkey_len - seskeyoff); } gcry_cipher_close (hd); hd = NULL; @@ -901,7 +987,7 @@ agent_kem_decrypt (ctrl_t ctrl, const char *desc_text, int kemid, } if (kemid == KEM_PGP || kemid == KEM_CMS) - err = ecc_kem_decrypt (kemid == KEM_PGP, ctrl, desc_text, s_cipher, outbuf); + err = ecc_kem_decrypt (kemid, ctrl, desc_text, s_cipher, outbuf); else if (kemid == KEM_PQC_PGP) { if (!ctrl->have_keygrip) diff --git a/common/compliance.c b/common/compliance.c index 8a335070f..69a1b3ae7 100644 --- a/common/compliance.c +++ b/common/compliance.c @@ -90,6 +90,7 @@ get_compliance_cache (enum gnupg_compliance_mode compliance, int for_rng) static int r_pgp7 = -1, s_pgp7 = -1; static int r_pgp8 = -1, s_pgp8 = -1; static int r_de_vs = -1, s_de_vs = -1; + static int r_fips = -1, s_fips = -1; int *ptr = NULL; @@ -101,6 +102,7 @@ get_compliance_cache (enum gnupg_compliance_mode compliance, int for_rng) case CO_PGP7: ptr = for_rng? &r_pgp7 : &s_pgp7 ; break; case CO_PGP8: ptr = for_rng? &r_pgp8 : &s_pgp8 ; break; case CO_DE_VS: ptr = for_rng? &r_de_vs : &s_de_vs ; break; + case CO_FIPS: ptr = for_rng? &r_fips : &s_fips ; break; } if (ptr && compliance == CO_DE_VS) @@ -204,6 +206,8 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, case PUBKEY_ALGO_ECDH: case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: + case PUBKEY_ALGO_ED25519: + case PUBKEY_ALGO_X25519: case GCRY_PK_ECDSA: case GCRY_PK_ECDH: case GCRY_PK_EDDSA: @@ -214,6 +218,8 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, return 0; /* Signing with Elgamal is not at all supported. */ case PUBKEY_ALGO_KYBER: + case PUBKEY_ALGO_MLK768_25519: + case PUBKEY_ALGO_MLK1024_448: algotype = is_kem; break; @@ -293,6 +299,70 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, } xfree (curve); } + else if (compliance == CO_FIPS) + { + char *curve = NULL; + + switch (algotype) + { + case is_elg: + result = 0; + break; + + case is_rsa: + result = ((keylength == 3072 + || keylength == 4096) + && keylength >= min_compliant_rsa_length); + (void)algo_flags; + break; + + case is_ecc: + if (!curvename && key) + { + curve = openpgp_oid_to_str (key[0]); + curvename = openpgp_oid_to_curve (curve, 0); + if (!curvename) + curvename = curve; + } + + result = (curvename + && (algo == PUBKEY_ALGO_ECDH + || algo == PUBKEY_ALGO_ECDSA + || algo == GCRY_PK_ECDH + || algo == GCRY_PK_ECDSA) + && (!strcmp (curvename, "brainpoolP256r1") + || !strcmp (curvename, "brainpoolP384r1") + || !strcmp (curvename, "brainpoolP512r1") + || !strcmp (curvename, "NIST P-256") + || !strcmp (curvename, "NIST P-384") + || !strcmp (curvename, "NIST P-521"))); + break; + + case is_kem: + if (!curvename && key) + { + curve = openpgp_oid_to_str (key[0]); + curvename = openpgp_oid_to_curve (curve, 0); + if (!curvename) + curvename = curve; + } + + result = (curvename + && (keylength == 768 || keylength == 1024) + && (algo == PUBKEY_ALGO_KYBER) + && (!strcmp (curvename, "brainpoolP256r1") + || !strcmp (curvename, "brainpoolP384r1") + || !strcmp (curvename, "brainpoolP512r1") + || !strcmp (curvename, "NIST P-256") + || !strcmp (curvename, "NIST P-384") + || !strcmp (curvename, "NIST P-521"))); + break; + + default: + result = 0; + } + xfree (curve); + } else { result = 1; /* Assume compliance. */ @@ -302,158 +372,331 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, } -/* Return true if ALGO with the given KEYLENGTH is allowed in the - * given COMPLIANCE mode. USE specifies for which use case the - * predicate is evaluated. This way policies can be strict in what - * they produce, and liberal in what they accept. */ -int -gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, - enum pk_use_case use, int algo, - unsigned int algo_flags, gcry_mpi_t key[], - unsigned int keylength, const char *curvename) +/* This is the CO_DE_VS helper for gnupg_pk_is_allowed. */ +static int +gnupg_pk_is_allowed_de_vs (enum pk_use_case use, int algo, + unsigned int algo_flags, gcry_mpi_t key[], + unsigned int keylength, const char *curvename) { int result = 0; - if (! initialized) - return 1; + (void)algo_flags; - /* Map the the generic ECC algo to ECDSA if requested. */ - if ((algo_flags & PK_ALGO_FLAG_ECC18) - && algo == GCRY_PK_ECC - && (use == PK_USE_VERIFICATION - || use == PK_USE_SIGNING)) - algo = GCRY_PK_ECDSA; - - switch (compliance) + switch (algo) { - case CO_DE_VS: - switch (algo) - { - case PUBKEY_ALGO_RSA: - case PUBKEY_ALGO_RSA_E: - case PUBKEY_ALGO_RSA_S: - switch (use) - { - case PK_USE_DECRYPTION: - case PK_USE_VERIFICATION: - result = 1; - break; - case PK_USE_ENCRYPTION: - case PK_USE_SIGNING: - result = ((keylength == 2048 - || keylength == 3072 - || keylength == 4096) - && keylength >= min_compliant_rsa_length); - break; - default: - log_assert (!"reached"); - } - break; + case PUBKEY_ALGO_RSA: + case PUBKEY_ALGO_RSA_E: + case PUBKEY_ALGO_RSA_S: + switch (use) + { + case PK_USE_DECRYPTION: + case PK_USE_VERIFICATION: + result = 1; + break; + case PK_USE_ENCRYPTION: + case PK_USE_SIGNING: + result = ((keylength == 2048 + || keylength == 3072 + || keylength == 4096) + && keylength >= min_compliant_rsa_length); + break; + default: + log_assert (!"reached"); + } + break; - case PUBKEY_ALGO_DSA: - if (use == PK_USE_VERIFICATION) - result = 1; - else if (use == PK_USE_SIGNING && key) - { - size_t P = gcry_mpi_get_nbits (key[0]); - size_t Q = gcry_mpi_get_nbits (key[1]); - result = (Q == 256 - && (P == 2048 || P == 3072) - && keylength >= min_compliant_rsa_length); + case PUBKEY_ALGO_DSA: + if (use == PK_USE_VERIFICATION) + result = 1; + else if (use == PK_USE_SIGNING && key) + { + size_t P = gcry_mpi_get_nbits (key[0]); + size_t Q = gcry_mpi_get_nbits (key[1]); + result = (Q == 256 + && (P == 2048 || P == 3072) + && keylength >= min_compliant_rsa_length); + } + break; + + case PUBKEY_ALGO_ELGAMAL: + case PUBKEY_ALGO_ELGAMAL_E: + result = (use == PK_USE_DECRYPTION); + break; + + case PUBKEY_ALGO_ECDH: /* Same value as GCRY_PK_ECC, i.e. 18 */ + case GCRY_PK_ECDH: + if (use == PK_USE_DECRYPTION) + result = 1; + else if (use == PK_USE_ENCRYPTION) + { + char *curve = NULL; + + if (!curvename && key) + { + curve = openpgp_oid_to_str (key[0]); + curvename = openpgp_oid_to_curve (curve, 0); + if (!curvename) + curvename = curve; } - break; - case PUBKEY_ALGO_ELGAMAL: - case PUBKEY_ALGO_ELGAMAL_E: - result = (use == PK_USE_DECRYPTION); - break; + result = (curvename + && (!strcmp (curvename, "brainpoolP256r1") + || !strcmp (curvename, "brainpoolP384r1") + || !strcmp (curvename, "brainpoolP512r1"))); + + xfree (curve); + } + break; + + case PUBKEY_ALGO_ECDSA: + case GCRY_PK_ECDSA: + if (use == PK_USE_VERIFICATION) + result = 1; + else + { + char *curve = NULL; - case PUBKEY_ALGO_ECDH: /* Same value as GCRY_PK_ECC, i.e. 18 */ - case GCRY_PK_ECDH: - if (use == PK_USE_DECRYPTION) - result = 1; - else if (use == PK_USE_ENCRYPTION) + if (! curvename && key) { - char *curve = NULL; + curve = openpgp_oid_to_str (key[0]); + curvename = openpgp_oid_to_curve (curve, 0); + if (!curvename) + curvename = curve; + } + + result = (use == PK_USE_SIGNING + && curvename + && (!strcmp (curvename, "brainpoolP256r1") + || !strcmp (curvename, "brainpoolP384r1") + || !strcmp (curvename, "brainpoolP512r1"))); + xfree (curve); + } + break; - if (!curvename && key) - { - curve = openpgp_oid_to_str (key[0]); - curvename = openpgp_oid_to_curve (curve, 0); - if (!curvename) - curvename = curve; - } + case PUBKEY_ALGO_EDDSA: + if (use == PK_USE_VERIFICATION) + result = 1; + else /* We may not create such signatures in de-vs mode. */ + result = 0; + break; - result = (curvename - && (!strcmp (curvename, "brainpoolP256r1") - || !strcmp (curvename, "brainpoolP384r1") - || !strcmp (curvename, "brainpoolP512r1"))); + case PUBKEY_ALGO_KYBER: + if (use == PK_USE_DECRYPTION) + result = 1; + else if (use == PK_USE_ENCRYPTION) + { + char *curve = NULL; - xfree (curve); + if (!curvename && key) + { + curve = openpgp_oid_to_str (key[0]); + curvename = openpgp_oid_to_curve (curve, 0); + if (!curvename) + curvename = curve; } + + result = (curvename + && (keylength == 768 || keylength == 1024) + && (!strcmp (curvename, "brainpoolP256r1") + || !strcmp (curvename, "brainpoolP384r1") + || !strcmp (curvename, "brainpoolP512r1"))); + + xfree (curve); + } + break; + + case PUBKEY_ALGO_X25519: + case PUBKEY_ALGO_MLK768_25519: + case PUBKEY_ALGO_MLK1024_448: + if (use == PK_USE_DECRYPTION) + result = 1; /* Only decryption is allowed. */ + break; + + default: + break; + } + + return result; +} + + +/* This is the CO_FIPS helper for gnupg_pk_is_allowed. */ +static int +gnupg_pk_is_allowed_fips (enum pk_use_case use, int algo, + unsigned int algo_flags, gcry_mpi_t key[], + unsigned int keylength, const char *curvename) +{ + int result = 0; + + (void)algo_flags; + + switch (algo) + { + case PUBKEY_ALGO_RSA: + case PUBKEY_ALGO_RSA_E: + case PUBKEY_ALGO_RSA_S: + switch (use) + { + case PK_USE_DECRYPTION: + case PK_USE_VERIFICATION: + result = 1; + break; + case PK_USE_ENCRYPTION: + case PK_USE_SIGNING: + result = ((keylength == 3072 || keylength == 4096) + && keylength >= min_compliant_rsa_length); break; + default: + log_assert (!"reached"); + } + break; - case PUBKEY_ALGO_ECDSA: - case GCRY_PK_ECDSA: - if (use == PK_USE_VERIFICATION) - result = 1; - else - { - char *curve = NULL; + case PUBKEY_ALGO_DSA: + if (use == PK_USE_VERIFICATION) + result = 1; + break; - if (! curvename && key) - { - curve = openpgp_oid_to_str (key[0]); - curvename = openpgp_oid_to_curve (curve, 0); - if (!curvename) - curvename = curve; - } + case PUBKEY_ALGO_ELGAMAL: + case PUBKEY_ALGO_ELGAMAL_E: + result = (use == PK_USE_DECRYPTION); + break; - result = (use == PK_USE_SIGNING - && curvename - && (!strcmp (curvename, "brainpoolP256r1") - || !strcmp (curvename, "brainpoolP384r1") - || !strcmp (curvename, "brainpoolP512r1"))); - xfree (curve); + case PUBKEY_ALGO_ECDH: /* Same value as GCRY_PK_ECC, i.e. 18 */ + case GCRY_PK_ECDH: + if (use == PK_USE_DECRYPTION) + result = 1; + else if (use == PK_USE_ENCRYPTION) + { + char *curve = NULL; + + if (!curvename && key) + { + curve = openpgp_oid_to_str (key[0]); + curvename = openpgp_oid_to_curve (curve, 0); + if (!curvename) + curvename = curve; } - break; + result = (curvename + && (!strcmp (curvename, "brainpoolP256r1") + || !strcmp (curvename, "brainpoolP384r1") + || !strcmp (curvename, "brainpoolP512r1") + || !strcmp (curvename, "NIST P-256") + || !strcmp (curvename, "NIST P-384") + || !strcmp (curvename, "NIST P-521"))); + xfree (curve); + } + break; - case PUBKEY_ALGO_EDDSA: - if (use == PK_USE_VERIFICATION) - result = 1; - else /* We may not create such signatures in de-vs mode. */ - result = 0; - break; + case PUBKEY_ALGO_ECDSA: + case GCRY_PK_ECDSA: + if (use == PK_USE_VERIFICATION) + result = 1; + else + { + char *curve = NULL; - case PUBKEY_ALGO_KYBER: - if (use == PK_USE_DECRYPTION) - result = 1; - else if (use == PK_USE_ENCRYPTION) + if (! curvename && key) { - char *curve = NULL; + curve = openpgp_oid_to_str (key[0]); + curvename = openpgp_oid_to_curve (curve, 0); + if (!curvename) + curvename = curve; + } - if (!curvename && key) - { - curve = openpgp_oid_to_str (key[0]); - curvename = openpgp_oid_to_curve (curve, 0); - if (!curvename) - curvename = curve; - } + result = (use == PK_USE_SIGNING + && curvename + && (!strcmp (curvename, "brainpoolP256r1") + || !strcmp (curvename, "brainpoolP384r1") + || !strcmp (curvename, "brainpoolP512r1") + || !strcmp (curvename, "NIST P-256") + || !strcmp (curvename, "NIST P-384") + || !strcmp (curvename, "NIST P-521"))); + xfree (curve); + } + break; - result = (curvename - && (keylength == 768 || keylength == 1024) - && (!strcmp (curvename, "brainpoolP256r1") - || !strcmp (curvename, "brainpoolP384r1") - || !strcmp (curvename, "brainpoolP512r1"))); + case PUBKEY_ALGO_EDDSA: + if (use == PK_USE_VERIFICATION) + result = 1; + else /* Are we allowed tocreate such signatures - needs to be checked. */ + result = 0; + break; + + case PUBKEY_ALGO_KYBER: + if (use == PK_USE_DECRYPTION) + result = 1; + else if (use == PK_USE_ENCRYPTION) + { + char *curve = NULL; - xfree (curve); + if (!curvename && key) + { + curve = openpgp_oid_to_str (key[0]); + curvename = openpgp_oid_to_curve (curve, 0); + if (!curvename) + curvename = curve; } - break; - default: - break; - } + result = (curvename + && (keylength == 768 || keylength == 1024) + && (!strcmp (curvename, "brainpoolP256r1") + || !strcmp (curvename, "brainpoolP384r1") + || !strcmp (curvename, "brainpoolP512r1") + || !strcmp (curvename, "NIST P-256") + || !strcmp (curvename, "NIST P-384") + || !strcmp (curvename, "NIST P-521"))); + xfree (curve); + } + break; + + case PUBKEY_ALGO_X25519: + case PUBKEY_ALGO_MLK768_25519: + case PUBKEY_ALGO_MLK1024_448: + if (use == PK_USE_DECRYPTION) + result = 1; /* Only decryption is allowed. FIXME: Why */ + break; + + default: + break; + } + + return result; +} + + +/* Return true if ALGO with the given KEYLENGTH is allowed in the + * given COMPLIANCE mode. USE specifies for which use case the + * predicate is evaluated. This way policies can be strict in what + * they produce, and liberal in what they accept. */ +int +gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, + enum pk_use_case use, int algo, + unsigned int algo_flags, gcry_mpi_t key[], + unsigned int keylength, const char *curvename) +{ + int result = 0; + + if (! initialized) + return 1; + + /* Map the the generic ECC algo to ECDSA if requested. */ + if ((algo_flags & PK_ALGO_FLAG_ECC18) + && algo == GCRY_PK_ECC + && (use == PK_USE_VERIFICATION + || use == PK_USE_SIGNING)) + algo = GCRY_PK_ECDSA; + + switch (compliance) + { + case CO_DE_VS: + result = gnupg_pk_is_allowed_de_vs (use, algo, algo_flags, key, + keylength, curvename); + break; + + case CO_FIPS: + result = gnupg_pk_is_allowed_fips (use, algo, algo_flags, key, + keylength, curvename); break; default: @@ -494,7 +737,29 @@ gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance, } log_assert (!"reached"); - default: + default: /* for CO_DE_VS */ + return 0; + } + log_assert (!"reached"); + + case CO_FIPS: + switch (cipher) + { + case CIPHER_ALGO_AES: + case CIPHER_ALGO_AES192: + case CIPHER_ALGO_AES256: + switch (module) + { + case GNUPG_MODULE_NAME_GPG: + return (mode == GCRY_CIPHER_MODE_CFB + || mode == GCRY_CIPHER_MODE_GCM); + case GNUPG_MODULE_NAME_GPGSM: + return (mode == GCRY_CIPHER_MODE_CBC + || mode == GCRY_CIPHER_MODE_GCM); + } + log_assert (!"reached"); + + default: /* for CO_FIPS */ return 0; } log_assert (!"reached"); @@ -553,12 +818,36 @@ gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer, || mode == GCRY_CIPHER_MODE_CFB || mode == GCRY_CIPHER_MODE_OCB) && ! producer); - default: + default: /* for CO_DE_VS */ return 0; } log_assert (!"reached"); - default: + case CO_FIPS: + switch (cipher) + { + case CIPHER_ALGO_AES: + case CIPHER_ALGO_AES192: + case CIPHER_ALGO_AES256: + switch (module) + { + case GNUPG_MODULE_NAME_GPG: + return (mode == GCRY_CIPHER_MODE_NONE + || mode == GCRY_CIPHER_MODE_CFB + || mode == GCRY_CIPHER_MODE_GCM); + case GNUPG_MODULE_NAME_GPGSM: + return (mode == GCRY_CIPHER_MODE_NONE + || mode == GCRY_CIPHER_MODE_CBC + || mode == GCRY_CIPHER_MODE_GCM); + } + log_assert (!"reached"); + + default: /* for CO_FIPS */ + return 0; + } + log_assert (!"reached"); + + default: /* for other compliance modes. */ /* The default policy is to allow all algorithms. */ return 1; } @@ -578,6 +867,7 @@ gnupg_digest_is_compliant (enum gnupg_compliance_mode compliance, switch (compliance) { case CO_DE_VS: + case CO_FIPS: switch (digest) { case DIGEST_ALGO_SHA256: @@ -628,7 +918,19 @@ gnupg_digest_is_allowed (enum gnupg_compliance_mode compliance, int producer, } log_assert (!"reached"); - default: + case CO_FIPS: + switch (digest) + { + case DIGEST_ALGO_SHA256: + case DIGEST_ALGO_SHA384: + case DIGEST_ALGO_SHA512: + return 1; + default: + return 0; + } + log_assert (!"reached"); + + default: /* for other compliance modes. */ /* The default policy is to allow all algorithms. */ return 1; } @@ -652,7 +954,7 @@ gnupg_rng_is_compliant (enum gnupg_compliance_mode compliance) if (result && *result != -1) res = *result; /* Use cached result. */ - else if (compliance == CO_DE_VS) + else if (compliance == CO_DE_VS || compliance == CO_FIPS) { /* We also check whether the library is at all compliant. */ res = gnupg_gcrypt_is_compliant (compliance); @@ -726,6 +1028,13 @@ gnupg_gcrypt_is_compliant (enum gnupg_compliance_mode compliance) else res = 0; /* Non-compliant version of Libgcrypt. */ } + else if (compliance == CO_FIPS) + { + if (gcry_check_version ("1.12.0")) + res = 1; /* Compliant. */ + else + res = 0; /* Non-compliant version of Libgcrypt. */ + } else res = 1; @@ -750,6 +1059,8 @@ gnupg_status_compliance_flag (enum gnupg_compliance_mode compliance) log_assert (!"no status code assigned for this compliance mode"); case CO_DE_VS: return get_assumed_de_vs_compliance ()? "2023" : "23"; + case CO_FIPS: + return "140"; } log_assert (!"invalid compliance mode"); } @@ -816,10 +1127,11 @@ gnupg_manu_notation_value (enum gnupg_compliance_mode compliance) snprintf (buffer2, sizeof buffer2, "2,%.*s+%.*s,%d,%d", vers1len, vers1, vers2len, vers2, arch_id, os_id); snprintf (buffer, sizeof buffer, "%s,%d", - buffer2, get_assumed_de_vs_compliance ()? 2023 : 23); + buffer2, (compliance == CO_FIPS? 140 : + get_assumed_de_vs_compliance ()? 2023 : 23)); } - if (compliance == CO_DE_VS) + if (compliance == CO_DE_VS || compliance == CO_FIPS) return buffer; else return buffer2; @@ -870,6 +1182,7 @@ gnupg_compliance_option_string (enum gnupg_compliance_mode compliance) case CO_PGP7: return "--compliance=pgp7"; case CO_PGP8: return "--compliance=pgp8"; case CO_DE_VS: return "--compliance=de-vs"; + case CO_FIPS: return "--compliance=fips"; } log_assert (!"invalid compliance mode"); diff --git a/common/compliance.h b/common/compliance.h index 1ab39d607..e69d36183 100644 --- a/common/compliance.h +++ b/common/compliance.h @@ -39,7 +39,7 @@ void gnupg_initialize_compliance (int gnupg_module_name); enum gnupg_compliance_mode { CO_GNUPG, CO_RFC4880, CO_RFC2440, - CO_PGP7, CO_PGP8, CO_DE_VS + CO_PGP7, CO_PGP8, CO_DE_VS, CO_FIPS }; enum pk_use_case diff --git a/common/kem.c b/common/kem.c index 2f479aa7a..dcb04d747 100644 --- a/common/kem.c +++ b/common/kem.c @@ -144,12 +144,13 @@ compute_kmac256 (void *digest, size_t digestlen, } -/* Compute KEK for ECC with HASHALGO, ECDH result, and KDF_PARAMS. +/* Compute KEK for ECC with KDF_ALGO, HASHALGO, ECDH result, and + * KDF_PARAMS. * * KDF_PARAMS is specified by upper layer. */ gpg_error_t -gnupg_ecc_kem_kdf (void *kek, size_t kek_len, int is_pgp, +gnupg_ecc_kem_kdf (void *kek, size_t kek_len, int kdf_algo, int hashalgo, const void *ecdh, size_t ecdh_len, const unsigned char *kdf_params, size_t kdf_params_len) { @@ -157,12 +158,6 @@ gnupg_ecc_kem_kdf (void *kek, size_t kek_len, int is_pgp, gpg_error_t err; gcry_kdf_hd_t hd; unsigned long param[1]; - int kdf_algo; - - if (is_pgp) - kdf_algo = GCRY_KDF_ONESTEP_KDF; - else - kdf_algo = GCRY_KDF_X963_KDF; param[0] = kek_len; err = gcry_kdf_open (&hd, kdf_algo, hashalgo, param, 1, @@ -209,6 +204,7 @@ gnupg_ecc_kem_simple_kdf (void *kek, size_t kek_len, return 0; } + /* Compute KEK by combining two KEMs. The caller provides a buffer * KEK allocated with size KEK_LEN which will receive the computed * KEK. (ECC_SS, ECC_SS_LEN) is the shared secret of the first key. @@ -217,8 +213,7 @@ gnupg_ecc_kem_simple_kdf (void *kek, size_t kek_len, * (MLKEM_CT, MLKEM_CT_LEN) is the ciphertext of the second key. * (FIXEDINFO, FIXEDINFO_LEN) is an octet string used to bind the KEK * to a the key; for PGP we use the concatenation of the session key's - * algorithm id and the v5 fingerprint of the key. - */ + * algorithm id and the v5 fingerprint of the key. */ gpg_error_t gnupg_kem_combiner (void *kek, size_t kek_len, const void *ecc_ss, size_t ecc_ss_len, @@ -255,7 +250,53 @@ gnupg_kem_combiner (void *kek, size_t kek_len, KMAC_CUSTOM, strlen (KMAC_CUSTOM), iov, 6); return err; } + + +/* Compute KEK by combining two KEMs; this ise the RFC9980 version + * using SHA3-256. The caller provides a buffer KEK allocated with + * size KEK_LEN which will receive the computed + * KEK. (ECC_SS,ECC_SS_LEN) is the shared secret of the first key. + * (ECC_CT,ECC_CT_LEN) is the ciphertext of the first key. + * (ECC_PK,ECC_PK_LEN) is the public key of the first key. + * (MLKEM_SS,ECC_SS_LEN) is the shared secret of the second key. + * (FIXEDINFO, FIXEDINFO_LEN) is an octet string used to bind the KEK + * to a the key. */ +gpg_error_t +gnupg_kem_combiner_sha3_256 (void *kek, size_t kek_len, + const void *ecc_ss, size_t ecc_ss_len, + const void *ecc_ct, size_t ecc_ct_len, + const void *ecc_pk, size_t ecc_pk_len, + const void *mlkem_ss, size_t mlkem_ss_len, + const void *fixedinfo, size_t fixedinfo_len) +{ + gpg_error_t err; + gcry_buffer_t iov[5]; + + memset (iov, 0, sizeof (iov)); + + iov[0].data = (unsigned char *)mlkem_ss; + iov[0].len = mlkem_ss_len; + + iov[1].data = (unsigned char *)ecc_ss; + iov[1].len = ecc_ss_len; + + iov[2].data = (unsigned char *)ecc_ct; + iov[2].len = ecc_ct_len; + + iov[3].data = (unsigned char *)ecc_pk; + iov[3].len = ecc_pk_len; + + iov[4].data = (unsigned char *)fixedinfo; + iov[4].len = fixedinfo_len; + + err = gcry_md_hash_buffers_ext (GCRY_MD_SHA3_256, 0, + kek, kek_len, iov, 5); + return err; +} + + + #define ECC_CURVE25519_INDEX 0 static const struct gnupg_ecc_params ecc_table[] = { @@ -266,6 +307,12 @@ static const struct gnupg_ecc_params ecc_table[] = 1, 1, 0 }, { + "ietf25", + 32, 32, 32, + GCRY_MD_SHA3_256, GCRY_KEM_RAW_X25519, + 0, 0, 0 + }, + { "X448", 56, 56, 56, GCRY_MD_SHA3_512, GCRY_KEM_RAW_X448, diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index 7c5fbef86..752dbddd7 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -53,6 +53,8 @@ static struct { PUBKEY_ALGO_EDDSA }, { "Curve25519", "1.3.101.110", 255, "cv25519", NULL, PUBKEY_ALGO_ECDH }, + { "ietf25", "1.3.6.1.4.1.11591.15.25",255, NULL, NULL, + PUBKEY_ALGO_X25519 }, { "Ed25519", "1.3.101.112", 255, "ed25519", NULL, PUBKEY_ALGO_EDDSA }, { "X448", "1.3.101.111", 448, "cv448", NULL, diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h index 91dab2910..f3d97b665 100644 --- a/common/openpgpdefs.h +++ b/common/openpgpdefs.h @@ -51,7 +51,7 @@ typedef enum PKT_ATTRIBUTE = 17, /* PGP's attribute packet. */ PKT_ENCRYPTED_MDC = 18, /* Integrity protected encrypted data. */ PKT_MDC = 19, /* Manipulation detection code packet. */ - PKT_ENCRYPTED_AEAD= 20, /* AEAD encrypted data packet. */ + PKT_ENCRYPTED_OCB = 20, /* OCB encrypted data packet. */ PKT_COMMENT = 61, /* new comment packet (GnuPG specific). */ PKT_GPG_CONTROL = 63 /* internal control packet (GnuPG specific). */ } @@ -156,7 +156,8 @@ typedef enum { AEAD_ALGO_NONE = 0, AEAD_ALGO_EAX = 1, - AEAD_ALGO_OCB = 2 + AEAD_ALGO_OCB = 2, + AEAD_ALGO_GCM = 3 /* Urgs - the NSA wants this for FIPS. */ } aead_algo_t; @@ -166,7 +167,8 @@ typedef enum PUBKEY_ALGO_RSA = 1, PUBKEY_ALGO_RSA_E = 2, /* RSA encrypt only (legacy). */ PUBKEY_ALGO_RSA_S = 3, /* RSA sign only (legacy). */ - PUBKEY_ALGO_KYBER = 8, /* Kyber (FIPS-203 final) */ + PUBKEY_ALGO_KYBER = 8, /* Kyber (FIPS-203) */ + PUBKEY_ALGO_DILITHIUM = 9, /* Dilithium (FIPS-204) */ PUBKEY_ALGO_ELGAMAL_E = 16, /* Elgamal encrypt only. */ PUBKEY_ALGO_DSA = 17, PUBKEY_ALGO_ECDH = 18, /* RFC-6637 */ @@ -174,14 +176,35 @@ typedef enum PUBKEY_ALGO_ELGAMAL = 20, /* Elgamal encrypt+sign (legacy). */ /* 21 reserved by OpenPGP. */ PUBKEY_ALGO_EDDSA = 22, /* EdDSA. */ + PUBKEY_ALGO_X25519 = 25, /* X25519 (RFC9580). */ + PUBKEY_ALGO_ED25519 = 27, /* ED25519 (RFC9580). */ /* 29 (was fips203.ipd.2023-08-24 in 1.5.0) */ - PUBKEY_ALGO_DIL3_25519 = 35, /* Dilithium3 + Ed25519 (aka ML-DSA-65) */ - PUBKEY_ALGO_DIL5_448 = 36, /* Dilithium5 + Ed448 (aka ML-DSA-87) */ - PUBKEY_ALGO_SPHINX_SHA2 = 41, /* SPHINX+-simple-SHA2 (aka SLH-DSA-SHA2) */ + PUBKEY_ALGO_MLD65_25519 =30, /* ML-DSA-65+Ed25519 (RFC9980) */ + PUBKEY_ALGO_MLD87_448 =31, /* ML-DSA-87+Ed448 (RFC9980) */ + PUBKEY_ALGO_MLK768_25519 =35, /* ML-KEM-768+X25519 (RFC9980) */ + PUBKEY_ALGO_MLK1024_448 =36, /* ML-KEM-1024+X448 (RFC9980) */ + PUBKEY_ALGO_MLK768_NP384 =37, /* ML-KEM-768+ECDH-NIST-P-384 */ + PUBKEY_ALGO_MLK1024_NP521=38, /* ML-KEM-1024+ECDH-NIST-P-521 */ + PUBKEY_ALGO_MLK768_BP384 =39, /* ML-KEM-768+ECDH-brainpoolP384r1 */ + PUBKEY_ALGO_MLK1024_BP512=40, /* ML-KEM-1024+ECDH-brainpoolP512r1 */ + PUBKEY_ALGO_PRIVATE10 = 110 } pubkey_algo_t; +#define IS_PUBKEY_ALGO_MLK768(a) \ + ((a) == PUBKEY_ALGO_MLK768_25519 \ + || (a) == PUBKEY_ALGO_MLK768_NP384 \ + || (a) == PUBKEY_ALGO_MLK768_BP384) + +#define IS_PUBKEY_ALGO_MLK1024(a) \ + ((a) == PUBKEY_ALGO_MLK1024_448 \ + || (a) == PUBKEY_ALGO_MLK1024_NP521 \ + || (a) == PUBKEY_ALGO_MLK1024_BP512) + +#define IS_PUBKEY_ALGO_MLK(a) \ + (IS_PUBKEY_ALGO_MLK768 ((a)) || IS_PUBKEY_ALGO_MLK1024 ((a))) + typedef enum { diff --git a/common/util.h b/common/util.h index 9627d8e9b..f6055c9d8 100644 --- a/common/util.h +++ b/common/util.h @@ -320,7 +320,7 @@ gpgsm_ecc_kem_kdf (void *kek, size_t kek_len, const unsigned char *wrap, size_t wrap_len, const unsigned char *ukm, size_t ukm_len); -gpg_error_t gnupg_ecc_kem_kdf (void *kek, size_t kek_len, int is_pgp, +gpg_error_t gnupg_ecc_kem_kdf (void *kek, size_t kek_len, int kdf_algo, int hashalgo, const void *ecdh, size_t ecdh_len, const unsigned char *kdf_params, size_t kdf_params_len); @@ -337,6 +337,13 @@ gpg_error_t gnupg_kem_combiner (void *kek, size_t kek_len, const void *mlkem_ct, size_t mlkem_ct_len, const void *fixedinfo, size_t fixedinfo_len); +gpg_error_t gnupg_kem_combiner_sha3_256 (void *kek, size_t kek_len, + const void *ecc_ss, size_t ecc_ss_len, + const void *ecc_ct, size_t ecc_ct_len, + const void *ecc_pk, size_t ecc_pk_len, + const void *mlkem_ss, size_t mlkem_ss_len, + const void *fixedinfo, size_t fixedinfo_len); + /* ECC parameters for KEM encryption/decryption. */ struct gnupg_ecc_params { diff --git a/configure.ac b/configure.ac index 7ad99c5d2..79eea3b41 100644 --- a/configure.ac +++ b/configure.ac @@ -60,7 +60,7 @@ AC_DEFINE_UNQUOTED(GNUPG_SWDB_TAG, "gnupg26", [swdb tag for this branch]) NEED_GPGRT_VERSION=1.56 NEED_LIBGCRYPT_API=1 -NEED_LIBGCRYPT_VERSION=1.11.0 +NEED_LIBGCRYPT_VERSION=1.12.0 NEED_LIBASSUAN_API=3 NEED_LIBASSUAN_VERSION=3.0.0 diff --git a/doc/DETAILS b/doc/DETAILS index 531f46500..7a43418b0 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -261,8 +261,9 @@ described here. Valid values are: - - 8 :: The key is compliant with RFC4880bis - - 23 :: The key is compliant with compliance mode "de-vs". + - 8 :: The key is compliant with RFC4880bis + - 23 :: The key is compliant with compliance mode "de-vs". + - 140 :: The key is compliant with compliance mode "fips". - 2023 :: The key is compliant with a compliance mode "de-vs" but the software has not yet been approved. - 6001 :: Screening hit on the ROCA vulnerability. @@ -1691,7 +1692,9 @@ Status codes are: * Object identifiers - OIDs below the GnuPG arc: +The OID 1.3.6.1.4.1.11591 has been assigned to the GNU project. The +list below shows only the OIDs relevant for GnuPG. You find the +definitive list in the [[http://www.gnu.org/prep/standards/html_node/OID-Allocations.html][GNU Standards document]]. #+begin_example 1.3.6.1.4.1.11591.2 GnuPG @@ -1718,8 +1721,33 @@ Status codes are: 1.3.6.1.4.1.11591.2.6.3 use for encryption key 1.3.6.1.4.1.11591.2.6.4 use for authentication key 1.3.6.1.4.1.11591.2.12242973 invalid encoded OID + 1.3.6.1.4.1.11591.12 DigestAlgorithms + 1.3.6.1.4.1.11591.12.2 TIGER/192 + 1.3.6.1.4.1.11591.13 EncryptionAlgorithms + 1.3.6.1.4.1.11591.13.2 Serpent + 1.3.6.1.4.1.11591.13.2.1 Serpent-128-ECB + 1.3.6.1.4.1.11591.13.2.2 Serpent-128-CBC + 1.3.6.1.4.1.11591.13.2.3 Serpent-128-OFB + 1.3.6.1.4.1.11591.13.2.4 Serpent-128-CFB + 1.3.6.1.4.1.11591.13.2.21 Serpent-192-ECB + 1.3.6.1.4.1.11591.13.2.22 Serpent-192-CBC + 1.3.6.1.4.1.11591.13.2.23 Serpent-192-OFB + 1.3.6.1.4.1.11591.13.2.24 Serpent-192-CFB + 1.3.6.1.4.1.11591.13.2.41 Serpent-256-ECB + 1.3.6.1.4.1.11591.13.2.42 Serpent-256-CBC + 1.3.6.1.4.1.11591.13.2.43 Serpent-256-OFB + 1.3.6.1.4.1.11591.13.2.44 Serpent-256-CFB + 1.3.6.1.4.1.11591.14 CRC Algorithms + 1.3.6.1.4.1.11591.14.1 CRC 32 + 1.3.6.1.4.1.11591.15 EllipticCurves + 1.3.6.1.4.1.11591.15.1 Ed25519 + 1.3.6.1.4.1.11591.15.25 ietf25 #+end_example +The curve ietf25 is actually X25519 as specified by RFC-7748. We used +the name X25519 already as an alias for Curve25519 in Libgcrypt but +with slighly different semantics. + The OpenPGP KDF/KEK parameter extension is used to convey additional info for OpenPGP keys as an X.509 extensions. @@ -1822,13 +1850,13 @@ Description of some debug flags: a key or signature. This is a list of comma delimited values with these defined fields: - | field | name | defined values | - |-------+------------------+------------------------| - | 1 | software product | see: prod-id | - | 2 | software version | e.g. "2.2", "2.5+1.12" | - | 3 | architecture | see: arch-id | - | 4 | operating system | see: os-id | - | 5 | compliance class | e.g. "23", "2023" | + | field | name | defined values | + |-------+------------------+--------------------------| + | 1 | software product | see: prod-id | + | 2 | software version | e.g. "2.2", "2.5+1.12" | + | 3 | architecture | see: arch-id | + | 4 | operating system | see: os-id | + | 5 | compliance class | e.g. "23", "2023", "140" | | prod-id | name | |---------+-------------| @@ -1853,7 +1881,8 @@ Description of some debug flags: If a value for a field is not known, the empty string may be used. The values are also used for the X.509/CMS extension 1.3.6.1.4.1.11591.2.1.2. The compliance class - values are 23 for "de-vs" and 2023 for non-approved "de-vs". + values are 23 for "de-vs" and 2023 for non-approved + "de-vs" or 140 for FIPS compliance. This notation shall be human readable. It is defined in away to minimize its size but to be easily viewable by diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index 96242048c..812ffbe0b 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -290,6 +290,12 @@ This option enables extra debug information pertaining to the Pinentry. As of now it is only useful when used along with @code{--debug 1024}. +@item --debug-no-libgcrypt +@opindex debug-no-libgcrypt +Do no use the debug flags "crypto" and "mpi" to enable similar debug +features in Libgcrypt. Note well: This is a maintainer only option +and may thus be changed or removed at any time without notice. + @item --no-detach @opindex no-detach Don't detach the process from the console. This is mainly useful for diff --git a/doc/gpg.texi b/doc/gpg.texi index 3b10273d4..8a311b1a0 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -3352,6 +3352,12 @@ Change the buffer size of the IOBUFs to @var{n} kilobyte. Using 0 prints the current size. Note well: This is a maintainer only option and may thus be changed or removed at any time without notice. +@item --debug-no-libgcrypt +@opindex debug-no-libgcrypt +Do no use the debug flags "crypto" and "mpi" to enable similar debug +features in Libgcrypt. Note well: This is a maintainer only option +and may thus be changed or removed at any time without notice. + @item --debug-allow-large-chunks @opindex debug-allow-large-chunks To facilitate software tests and experiments this option allows one to @@ -3981,6 +3987,17 @@ This is an obsolete option and is not used anywhere. @item --no-allow-multiple-messages These are obsolete options; they have no more effect since GnuPG 2.2.8. + +@item --allow-9980 +@opindex allow-9980 +Enabling this option allows the processing of some data formats +specified by RFC-9980 (PQC for OpenPGP) and its dependencies. It is +implictly enabled when using the compliance modes ``fips'' or +``de-vs''. Due the bloated specification the use of this option is +not suggested. The standard GnuPG mode is more oriented at the +classic PGP and thus LibrePGP specification which also provide PQC +support but in a more lean and PGP-style way. + @item --enable-special-filenames @opindex enable-special-filenames This option enables a mode in which filenames of the form diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index 7f16cc0fa..42d815e0a 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -893,6 +893,12 @@ This is actually not a debugging option but only useful as such. It lets @command{gpgsm} ignore all notAfter dates, this is used by the regression tests. +@item --debug-no-libgcrypt +@opindex debug-no-libgcrypt +Do no use the debug flags "crypto" and "mpi" to enable similar debug +features in Libgcrypt. Note well: This is a maintainer only option +and may thus be changed or removed at any time without notice. + @item --passphrase-fd @code{n} @opindex passphrase-fd Read the passphrase from file descriptor @code{n}. Only the first line diff --git a/doc/keyformat.txt b/doc/keyformat.txt index 912e65dd9..755453e36 100644 --- a/doc/keyformat.txt +++ b/doc/keyformat.txt @@ -384,7 +384,17 @@ More items may be added to the list. ** OpenPGP Private Key Transfer Format -This format is used to transfer keys between gpg and gpg-agent. +This format is used to transfer keys between gpg and gpg-agent. It is +required so that gpg does not need to decrypt the secret key parms. +Instead gpg-agent decrypts and re-encrypts them. This is not ideal +becuase it moves protocol knowledge into gpg-agent (cvt-openpgp.c). A +better solution is to have a a separate helper tool to do the +conversion and re-encryption which can then be called by gpg-agent. +The use of s-expression here is also too complicated. + +With version 2.5.2x we dropped the need for this format iff no +protection was used. + (openpgp-private-key (version V) diff --git a/g10/armor.c b/g10/armor.c index 0513dbc97..5e5f9b727 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -255,7 +255,7 @@ is_armored (const byte *buf) case PKT_COMPRESSED: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: - case PKT_ENCRYPTED_AEAD: + case PKT_ENCRYPTED_OCB: case PKT_PLAINTEXT: case PKT_OLD_COMMENT: case PKT_COMMENT: diff --git a/g10/build-packet.c b/g10/build-packet.c index 0bcb051a2..7ebed8131 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -38,11 +38,12 @@ static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid ); static int do_key (iobuf_t out, int ctb, PKT_public_key *pk); static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ); static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ); +static int do_pubkey_enc_v6 (iobuf_t out, int ctb, PKT_pubkey_enc *enc); static u32 calc_plaintext( PKT_plaintext *pt ); static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ); static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ); -static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ); -static int do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed); +static int do_encrypted_mdc (iobuf_t out, int ctb, PKT_encrypted *ed ); +static int do_encrypted_ocb (iobuf_t out, int ctb, PKT_encrypted *ed); static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd ); static int do_signature( IOBUF out, int ctb, PKT_signature *sig ); static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ); @@ -150,7 +151,7 @@ build_packet (IOBUF out, PACKET *pkt) break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: - case PKT_ENCRYPTED_AEAD: + case PKT_ENCRYPTED_OCB: new_ctb = pkt->pkt.encrypted->new_ctb; break; case PKT_COMPRESSED: @@ -203,8 +204,8 @@ build_packet (IOBUF out, PACKET *pkt) case PKT_ENCRYPTED_MDC: rc = do_encrypted_mdc (out, ctb, pkt->pkt.encrypted); break; - case PKT_ENCRYPTED_AEAD: - rc = do_encrypted_aead (out, ctb, pkt->pkt.encrypted); + case PKT_ENCRYPTED_OCB: + rc = do_encrypted_ocb (out, ctb, pkt->pkt.encrypted); break; case PKT_COMPRESSED: rc = do_compressed (out, ctb, pkt->pkt.compressed); @@ -452,6 +453,52 @@ gpg_mpi_write_opaque_nohdr (iobuf_t out, gcry_mpi_t a) } +/* Write an opaque MPI string with a one-byte octet count to the + * output stream. If R_NWRITTEN is not NULL the number of written + * bytes is stored there. OUT may be NULL in which case only + * R_NWRITTEN is updated and error checking is done. */ +gpg_error_t +gpg_mpi_write_opaque_8 (iobuf_t out, gcry_mpi_t a, unsigned int *r_nwritten) +{ + gpg_error_t err; + + if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + { + unsigned int nbits, nbytes; + const void *p; + + p = gcry_mpi_get_opaque (a, &nbits); + nbytes = (nbits + 7)/8; + if (nbytes > 255) + { + err = gpg_error (GPG_ERR_TOO_LARGE); + if (r_nwritten) + *r_nwritten = 0; + } + else + { + if (out) + { + iobuf_put (out, nbytes); + err = p ? iobuf_write (out, p, nbytes) : 0; + } + else + err = 0; + if (r_nwritten) + *r_nwritten = 4 + (p? nbytes : 0); + } + } + else + { + err = gpg_error (GPG_ERR_BAD_MPI); + if (r_nwritten) + *r_nwritten = 0; + } + + return err; +} + + /* * Write an opaque MPI string with a four-byte octet count to the * output stream. If R_NWRITTEN is not NULL the number of written @@ -640,7 +687,8 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) u32 pkbytes = 0; int is_v5; - log_assert (pk->version == 0 || pk->version == 4 || pk->version == 5); + log_assert (pk->version == 0 || pk->version == 4 || pk->version == 5 + || pk->version == 6); log_assert (ctb_pkttype (ctb) == PKT_PUBLIC_KEY || ctb_pkttype (ctb) == PKT_PUBLIC_SUBKEY || ctb_pkttype (ctb) == PKT_SECRET_KEY @@ -657,7 +705,7 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) /* Note that the Version number, Timestamp, Algo, and the v5 Key * material count are written at the end of the function. */ - is_v5 = (pk->version == 5); + is_v5 = (pk->version >= 5); /* Get number of secret and public parameters. They are held in one array: the public ones followed by the secret ones. */ @@ -686,6 +734,12 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) /* Write a four-octet count prefixed Kyber public key. */ err = gpg_mpi_write_opaque_32 (a, pk->pkey[2], NULL); } + else if (IS_PUBKEY_ALGO_MLK (pk->pubkey_algo) + || pk->pubkey_algo == PUBKEY_ALGO_ED25519 + || pk->pubkey_algo == PUBKEY_ALGO_X25519) + { + err = gpg_mpi_write_opaque_nohdr (a, pk->pkey[i]); + } else if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH) @@ -887,7 +941,10 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) if (is_v5) len += 4; /* public key material count */ - write_header2 (out, ctb, len, 0); + if (pk->version == 6) + write_new_header (out, (0xc0|(ctb_pkttype(ctb)&0x3f)), len, 0); + else + write_header2 (out, ctb, len, 0); /* And finally write it out to the real stream. */ iobuf_put (out, pk->version? pk->version : 4); /* version number */ write_32 (out, pk->timestamp ); @@ -955,13 +1012,17 @@ do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) CTB is the serialization's CTB. It specifies the header format and the packet's type. The header length must not be set. */ static int -do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) +do_pubkey_enc (iobuf_t out, int ctb, PKT_pubkey_enc *enc) { int rc = 0; int n, i; - IOBUF a = iobuf_temp(); + iobuf_t a; log_assert (ctb_pkttype (ctb) == PKT_PUBKEY_ENC); + if (enc->version == 6) + return do_pubkey_enc_v6 (out, ctb, enc); + + a = iobuf_temp(); iobuf_put (a, 3); /* Version. */ @@ -1010,6 +1071,61 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) } +static int +do_pubkey_enc_v6 (iobuf_t out, int ctb, PKT_pubkey_enc *enc) +{ + int rc = 0; + iobuf_t a; + + a = iobuf_temp(); + iobuf_put (a, 6); /* The version of this packet. */ + + if (enc->throw_keyid) + iobuf_put (a, 0); /* No fingerprint given. */ + else + { + /* Note that we do not support the regular v5 keys here. v5 + * keys shall keep using the version 3 pubkey_enc packets + * because the v6 format is missing the session key algo and + * conveys the full fingerprint. */ + iobuf_put (a, (enc->fprlen == 32? 32 : 20) + 1); + iobuf_put (a, enc->fprlen == 32? 6 : 4); /* The _key_ version. */ + iobuf_write (a, enc->fpr, sizeof enc->fpr); + } + + iobuf_put (a, enc->pubkey_algo ); + + if (IS_PUBKEY_ALGO_MLK (enc->pubkey_algo)) + { + rc = gpg_mpi_write_opaque_nohdr (a, enc->data[0]); + if (!rc) + rc = gpg_mpi_write_opaque_nohdr (a, enc->data[1]); + if (!rc) + { + iobuf_put (a, 40); /* size octet. */ + rc = gpg_mpi_write_opaque_nohdr (a, enc->data[2]); + } + } + else if (enc->pubkey_algo == PUBKEY_ALGO_X25519) + { + rc = gpg_mpi_write_opaque_nohdr (a, enc->data[0]); + if (!rc) + rc = gpg_mpi_write_opaque_8 (a, enc->data[1], NULL); + } + else + rc = gpg_error (GPG_ERR_INV_PACKET); /* for this algo. */ + + if (!rc) + { + write_new_header (out, (0xc0|(ctb_pkttype(ctb)&0x3f)), + iobuf_get_temp_length(a), 0); + rc = iobuf_write_temp (out, a); + } + iobuf_close (a); + return rc; +} + + /* Calculate the length of the serialized plaintext packet PT (RFC 4480, Section 5.9). */ static u32 @@ -1125,22 +1241,33 @@ do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ) packet to OUT. (If you use the encryption iobuf filter (cipher_filter), then this is done automatically.) */ static int -do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ) +do_encrypted_mdc (iobuf_t out, int ctb, PKT_encrypted *ed) { - int rc = 0; - u32 n; - - log_assert (ed->mdc_method); - log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED_MDC); + u32 n; - /* Take version number and the following MDC packet in account. */ - n = ed->len ? (ed->len + ed->extralen + 1 + 22) : 0; - write_header(out, ctb, n ); - iobuf_put(out, 1 ); /* version */ + log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED_MDC); - /* This is all. The caller has to write the real data */ + if (ed->version == 2) + { + /* Take this header (4) in account. */ + n = ed->len ? (ed->len + ed->extralen + 4) : 0; + write_new_header (out, (0xc0|(ctb_pkttype(ctb)&0x3f)), n, 0); + iobuf_writebyte (out, 2); + iobuf_writebyte (out, ed->cipher_algo); + iobuf_writebyte (out, ed->aead_algo); + iobuf_writebyte (out, ed->chunkbyte); + } + else + { + log_assert (ed->mdc_method); + /* Take version number and the final MDC packet in account. */ + n = ed->len ? (ed->len + ed->extralen + 1 + 22) : 0; + write_header (out, ctb, n); + iobuf_put (out, 1); /* version */ + } - return rc; + /* This is all. The caller has to write the real data */ + return 0; } @@ -1151,11 +1278,11 @@ do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ) * follow up and write the actual encrypted data. This should be done * by pushing the the cipher_filter_aead. */ static int -do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed) +do_encrypted_ocb (iobuf_t out, int ctb, PKT_encrypted *ed) { u32 n; - log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED_AEAD); + log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED_OCB); n = ed->len ? (ed->len + ed->extralen + 4) : 0; write_header (out, ctb, n ); @@ -1987,36 +2114,67 @@ do_signature( IOBUF out, int ctb, PKT_signature *sig ) prior to the call of this function, because these subpackets are hashed. */ nn = sig->hashed? sig->hashed->len : 0; - write_16(a, nn); + if (sig->version >= 6) + write_32(a, nn); + else + write_16(a, nn); if (nn) iobuf_write( a, sig->hashed->data, nn ); nn = sig->unhashed? sig->unhashed->len : 0; - write_16(a, nn); + if (sig->version >= 6) + write_32(a, nn); + else + write_16(a, nn); if (nn) iobuf_write( a, sig->unhashed->data, nn ); } iobuf_put(a, sig->digest_start[0] ); iobuf_put(a, sig->digest_start[1] ); + + if (sig->version == 6) + { + if (sig->salt) + { + rc = gpg_mpi_write_opaque_8 (a, sig->salt, NULL); + if (rc) + goto leave; + } + else + iobuf_put (a, 0); + } + n = pubkey_get_nsig( sig->pubkey_algo ); if ( !n ) write_fake_data( a, sig->data[0] ); if (sig->pubkey_algo == PUBKEY_ALGO_ECDSA || sig->pubkey_algo == PUBKEY_ALGO_EDDSA) - for (i=0; i < n && !rc ; i++ ) - rc = sos_write (a, sig->data[i], NULL); + { + for (i=0; i < n && !rc ; i++ ) + rc = sos_write (a, sig->data[i], NULL); + } + else if (sig->pubkey_algo == PUBKEY_ALGO_ED25519) + { + rc = gpg_mpi_write_opaque_nohdr (a, sig->data[0]); + } else - for (i=0; i < n && !rc ; i++ ) - rc = gpg_mpi_write (a, sig->data[i], NULL); + { + for (i=0; i < n && !rc ; i++ ) + rc = gpg_mpi_write (a, sig->data[i], NULL); + } if (!rc) { if ( is_RSA(sig->pubkey_algo) && sig->version < 4 ) write_sign_packet_header(out, ctb, iobuf_get_temp_length(a) ); + else if (sig->version == 6) + write_new_header (out, (0xc0|(ctb_pkttype(ctb)&0x3f)), + iobuf_get_temp_length (a), 0); else write_header(out, ctb, iobuf_get_temp_length(a) ); rc = iobuf_write_temp( out, a ); } + leave: iobuf_close(a); return rc; } diff --git a/g10/call-agent.c b/g10/call-agent.c index 8949fad17..9b97e1778 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -2936,7 +2936,8 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, if (*keygrip2) cmdline = "PKDECRYPT --kem=PQC-PGP"; - else if (pubkey_algo == PUBKEY_ALGO_ECDH) + else if (pubkey_algo == PUBKEY_ALGO_ECDH + || pubkey_algo == PUBKEY_ALGO_X25519) cmdline = "PKDECRYPT --kem=PGP"; else cmdline = "PKDECRYPT"; diff --git a/g10/cipher-aead.c b/g10/cipher-aead.c index 0c07e65de..de7088ac4 100644 --- a/g10/cipher-aead.c +++ b/g10/cipher-aead.c @@ -65,6 +65,7 @@ set_nonce_and_ad (cipher_filter_context_t *cfx, int final) gpg_error_t err; unsigned char nonce[16]; unsigned char ad[21]; + int adlen; int i; switch (cfx->dek->use_aead) @@ -79,6 +80,11 @@ set_nonce_and_ad (cipher_filter_context_t *cfx, int final) i = 8; break; + case AEAD_ALGO_GCM: + memcpy (nonce, cfx->startiv, 12); + i = 4; + break; + default: BUG (); } @@ -93,13 +99,13 @@ set_nonce_and_ad (cipher_filter_context_t *cfx, int final) nonce[i++] ^= cfx->chunkindex; if (DBG_CRYPTO) - log_printhex (nonce, 15, "nonce:"); + log_printhex (nonce, i, "nonce:"); err = gcry_cipher_setiv (cfx->cipher_hd, nonce, i); if (err) return err; - ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD); - ad[1] = 1; + ad[0] = (0xc0 | (cfx->seipdv2? PKT_ENCRYPTED_MDC : PKT_ENCRYPTED_OCB)); + ad[1] = cfx->seipdv2? 2 : 1; ad[2] = cfx->dek->algo; ad[3] = cfx->dek->use_aead; ad[4] = cfx->chunkbyte; @@ -122,9 +128,22 @@ set_nonce_and_ad (cipher_filter_context_t *cfx, int final) ad[19] = cfx->total >> 8; ad[20] = cfx->total; } + + if (cfx->seipdv2) + { + adlen = 5; + if (final) + { + adlen += 8; + memmove (ad+5, ad+13, 8); + } + } + else + adlen = final? 21 : 13; + if (DBG_CRYPTO) - log_printhex (ad, final? 21 : 13, "authdata:"); - return gcry_cipher_authenticate (cfx->cipher_hd, ad, final? 21 : 13); + log_printhex (ad, adlen, "authdata:"); + return gcry_cipher_authenticate (cfx->cipher_hd, ad, adlen); } @@ -137,6 +156,8 @@ write_header (cipher_filter_context_t *cfx, iobuf_t a) unsigned int blocksize; unsigned int startivlen; enum gcry_cipher_modes ciphermode; + unsigned char *thekeybuffer = NULL; /* Used by seipdv2 */ + size_t thekeybufferlen; /* Malloced length of thekeybuffer. */ log_assert (cfx->dek->use_aead); @@ -158,49 +179,132 @@ write_header (cipher_filter_context_t *cfx, iobuf_t a) if (!cfx->buffer) return gpg_error_from_syserror (); + init_packet (&pkt); memset (&ed, 0, sizeof ed); ed.new_ctb = 1; /* (Is anyway required for the packet type). */ ed.len = 0; /* fixme: cfx->datalen */ - ed.extralen = startivlen + 16; /* (16 is the taglen) */ ed.cipher_algo = cfx->dek->algo; ed.aead_algo = cfx->dek->use_aead; ed.chunkbyte = cfx->chunkbyte; - - init_packet (&pkt); - pkt.pkttype = PKT_ENCRYPTED_AEAD; + ed.extralen = startivlen + 16; /* (16 is the taglen) */ + if (cfx->seipdv2) + { + pkt.pkttype = PKT_ENCRYPTED_MDC; + ed.version = 2; + } + else + { + pkt.pkttype = PKT_ENCRYPTED_OCB; + ed.version = 1; + } pkt.pkt.encrypted = &ed; if (DBG_FILTER) - log_debug ("aead packet: len=%lu extralen=%d\n", - (unsigned long)ed.len, ed.extralen); + log_debug ("aead packet: len=%lu extralen=%d version=%d\n", + (unsigned long)ed.len, ed.extralen, ed.version); print_cipher_algo_note (cfx->dek->algo); if (build_packet( a, &pkt)) - log_bug ("build_packet(ENCRYPTED_AEAD) failed\n"); + log_bug ("build_packet(%s) failed\n", + cfx->seipdv2? "ENCRYPTED_MDCv2":"ENCRYPTED_OCB"); - log_assert (sizeof cfx->startiv >= startivlen); - gcry_randomize (cfx->startiv, startivlen, GCRY_STRONG_RANDOM); - err = my_iobuf_write (a, cfx->startiv, startivlen); - if (err) - goto leave; + log_assert (startivlen <= sizeof cfx->startiv && startivlen >= 8); + if (cfx->seipdv2) + { + gcry_kdf_hd_t kdfhd; + unsigned long kdfparam[1]; + unsigned char kdfinfo[5]; + unsigned char salt[32]; - err = openpgp_cipher_open (&cfx->cipher_hd, - cfx->dek->algo, - ciphermode, - GCRY_CIPHER_SECURE); - if (err) - goto leave; + /* Expand key to M + N - 8. */ + log_assert (cfx->dek->keylen >= 16); + kdfparam[0] = cfx->dek->keylen + startivlen - 8; - if (DBG_CRYPTO) - log_printhex (cfx->dek->key, cfx->dek->keylen, "thekey:"); - err = gcry_cipher_setkey (cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen); + kdfinfo[0] = 0xd2; + kdfinfo[1] = 2; + kdfinfo[2] = ed.cipher_algo; + kdfinfo[3] = ed.aead_algo; + kdfinfo[4] = ed.chunkbyte; + + gcry_randomize (salt, sizeof salt, GCRY_STRONG_RANDOM); + err = my_iobuf_write (a, salt, sizeof salt); + if (err) + goto leave; + + if (DBG_CRYPTO) + { + log_printhex (salt, sizeof salt, "salt:"); + log_printhex (kdfinfo, sizeof kdfinfo, "info:"); + } + err = gcry_kdf_open (&kdfhd, GCRY_KDF_HKDF, GCRY_MAC_HMAC_SHA256, + kdfparam, 1, + cfx->dek->key, cfx->dek->keylen, + NULL, 0, + salt, sizeof salt, + kdfinfo, sizeof kdfinfo); + if (!err) + { + err = gcry_kdf_compute (kdfhd, NULL); + if (!err) + { + /* Allocate M + N */ + thekeybufferlen = cfx->dek->keylen + startivlen; + thekeybuffer = xtrymalloc_secure (thekeybufferlen); + if (!thekeybuffer) + err = gpg_error_from_syserror (); + else /* Get M + N - 8 */ + err = gcry_kdf_final (kdfhd, kdfparam[0], thekeybuffer); + } + gcry_kdf_close (kdfhd); + } + if (err) + { + log_error ("HKDF for session key failed: %s\n", gpg_strerror (err)); + goto leave; + } + memcpy (cfx->startiv, thekeybuffer + cfx->dek->keylen, startivlen - 8); + memset (cfx->startiv + startivlen - 8, 0, 8); + + err = openpgp_cipher_open (&cfx->cipher_hd, cfx->dek->algo, + ciphermode, GCRY_CIPHER_SECURE); + if (err) + goto leave; + + if (DBG_CRYPTO) + log_printhex (thekeybuffer, cfx->dek->keylen, "thekey:"); + err = gcry_cipher_setkey (cfx->cipher_hd, + thekeybuffer, cfx->dek->keylen); + } + else + { + log_assert (sizeof cfx->startiv >= startivlen); + gcry_randomize (cfx->startiv, startivlen, GCRY_STRONG_RANDOM); + err = my_iobuf_write (a, cfx->startiv, startivlen); + if (err) + goto leave; + + err = openpgp_cipher_open (&cfx->cipher_hd, cfx->dek->algo, + ciphermode, GCRY_CIPHER_SECURE); + if (err) + goto leave; + + if (DBG_CRYPTO) + log_printhex (cfx->dek->key, cfx->dek->keylen, "thekey:"); + err = gcry_cipher_setkey (cfx->cipher_hd, + cfx->dek->key, cfx->dek->keylen); + } if (err) - return err; + goto leave; cfx->wrote_header = 1; leave: + if (thekeybuffer) + { + wipememory (thekeybuffer, thekeybufferlen); + xfree (thekeybuffer); + } return err; } diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c index 55e078e56..df56fbc1a 100644 --- a/g10/decrypt-data.c +++ b/g10/decrypt-data.c @@ -54,7 +54,7 @@ struct decode_filter_context_s /* The hash handle for use in MDC mode. */ gcry_md_hd_t mdc_hash; - /* The start IV for AEAD encryption. */ + /* The start IV for AEAD encryption. For OCB this is the nonce. */ byte startiv[16]; /* The holdback buffer and its used length. For AEAD we need 32+1 @@ -75,6 +75,9 @@ struct decode_filter_context_s /* Flag to convey an error from aead_checktag. */ unsigned int checktag_failed : 1; + /* Set if the RFC-9850 SEIPD version 2 packet is used. */ + unsigned int seipdv2 : 1; + /* The actually used cipher algo for AEAD. */ byte cipher_algo; @@ -132,6 +135,7 @@ aead_set_nonce_and_ad (decode_filter_ctx_t dfx, int final) gpg_error_t err; unsigned char ad[21]; unsigned char nonce[16]; + unsigned int adlen; int i; switch (dfx->aead_algo) @@ -146,6 +150,11 @@ aead_set_nonce_and_ad (decode_filter_ctx_t dfx, int final) i = 8; break; + case AEAD_ALGO_GCM: + memcpy (nonce, dfx->startiv, 12); + i = 4; + break; + default: BUG (); } @@ -164,8 +173,8 @@ aead_set_nonce_and_ad (decode_filter_ctx_t dfx, int final) if (err) return err; - ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD); - ad[1] = 1; + ad[0] = (0xc0 | (dfx->seipdv2? PKT_ENCRYPTED_MDC:PKT_ENCRYPTED_OCB)); + ad[1] = dfx->seipdv2? 2:1; ad[2] = dfx->cipher_algo; ad[3] = dfx->aead_algo; ad[4] = dfx->chunkbyte; @@ -188,9 +197,22 @@ aead_set_nonce_and_ad (decode_filter_ctx_t dfx, int final) ad[19] = dfx->total >> 8; ad[20] = dfx->total; } + + if (dfx->seipdv2) + { + adlen = 5; + if (final) + { + adlen += 8; + memmove (ad+5, ad+13, 8); + } + } + else + adlen = final? 21 : 13; + if (DBG_CRYPTO) - log_printhex (ad, final? 21 : 13, "authdata:"); - return gcry_cipher_authenticate (dfx->cipher_hd, ad, final? 21 : 13); + log_printhex (ad, adlen, "authdata:"); + return gcry_cipher_authenticate (dfx->cipher_hd, ad, adlen); } @@ -201,19 +223,19 @@ aead_checktag (decode_filter_ctx_t dfx, int final, const void *tagbuf) { gpg_error_t err; - if (DBG_FILTER) - log_printhex (tagbuf, 16, "tag:"); err = gcry_cipher_checktag (dfx->cipher_hd, tagbuf, 16); if (err) { + if (DBG_CRYPTO) + log_printhex (tagbuf, 16, "invalid %stag:", final?"final ":""); log_error ("gcry_cipher_checktag%s failed: %s\n", final? " (final)":"", gpg_strerror (err)); write_status_error ("aead_checktag", err); dfx->checktag_failed = 1; return err; } - if (DBG_FILTER) - log_debug ("%stag is valid\n", final?"final ":""); + if (DBG_CRYPTO) + log_printhex (tagbuf, 16, "valid %stag:", final?"final ":""); return 0; } @@ -236,6 +258,11 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek, byte temp[32]; unsigned int blocksize; unsigned int nprefix; + unsigned char *thekeybuffer = NULL; /* Used by seipdv2 */ + size_t thekeybufferlen; /* Malloced length of thekeybuffer. */ + const void *thekey; /* Points either to thekeybuffer or dek->key. */ + size_t thekeylen; /* Same as dek->keylen */ + *compliance_error = 0; @@ -243,9 +270,12 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek, if (!dfx) return gpg_error_from_syserror (); dfx->refcount = 1; + dfx->seipdv2 = (ed->seipd && ed->version == 2); if ( opt.verbose && !dek->algo_info_printed ) { + if (dfx->seipdv2) + log_info (_("Note: Using the %s encryption packet\n"), "RFC-9580"); if (!openpgp_cipher_test_algo (dek->algo)) log_info (_("%s encrypted data\n"), openpgp_cipher_algo_mode_name (dek->algo, ed->aead_algo)); @@ -345,32 +375,6 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek, goto leave; } - /* Read the Start-IV. */ - if (ed->len) - { - for (i=0; i < startivlen && ed->len; i++, ed->len--) - { - if ((c=iobuf_get (ed->buf)) == -1) - break; - dfx->startiv[i] = c; - } - } - else - { - for (i=0; i < startivlen; i++ ) - if ( (c=iobuf_get (ed->buf)) == -1 ) - break; - else - dfx->startiv[i] = c; - } - if (i != startivlen) - { - log_error ("Start-IV in AEAD packet too short (%d/%u)\n", - i, startivlen); - rc = gpg_error (GPG_ERR_TOO_SHORT); - goto leave; - } - dfx->cipher_algo = ed->cipher_algo; dfx->aead_algo = ed->aead_algo; dfx->chunkbyte = ed->chunkbyte; @@ -381,6 +385,115 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek, openpgp_cipher_algo_name (dek->algo), openpgp_cipher_algo_name (dfx->cipher_algo)); + if (dfx->seipdv2) /* RFC-9580 mode */ + { + gcry_kdf_hd_t kdfhd; + unsigned long kdfparam[1]; + unsigned char kdfinfo[5]; + unsigned char salt[32]; + + /* Read the salt. */ + if (ed->len) + { + for (i=0; i < sizeof salt && ed->len; i++, ed->len--) + { + if ((c=iobuf_get (ed->buf)) == -1) + break; + salt[i] = c; + } + } + else + { + for (i=0; i < sizeof salt; i++ ) + if ( (c=iobuf_get (ed->buf)) == -1 ) + break; + else + salt[i] = c; + } + if (i != sizeof salt) + { + log_error ("SALT in SEIPDv2 packet too short (%d/%u)\n", i, 32); + rc = gpg_error (GPG_ERR_TOO_SHORT); + goto leave; + } + + /* Expand key to M + N - 8. */ + kdfparam[0] = dek->keylen + startivlen - 8; + + kdfinfo[0] = 0xd2; + kdfinfo[1] = 2; + kdfinfo[2] = dek->algo; + kdfinfo[3] = ed->aead_algo; + kdfinfo[4] = ed->chunkbyte; + + if (DBG_CRYPTO) + { + log_printhex (salt, sizeof salt, "salt:"); + log_printhex (kdfinfo, sizeof kdfinfo, "info:"); + } + rc = gcry_kdf_open (&kdfhd, GCRY_KDF_HKDF, GCRY_MAC_HMAC_SHA256, + kdfparam, 1, + dek->key, dek->keylen, + NULL, 0, + salt, sizeof salt, + kdfinfo, sizeof kdfinfo); + if (!rc) + { + rc = gcry_kdf_compute (kdfhd, NULL); + if (!rc) + { + /* Allocate M + N */ + thekeybufferlen = dek->keylen + startivlen; + thekeybuffer = xtrymalloc_secure (thekeybufferlen); + if (!thekeybuffer) + rc = gpg_error_from_syserror (); + else /* Get M + N - 8 */ + rc = gcry_kdf_final (kdfhd, kdfparam[0], thekeybuffer); + } + gcry_kdf_close (kdfhd); + } + if (rc) + { + log_error ("HKDF for session key failed: %s\n", gpg_strerror(rc)); + goto leave; + } + log_assert (sizeof dfx->startiv >= startivlen); + memcpy (dfx->startiv, thekeybuffer + dek->keylen, startivlen - 8); + memset (dfx->startiv + dek->keylen + startivlen - 8, 0, 8); + thekey = thekeybuffer; + thekeylen = dek->keylen; + } + else /* Standard RFC-4880bis (aka LibrePGP) mode. */ + { + /* Read the Start-IV (aka nonce). */ + if (ed->len) + { + for (i=0; i < startivlen && ed->len; i++, ed->len--) + { + if ((c=iobuf_get (ed->buf)) == -1) + break; + dfx->startiv[i] = c; + } + } + else + { + for (i=0; i < startivlen; i++ ) + if ( (c=iobuf_get (ed->buf)) == -1 ) + break; + else + dfx->startiv[i] = c; + } + if (i != startivlen) + { + log_error ("Start-IV in OCB packet too short (%d/%u)\n", + i, startivlen); + rc = gpg_error (GPG_ERR_TOO_SHORT); + goto leave; + } + thekey = dek->key; + thekeylen = dek->keylen; + } + rc = openpgp_cipher_open (&dfx->cipher_hd, dfx->cipher_algo, ciphermode, @@ -389,8 +502,8 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek, goto leave; /* Should never happen. */ if (DBG_CRYPTO) - log_printhex (dek->key, dek->keylen, "thekey:"); - rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen); + log_printhex (thekey, thekeylen, "thekey:"); + rc = gcry_cipher_setkey (dfx->cipher_hd, thekey, thekeylen); if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY) { log_info (_("WARNING: message was encrypted with" @@ -408,7 +521,6 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek, log_error(_("problem handling encrypted packet\n")); goto leave; } - } else /* CFB encryption. */ { @@ -441,8 +553,8 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek, BUG(); } - - /* log_hexdump( "thekey", dek->key, dek->keylen );*/ + if (DBG_CRYPTO) + log_printhex (dek->key, dek->keylen, "thekey:"); rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen); if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY ) { @@ -588,6 +700,11 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek, } leave: + if (thekeybuffer) + { + wipememory (thekeybuffer, thekeybufferlen); + xfree (thekeybuffer); + } release_dfx_context (dfx); return rc; } diff --git a/g10/ecdh.c b/g10/ecdh.c index 3d341a799..29e81f06e 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -109,7 +109,7 @@ ecc_build_kdf_params (unsigned char **r_kdf_params, size_t *r_len, (namely, hash algo for KDF and symmetric algo for wrapping key). Using this specifier (together with curve OID of the public key and the fingerprint), we build _the_ KDF parameters. */ - if (!gcry_mpi_get_flag (pkey[2], GCRYMPI_FLAG_OPAQUE)) + if (!pkey[2] || !gcry_mpi_get_flag (pkey[2], GCRYMPI_FLAG_OPAQUE)) return gpg_error (GPG_ERR_BAD_PUBKEY); kdf_params_spec = gcry_mpi_get_opaque (pkey[2], &nbits); diff --git a/g10/encrypt.c b/g10/encrypt.c index e1f9b35d7..9fe42cafe 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -154,7 +154,9 @@ create_dek_with_warnings (pk_list_t pk_list) { int non_kyber_pk = 0; for ( ; pk_list; pk_list = pk_list->next) - if (pk_list->pk->pubkey_algo != PUBKEY_ALGO_KYBER) + if (!(pk_list->pk->pubkey_algo == PUBKEY_ALGO_KYBER + || (RFC9980 + && IS_PUBKEY_ALGO_MLK (pk_list->pk->pubkey_algo)))) non_kyber_pk += 1; if (!non_kyber_pk) dek->algo = CIPHER_ALGO_AES256; @@ -212,6 +214,7 @@ check_encryption_compliance (DEK *dek, pk_list_t pk_list) /* From here on we only test for CO_DE_VS - if we ever want to * return other compliance mode values we need to change this to * loop over all those values. */ + /* FIXME:CO_FIPS */ compliant = gnupg_gcrypt_is_compliant (CO_DE_VS); if (!gnupg_cipher_is_compliant (CO_DE_VS, dek->algo, GCRY_CIPHER_MODE_CFB)) @@ -394,7 +397,8 @@ use_aead (pk_list_t pk_list, int algo) can_use = openpgp_cipher_get_algo_blklen (algo) == 16; /* With --force-aead we want OCB. We also use OCB in symmetric mode - * with --use-ocb-sym which is detected by an empty PK_LIST. */ + * with --use-ocb-sym which is detected by an empty PK_LIST. In + * FIPS mode we silently replace OCB by GCM. */ if (opt.force_ocb || (!pk_list && opt.use_ocb_sym)) { if (!can_use) @@ -403,7 +407,7 @@ use_aead (pk_list_t pk_list, int algo) openpgp_cipher_algo_name (algo)); return 0; } - return AEAD_ALGO_OCB; + return opt.compliance == CO_FIPS? AEAD_ALGO_GCM : AEAD_ALGO_OCB; } /* AEAD does only work with 128 bit cipher blocklength. */ @@ -436,6 +440,26 @@ use_mdc (pk_list_t pk_list,int algo) } +/* We use the SEIPDv2 packet only if all keys are from RFC9980. */ +int +use_rfc9980_seipdv2 (pk_list_t pk_list) +{ + PKT_public_key *pk; + + if (!RFC9980 || !pk_list) + return 0; /* No. */ + + for ( ; pk_list; pk_list = pk_list->next ) + { + pk = pk_list->pk; + if (!(pk->pubkey_algo == PUBKEY_ALGO_X25519 + || IS_PUBKEY_ALGO_MLK (pk->pubkey_algo))) + return 0; /* No. */ + } + return 1; /* Yes. */ +} + + /* This function handles the --symmetric only (MODE true) and --store * (MODE false) cases. We don't want to use USE_SESKEY by default * very old gnupg versions can't handle it, and there isn't really any @@ -941,6 +965,8 @@ encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename, cfx.dek->use_aead = use_aead (pk_list, cfx.dek->algo); if (!cfx.dek->use_aead) cfx.dek->use_mdc = !!use_mdc (pk_list, cfx.dek->algo); + else if (use_rfc9980_seipdv2 (pk_list)) + cfx.seipdv2 = 1; /* Use SEIPDV2 and not the OCB.*/ make_session_key (cfx.dek); if (DBG_CRYPTO) @@ -982,6 +1008,9 @@ encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename, else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ + if (cfx.seipdv2) + log_info (_("Note: Using the %s encryption packet\n"), "RFC-9580"); + /* Register the cipher filter. */ iobuf_push_filter (out, cfx.dek->use_aead? cipher_filter_aead @@ -1226,6 +1255,8 @@ encrypt_filter (void *opaque, int control, { efx->header_okay = 1; + /* Fixme: The core functionality is duplicated from + * encrypt_crypt. We should use common functions. */ efx->cfx.dek = create_dek_with_warnings (efx->pk_list); rc = check_encryption_compliance (efx->cfx.dek, efx->pk_list); @@ -1235,6 +1266,8 @@ encrypt_filter (void *opaque, int control, efx->cfx.dek->use_aead = use_aead (efx->pk_list, efx->cfx.dek->algo); if (!efx->cfx.dek->use_aead) efx->cfx.dek->use_mdc = !!use_mdc (efx->pk_list,efx->cfx.dek->algo); + else if (use_rfc9980_seipdv2 (efx->pk_list)) + efx->cfx.seipdv2 = 1; /* Use SEIPDV2 and not the OCB.*/ make_session_key ( efx->cfx.dek ); if (DBG_CRYPTO) @@ -1253,6 +1286,9 @@ encrypt_filter (void *opaque, int control, return rc; } + if (efx->cfx.seipdv2) + log_info (_("Note: Using the %s encryption packet\n"), "RFC-9580"); + iobuf_push_filter (a, efx->cfx.dek->use_aead? cipher_filter_aead /**/ : cipher_filter_cfb, @@ -1286,13 +1322,28 @@ write_pubkey_enc (ctrl_t ctrl, PKT_pubkey_enc *enc; int rc; gcry_mpi_t frame; + int is_rfc9980; + size_t fprlen; + + if (pk->pubkey_algo == PUBKEY_ALGO_X25519 + || IS_PUBKEY_ALGO_MLK (pk->pubkey_algo)) + is_rfc9980 = 1; + else + is_rfc9980 = 0; print_pubkey_algo_note ( pk->pubkey_algo ); enc = xmalloc_clear ( sizeof *enc ); enc->pubkey_algo = pk->pubkey_algo; - keyid_from_pk( pk, enc->keyid ); + keyid_from_pk (pk, enc->keyid); enc->throw_keyid = throw_keyid; enc->seskey_algo = dek->algo; /* (Used only by PUBKEY_ALGO_KYBER.) */ + if (is_rfc9980) + { + enc->version = 6; + fingerprint_from_pk (pk, enc->fpr, &fprlen); + log_assert (fprlen == 20 || fprlen == 32); + enc->fprlen = fprlen; + } /* Okay, what's going on: We have the session key somewhere in * the structure DEK and want to encode this session key in an diff --git a/g10/filter.h b/g10/filter.h index b15ce6aa5..23f01ed95 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -100,6 +100,9 @@ typedef struct * requires partial encoding for a larger data size. */ u32 datalen; + /* Set if the RFC-9850 SEIPD version 2 packet is to be used. */ + unsigned int seipdv2 : 1; + /* The current cipher handle. */ gcry_cipher_hd_t cipher_hd; diff --git a/g10/free-packet.c b/g10/free-packet.c index bdbca3f6f..5093a6c17 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -89,6 +89,8 @@ copy_pubkey_enc_parts (PKT_pubkey_enc *dst, PKT_pubkey_enc *src) dst->pubkey_algo = src->pubkey_algo; dst->seskey_algo = src->seskey_algo; dst->throw_keyid = src->throw_keyid; + dst->fprlen = src->fprlen; + memcpy (dst->fpr, src->fpr, sizeof src->fpr); if (!(n = pubkey_get_nenc (dst->pubkey_algo))) n = 1; /* All data is in the first item as an opaque MPI. */ @@ -99,6 +101,17 @@ copy_pubkey_enc_parts (PKT_pubkey_enc *dst, PKT_pubkey_enc *src) } +static void +free_onepass_sig (PKT_onepass_sig *ops) +{ + if (ops) + { + mpi_release (ops->salt); + xfree (ops); + } +} + + void free_seckey_enc( PKT_signature *sig ) { @@ -113,6 +126,7 @@ free_seckey_enc( PKT_signature *sig ) xfree(sig->revkey); xfree(sig->hashed); xfree(sig->unhashed); + mpi_release (sig->salt); /* Do not forget to update copy_signature() too. */ xfree(sig->rev_subject_info); @@ -148,6 +162,11 @@ release_public_key_parts (PKT_public_key *pk) xfree (pk->prefs); pk->prefs = NULL; } + if (pk->dks_prefs) + { + xfree (pk->dks_prefs); + pk->dks_prefs = NULL; + } free_user_id (pk->user_id); pk->user_id = NULL; if (pk->revkey) @@ -241,6 +260,7 @@ copy_public_key_basics (PKT_public_key *d, PKT_public_key *s) d->seckey_info = NULL; d->user_id = NULL; d->prefs = NULL; + d->dks_prefs = NULL; d->revoked.got_reason = 0; d->revoked.reason_code = 0; d->revoked.reason_comment = NULL; @@ -275,6 +295,7 @@ copy_public_key (PKT_public_key *d, PKT_public_key *s) d = copy_public_key_basics (d, s); d->user_id = scopy_user_id (s->user_id); d->prefs = copy_prefs (s->prefs); + d->dks_prefs = copy_prefs (s->dks_prefs); if (!s->revkey && s->numrevkeys) BUG(); @@ -326,6 +347,7 @@ copy_signature( PKT_signature *d, PKT_signature *s ) } d->hashed = cp_subpktarea (s->hashed); d->unhashed = cp_subpktarea (s->unhashed); + d->salt = s->salt? my_mpi_copy (s->salt) : NULL; if (s->signers_uid) d->signers_uid = xstrdup (s->signers_uid); else @@ -520,6 +542,9 @@ free_packet (PACKET *pkt, parse_packet_ctx_t parsectx) case PKT_SIGNATURE: free_seckey_enc (pkt->pkt.signature); break; + case PKT_ONEPASS_SIG: + free_onepass_sig (pkt->pkt.onepass_sig); + break; case PKT_PUBKEY_ENC: free_pubkey_enc (pkt->pkt.pubkey_enc); break; @@ -543,7 +568,7 @@ free_packet (PACKET *pkt, parse_packet_ctx_t parsectx) break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: - case PKT_ENCRYPTED_AEAD: + case PKT_ENCRYPTED_OCB: free_encrypted (pkt->pkt.encrypted); break; case PKT_PLAINTEXT: diff --git a/g10/getkey.c b/g10/getkey.c index 3a59f48fa..057cf7ff1 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -2628,6 +2628,63 @@ parse_key_usage (PKT_signature * sig) } +/* Return an array of preferences. That array is terminated by a + * PREFTYPE_NONE entry. NULL is returned if the signature has no + * suitable preferences. */ +static prefitem_t * +parse_prefs (PKT_signature *sig) +{ + prefitem_t *prefs; + const byte *p, *sym, *aead, *hash, *zip; + size_t n, nsym, naead, nhash, nzip; + + /* The preferences must come from the hashed section so nobody can + * modify the ciphers a key is willing to accept. */ + p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_SYM, &n); + sym = p; + nsym = p ? n : 0; + p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_AEAD, &n); + aead = p; + naead = p ? n : 0; + p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_HASH, &n); + hash = p; + nhash = p ? n : 0; + p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_COMPR, &n); + zip = p; + nzip = p ? n : 0; + n = nsym + naead + nhash + nzip; + if (!n) + return NULL; + + prefs = xmalloc (sizeof (*prefs) * (n + 1)); + n = 0; + for (; nsym; nsym--, n++) + { + prefs[n].type = PREFTYPE_SYM; + prefs[n].value = *sym++; + } + for (; naead; naead--, n++) + { + prefs[n].type = PREFTYPE_AEAD; + prefs[n].value = *aead++; + } + for (; nhash; nhash--, n++) + { + prefs[n].type = PREFTYPE_HASH; + prefs[n].value = *hash++; + } + for (; nzip; nzip--, n++) + { + prefs[n].type = PREFTYPE_ZIP; + prefs[n].value = *zip++; + } + prefs[n].type = PREFTYPE_NONE; /* End of list marker */ + prefs[n].value = 0; + + return prefs; +} + + /* Apply information from SIGNODE (which is the valid self-signature * associated with that UID) to the UIDNODE: * - whether the UID has been revoked @@ -2642,8 +2699,8 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated) { PKT_user_id *uid = uidnode->pkt->pkt.user_id; PKT_signature *sig = signode->pkt->pkt.signature; - const byte *p, *sym, *aead, *hash, *zip; - size_t n, nsym, naead, nhash, nzip; + const byte *p; + size_t n; sig->flags.chosen_selfsig = 1;/* We chose this one. */ uid->created = 0; /* Not created == invalid. */ @@ -2687,58 +2744,9 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated) if (p && *p) uid->flags.primary = 2; - /* We could also query this from the unhashed area if it is not in - * the hased area and then later try to decide which is the better - * there should be no security problem with this. - * For now we only look at the hashed one. */ - - /* Now build the preferences list. These must come from the - hashed section so nobody can modify the ciphers a key is - willing to accept. */ - p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_SYM, &n); - sym = p; - nsym = p ? n : 0; - p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_AEAD, &n); - aead = p; - naead = p ? n : 0; - p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_HASH, &n); - hash = p; - nhash = p ? n : 0; - p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_COMPR, &n); - zip = p; - nzip = p ? n : 0; - if (uid->prefs) - xfree (uid->prefs); - n = nsym + naead + nhash + nzip; - if (!n) - uid->prefs = NULL; - else - { - uid->prefs = xmalloc (sizeof (*uid->prefs) * (n + 1)); - n = 0; - for (; nsym; nsym--, n++) - { - uid->prefs[n].type = PREFTYPE_SYM; - uid->prefs[n].value = *sym++; - } - for (; naead; naead--, n++) - { - uid->prefs[n].type = PREFTYPE_AEAD; - uid->prefs[n].value = *aead++; - } - for (; nhash; nhash--, n++) - { - uid->prefs[n].type = PREFTYPE_HASH; - uid->prefs[n].value = *hash++; - } - for (; nzip; nzip--, n++) - { - uid->prefs[n].type = PREFTYPE_ZIP; - uid->prefs[n].value = *zip++; - } - uid->prefs[n].type = PREFTYPE_NONE; /* End of list marker */ - uid->prefs[n].value = 0; - } + /* Get the preferences. */ + xfree (uid->prefs); + uid->prefs = parse_prefs (sig); /* See whether we have the MDC feature. */ uid->flags.mdc = 0; @@ -3028,6 +3036,8 @@ merge_selfsigs_main (ctrl_t ctrl, kbnode_t keyblock, int *r_revoked, key_expire_seen = 1; } + pk->dks_prefs = parse_prefs (sig); + /* Mark that key as valid: One direct key signature should * render a key as valid. */ pk->flags.valid = 1; @@ -3712,6 +3722,14 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock) break; } } + + /* In rfc-9980 mode fallback to prefs from direct key signatures. */ + if (!prefs && RFC9980) + { + if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) + prefs = keyblock->pkt->pkt.public_key->dks_prefs; + } + for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY @@ -242,6 +242,7 @@ enum cmd_and_opt_values oDebugSetIobufSize, oDebugAllowLargeChunks, oDebugIgnoreExpiration, + oDebugNoLibgcrypt, oStatusFD, oStatusFile, oAttributeFD, @@ -260,6 +261,7 @@ enum cmd_and_opt_values oPGP7, oPGP8, oDE_VS, + oFIPS, oMinRSALength, oRFC2440Text, oNoRFC2440Text, @@ -439,6 +441,7 @@ enum cmd_and_opt_values oDisableDSA2, oAllowWeakDigestAlgos, oAllowWeakKeySignatures, + oAllow9980, oFakedSystemTime, oNoAutostart, oPrintDANERecords, @@ -624,6 +627,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oDebugIOLBF, "debug-iolbf", "@"), ARGPARSE_s_u (oDebugSetIobufSize, "debug-set-iobuf-size", "@"), ARGPARSE_s_u (oDebugAllowLargeChunks, "debug-allow-large-chunks", "@"), + ARGPARSE_s_n (oDebugNoLibgcrypt, "debug-no-libgcrypt", "@"), ARGPARSE_s_s (oDisplayCharset, "display-charset", "@"), ARGPARSE_s_s (oDisplayCharset, "charset", "@"), ARGPARSE_conffile (oOptions, "options", N_("|FILE|read options from FILE")), @@ -921,6 +925,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_s (oCipherAlgo, "cipher-algo", "@"), ARGPARSE_s_s (oDigestAlgo, "digest-algo", "@"), ARGPARSE_s_s (oCertDigestAlgo, "cert-digest-algo", "@"), + ARGPARSE_s_n (oAllow9980, "allow-9980", "@"), ARGPARSE_s_n (oRequirePQCEncryption, "require-pqc-encryption", "@"), ARGPARSE_s_n (oDisablePQCEncryption, "disable-pqc-encryption", "@"), @@ -1111,6 +1116,7 @@ static int opt_log_time; /* Collection of options used only in this module. */ static struct { unsigned int forbid_gen_key; + unsigned int no_libgcrypt_debug; } mopt; @@ -1413,9 +1419,9 @@ set_debug (const char *level) memory_debug_mode = 1; if ((opt.debug & DBG_MEMSTAT_VALUE)) memory_stat_debug_mode = 1; - if (DBG_MPI) + if (DBG_MPI && !mopt.no_libgcrypt_debug) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2); - if (DBG_CRYPTO) + if (DBG_CRYPTO && !mopt.no_libgcrypt_debug) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); if ((opt.debug & DBG_IOBUF_VALUE)) iobuf_debug_mode = 1; @@ -2042,6 +2048,9 @@ gpgconf_list (void) (opt.compliance==CO_DE_VS && gnupg_rng_is_compliant (CO_DE_VS))? atoi (gnupg_status_compliance_flag (CO_DE_VS)) : 0); + es_printf ("compliance_fips:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, + (opt.compliance==CO_FIPS)? + atoi (gnupg_status_compliance_flag (CO_FIPS)) : 0); es_printf ("use_keyboxd:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, opt.use_keyboxd); @@ -2160,6 +2169,8 @@ parse_list_options(char *str) NULL}, {"sort-sigs", LIST_SORT_SIGS, NULL, NULL}, + {"debug-show-sexp", LIST_DEBUG_SHOW_SEXP, NULL, + NULL}, {NULL,0,NULL,NULL} }; int i; @@ -2294,7 +2305,8 @@ static struct gnupg_compliance_option compliance_options[] = { "pgp6", oPGP7 }, { "pgp7", oPGP7 }, { "pgp8", oPGP8 }, - { "de-vs", oDE_VS } + { "de-vs", oDE_VS }, + { "fips", oFIPS } }; @@ -2324,6 +2336,7 @@ set_compliance_option (enum cmd_and_opt_values option) opt.s2k_digest_algo = 0; opt.s2k_cipher_algo = DEFAULT_CIPHER_ALGO; opt.flags.allow_old_cipher_algos = 0; + opt.flags.allow_9980 = 0; break; case oOpenPGP: @@ -2369,6 +2382,16 @@ set_compliance_option (enum cmd_and_opt_values option) /* We divert here from the backward compatible rfc4880 algos. */ opt.s2k_digest_algo = DIGEST_ALGO_SHA256; opt.s2k_cipher_algo = CIPHER_ALGO_AES256; + opt.flags.allow_9980 = 1; /* Germany likes the NSA. */ + break; + + case oFIPS: + set_compliance_option (oGnuPG); + opt.compliance = CO_FIPS; + /* We divert here from the backward compatible rfc4880 algos. */ + opt.s2k_digest_algo = DIGEST_ALGO_SHA256; + opt.s2k_cipher_algo = CIPHER_ALGO_AES256; + opt.flags.allow_9980 = 1; /* Required to have GCM. */ break; default: @@ -2913,6 +2936,8 @@ main (int argc, char **argv) case oDebugAll: opt.debug = ~0; break; case oDebugLevel: debug_level = pargs.r.ret_str; break; + case oDebugNoLibgcrypt: mopt.no_libgcrypt_debug = 1; break; + case oDebugIOLBF: break; /* Already set in pre-parse step. */ case oDebugSetIobufSize: @@ -3587,6 +3612,7 @@ main (int argc, char **argv) } break; case oNoSigCache: opt.no_sig_cache = 1; break; + case oAllow9980: opt.flags.allow_9980 = 1; break; case oAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid = 1; break; case oNoAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid=0; break; case oAllowFreeformUID: opt.allow_freeform_uid = 1; break; diff --git a/g10/gpgv.c b/g10/gpgv.c index 23704e21c..382720ef8 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -578,11 +578,13 @@ import_included_key_block (ctrl_t ctrl, kbnode_t keyblock) * No encryption here but mainproc links to these functions. */ gpg_error_t -get_session_key (ctrl_t ctrl, struct seskey_enc_list *k, DEK *dek) +get_session_key (ctrl_t ctrl, struct seskey_enc_list *k, DEK *dek, + int seipdv2_cipher_algo) { (void)ctrl; (void)k; (void)dek; + (void)seipdv2_cipher_algo; return GPG_ERR_GENERAL; } @@ -881,3 +883,10 @@ impex_filter_getval (void *cookie, const char *propname) (void)propname; return NULL; } + +nvc_t +seckey_packet_to_nvc (PKT_public_key *pk) +{ + (void)pk; + return NULL; +} diff --git a/g10/import.c b/g10/import.c index 8279edead..593995c37 100644 --- a/g10/import.c +++ b/g10/import.c @@ -223,6 +223,9 @@ parse_import_options(char *str,unsigned int *options,int noisy) {"collapse-uids", IMPORT_COLLAPSE_UIDS, NULL, NULL}, {"collapse-subkeys", IMPORT_COLLAPSE_SUBKEYS, NULL, NULL}, + /* Debug only options used for regression testings. */ + {"debug-accept-no-uid", IMPORT_DEBUG_ACCEPT_NO_UID, NULL, NULL }, + /* Aliases for backward compatibility */ {"allow-local-sigs",IMPORT_LOCAL_SIGS,NULL,NULL}, {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,NULL}, @@ -2048,7 +2051,8 @@ import_one_real (ctrl_t ctrl, { if (!silent) log_error( _("key %s: no user ID\n"), keystr_from_pk(pk)); - return 0; + if (!(options & IMPORT_DEBUG_ACCEPT_NO_UID)) + return 0; } if (screener && screener (keyblock, screener_arg)) @@ -2154,7 +2158,8 @@ import_one_real (ctrl_t ctrl, log_info(_("this may be caused by a missing self-signature\n")); } stats->no_user_id++; - return 0; + if (!(options & IMPORT_DEBUG_ACCEPT_NO_UID)) + return 0; } /* Get rid of deleted nodes. */ @@ -2182,7 +2187,8 @@ import_one_real (ctrl_t ctrl, if (!opt.quiet ) log_info ( _("key %s: no valid user IDs\n"), keystr_from_pk (pk)); stats->no_user_id++; - return 0; + if (!(options & IMPORT_DEBUG_ACCEPT_NO_UID)) + return 0; } /* The keyblock is valid and ready for real import. */ @@ -2583,6 +2589,230 @@ import_one (ctrl_t ctrl, +/* Helper to build an s-expression with a composite key RFC-9980 + * ML-KEM key from the data in PK and the additional information + * MLKNAME and ECCNAME. Returns 0 on success and store the + * s-expression at R_SKEY. */ +static gpg_error_t +build_sexp_from_mlk (gcry_sexp_t *r_skey, PKT_public_key *pk, + const char *mlkname, const char *eccname) +{ + gpg_error_t err; + gcry_sexp_t s_kyberparm = NULL; + gcry_sexp_t s_kyberfull = NULL; + gcry_sexp_t s_kyberpriv = NULL; + gcry_mpi_t mpi_p = NULL; + + *r_skey = NULL; + + /* A quick first check. */ + if (!gcry_mpi_get_flag (pk->pkey[3], GCRYMPI_FLAG_OPAQUE) + || !gcry_mpi_get_opaque (pk->pkey[3], NULL)) + { + err = gpg_error (GPG_ERR_BAD_MPI); + goto leave; + } + + /* Create the full Kyber key from the supplied seed. */ + err = gcry_sexp_build (&s_kyberparm, NULL, + "(genkey(%s(derive-parms(seed%m))))", + mlkname, pk->pkey[3]); + if (!err) + err = gcry_pk_genkey (&s_kyberfull, s_kyberparm); + if (err) + goto leave; + gcry_sexp_release (s_kyberparm); s_kyberparm = NULL; + + /* Extract the private kyber part. */ + s_kyberpriv = gcry_sexp_find_token (s_kyberfull, "private-key", 0); + if (!s_kyberpriv) + { + log_error ("key conversion failed: no private key part\n"); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + + /* Check that the generated public part as returned by genkey + * matches the supplied public key. */ + err = gcry_sexp_extract_param (s_kyberfull, "public-key", "/p", &mpi_p, NULL); + if (err) + goto leave; + if (gcry_mpi_cmp (pk->pkey[1], mpi_p)) + { + err = gpg_error (GPG_ERR_BAD_PUBKEY); + log_error ("key import failed: public key does not match private key\n"); + goto leave; + } + + gcry_sexp_release (s_kyberfull); s_kyberfull = NULL; + + /* Build the composite s-expression. */ + err = gcry_sexp_build (r_skey, NULL, + "(composite-key" + "(private-key(ecc(curve %s)(q%m)(d%m)))" + "%S" + ")", + eccname, pk->pkey[0], pk->pkey[2], s_kyberpriv); + + + leave: + gcry_sexp_release (s_kyberpriv); + gcry_sexp_release (s_kyberfull); + gcry_sexp_release (s_kyberparm); + gcry_mpi_release (mpi_p); + return err; +} + + +/* This function builds a gpg-agent private key format (aka mode1003) + * s-expression from the secret key packet in PK. It currently + * fails for a protected key. The final plan is to use a separate + * helper for re-encrypting a protected key. */ +static gpg_error_t +build_mode1003_sexp (PKT_public_key *pk, gcry_sexp_t *result) +{ + gpg_error_t err; + int nskey; + gcry_sexp_t skey = NULL; + char *curvename = NULL; + + *result = NULL; + + nskey = pubkey_get_nskey (pk->pubkey_algo); + if (!nskey || nskey > PUBKEY_MAX_NSKEY || !pk->seckey_info) + { + err = gpg_error (GPG_ERR_BAD_SECKEY); + log_error ("%s: internal error: %s\n", __func__, gpg_strerror (err)); + goto leave; + } + + if (pk->seckey_info->is_protected) + { + err = gpg_error (GPG_ERR_INTERNAL); + log_error ("%s: protected seckeys are not yet supported\n", __func__); + goto leave; + } + + switch (pk->pubkey_algo) + { + case GCRY_PK_RSA: + case GCRY_PK_RSA_E: + case GCRY_PK_RSA_S: + /* Check the condition P < Q, since libgcrypt requires that. + * LibrePGP/OpenPGP also requires this condition. */ + if (gcry_mpi_cmp (pk->pkey[3], pk->pkey[4]) >= 0) + err = gpg_error (GPG_ERR_BAD_SECKEY); + else + err = gcry_sexp_build + (&skey, NULL, "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", + pk->pkey[0], pk->pkey[1], + pk->pkey[2], pk->pkey[3], pk->pkey[4], pk->pkey[5]); + break; + + case PUBKEY_ALGO_DSA: + err = gcry_sexp_build (&skey, NULL, + "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))", + pk->pkey[0], pk->pkey[1], pk->pkey[2], + pk->pkey[3], pk->pkey[4]); + break; + + case PUBKEY_ALGO_ELGAMAL: + case PUBKEY_ALGO_ELGAMAL_E: + err = gcry_sexp_build (&skey, NULL, + "(private-key(elg(p%m)(g%m)(y%m)(x%m)))", + pk->pkey[0], pk->pkey[1], pk->pkey[2], + pk->pkey[3]); + break; + + case PUBKEY_ALGO_ECDH: + /* Note that pkey[2] conveys the KDF parameters. */ + curvename = openpgp_oid_to_str (pk->pkey[0]); + if (!curvename) + err = gpg_error_from_syserror (); + else if (openpgp_oid_is_cv25519 (pk->pkey[0])) + err = gcry_sexp_build + (&skey,NULL,"(private-key(ecc(curve %s)(flags djb-tweak)(q%m)(d%m)))", + curvename, pk->pkey[2], pk->pkey[3]); + else + err = gcry_sexp_build + (&skey,NULL,"(private-key(ecc(curve %s)(q%m)(d%m)))", + curvename, pk->pkey[2], pk->pkey[3]); + break; + + case PUBKEY_ALGO_X25519: + err = gcry_sexp_build + (&skey,NULL,"(private-key(ecc(curve ietf25)(q%m)(d%m)))", + pk->pkey[0], pk->pkey[1]); + break; + + case PUBKEY_ALGO_ECDSA: + curvename = openpgp_oid_to_str (pk->pkey[0]); + if (!curvename) + err = gpg_error_from_syserror (); + else + err = gcry_sexp_build + (&skey, NULL, "(private-key(ecc(curve %s)(q%m)(d%m)))", + curvename, pk->pkey[1], pk->pkey[2]); + break; + + case PUBKEY_ALGO_EDDSA: + curvename = openpgp_oid_to_str (pk->pkey[0]); + if (!curvename) + err = gpg_error_from_syserror (); + else + err = gcry_sexp_build + (&skey, NULL, "(private-key(ecc(curve %s)(flags eddsa)(q%m)(d%m)))", + curvename, pk->pkey[1], pk->pkey[2]); + break; + + case PUBKEY_ALGO_ED25519: + err = gcry_sexp_build + (&skey, NULL,"(private-key(ecc(curve Ed25519)(flags eddsa)(q%m)(d%m)))", + pk->pkey[0], pk->pkey[1]); + break; + + case PUBKEY_ALGO_MLK768_25519: + err = build_sexp_from_mlk (&skey, pk, "kyber768", "ietf25"); + break; + + case PUBKEY_ALGO_MLK768_NP384: + err = build_sexp_from_mlk (&skey, pk, "kyber768", "nistp384"); + break; + + case PUBKEY_ALGO_MLK768_BP384: + err = build_sexp_from_mlk (&skey, pk, "kyber768", "brainpoolP384r1"); + break; + + case PUBKEY_ALGO_MLK1024_448: + err = build_sexp_from_mlk (&skey, pk, "kyber1024", "X448"); + break; + + case PUBKEY_ALGO_MLK1024_NP521: + err = build_sexp_from_mlk (&skey, pk, "kyber1024", "nistp521"); + break; + + case PUBKEY_ALGO_MLK1024_BP512: + err = build_sexp_from_mlk (&skey, pk, "kyber1024", "brainpoolP512r1"); + break; + + default: + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + break; + } + + if (!err) + { + *result = skey; + skey = NULL; + } + + leave: + gcry_sexp_release (skey); + xfree (curvename); + return err; +} + + /* Convert our internal secret key object into an S-expression. PK is * the public key. R_CURVE received an sexp with the name of the * curve; caller must free this. R_SKEY will receive the result; @@ -2670,6 +2900,44 @@ internal_skey_object_to_sexp (PKT_public_key *pk, gcry_sexp_t *r_curve, " are not cleared\n"); } } + else if (RFC9980 && pk->pubkey_algo == PUBKEY_ALGO_ED25519) + { + gcry_sexp_release (*r_curve); + err = gcry_sexp_build (r_curve, NULL, "(curve Ed25519)"); + if (err) + goto leave; + + j = 0; + /* Append the public key element Q. */ + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = pk->pkey + 0; + + /* Append the secret key element D. */ + if (gcry_mpi_get_flag (pk->pkey[1], GCRYMPI_FLAG_USER1)) + put_membuf_str (&mbuf, " e %m"); + else + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = pk->pkey + 1; + } + else if (RFC9980 && pk->pubkey_algo == PUBKEY_ALGO_X25519) + { + gcry_sexp_release (*r_curve); + err = gcry_sexp_build (r_curve, NULL, "(curve ietf25)"); + if (err) + goto leave; + + j = 0; + /* Append the public key element Q. */ + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = pk->pkey + 0; + + /* Append the secret key element D. */ + if (gcry_mpi_get_flag (pk->pkey[1], GCRYMPI_FLAG_USER1)) + put_membuf_str (&mbuf, " e %m"); + else + put_membuf_str (&mbuf, " _ %m"); + format_args[j++] = pk->pkey + 1; + } else /* Standard case for the old (non-ECC) algorithms. */ { for (i=j=0; i < nskey; i++) @@ -2795,6 +3063,7 @@ transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, size_t wrappedkeylen; char *cache_nonce = NULL; int stub_key_skipped = 0; + int use_mode1003 = 0; /* Get the current KEK. */ err = agent_keywrap_key (ctrl, 0, &kek, &keklen); @@ -2888,6 +3157,13 @@ transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, tmpbuflen = (tmpbuflen +7)/8; /* Fixup bits to bytes */ err = gcry_sexp_new (&tmpsexp, tmpbuf, tmpbuflen, 0); } + use_mode1003 = 1; + } + else if (!ski->is_protected) + { + /* The key is not protected. Build a mode1003 format here. */ + err = build_mode1003_sexp (pk, &tmpsexp); + use_mode1003 = 1; } else err = build_classic_transfer_sexp (pk, &tmpsexp); @@ -2921,7 +3197,7 @@ transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, /* Send the wrapped key to the agent. */ { char *desc = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_IMPORT, 1); - err = agent_import_key (ctrl, desc, ski->s2k.mode == 1003, + err = agent_import_key (ctrl, desc, use_mode1003, &cache_nonce, wrappedkey, wrappedkeylen, batch, force, pk->keyid, pk->main_keyid, pk->pubkey_algo, @@ -2970,6 +3246,34 @@ transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, } +/* Create a name-value container from a secret (sub)key packet PK. + * Callers needs to release it. This function can be used to list + * secret keys as s-expression in a format as used for our private + * keys. Works only with unprotected keys. Callers needs to release + * the result. On error NULL is returned. */ +nvc_t +seckey_packet_to_nvc (PKT_public_key *pk) +{ + gcry_sexp_t mysexp; + nvc_t result; + + if (build_mode1003_sexp (pk, &mysexp) || !mysexp) + return NULL; + + result = nvc_new_private_key (); + if (result) + { + if (nvc_set_private_key (result, mysexp)) + { + nvc_release (result); + result = NULL; + } + } + gcry_sexp_release (mysexp); + return result; +} + + /* Walk a secret keyblock and produce a public keyblock out of it. * Returns a new node or NULL on error. Modifies the tag field of the * nodes. */ @@ -3302,8 +3606,11 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock, { if (!for_migration) log_error( _("key %s: no user ID\n"), keystr_from_pk (pk)); - release_kbnode (keyblock); - return 0; + if (!(options & IMPORT_DEBUG_ACCEPT_NO_UID)) + { + release_kbnode (keyblock); + return 0; + } } ski = pk->seckey_info; @@ -4462,7 +4769,7 @@ collapse_subkeys (kbnode_t *keyblock) break; } - /* Snip out subkye-2 */ + /* Snip out subkey-2 */ find_prev_kbnode (*keyblock, kb2, 0)->next = last->next; /* Put subkey-2 in place as part of subkey-1 */ diff --git a/g10/keydb.h b/g10/keydb.h index 364e1287c..e45560c16 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -509,7 +509,7 @@ char *pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize); int compare_pubkey_string (const char *astr, const char *bstr); #define PUBKEY_STRING_SIZE 32 u32 v3_keyid (gcry_mpi_t a, u32 *ki); -void hash_public_key( gcry_md_hd_t md, PKT_public_key *pk ); +void hash_public_key( gcry_md_hd_t md, PKT_public_key *pk, PKT_signature *sig); char *format_keyid (u32 *keyid, int format, char *buffer, int len); /* Return PK's keyid. The memory is owned by PK. */ diff --git a/g10/keyedit.c b/g10/keyedit.c index 0eba8d378..affe6c655 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1384,7 +1384,7 @@ enum cmdids cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG, cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY, cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, - cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN, cmdADDADSK, + cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN, cmdADDADSK, cmdADDPUBKEY, #ifndef NO_TRUST_MODELS cmdENABLEKEY, cmdDISABLEKEY, #endif /*!NO_TRUST_MODELS*/ @@ -1436,6 +1436,7 @@ static struct /* delphoto is really deluid in disguise */ { "delphoto", cmdDELUID, 0, NULL}, { "addkey", cmdADDKEY, KEYEDIT_NEED_SK, N_("add a subkey")}, + { "addpubkey", cmdADDPUBKEY, KEYEDIT_NEED_SK, NULL }, #ifdef ENABLE_CARD_SUPPORT { "addcardkey", cmdADDCARDKEY, KEYEDIT_NEED_SK, N_("add a key to a smartcard")}, @@ -1954,6 +1955,86 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, } break; + case cmdADDPUBKEY: + { + /* Ask for a filename, check whether this is really a + * subkey key, parse that key, and add it as subkey to the + * current keyblock. */ + kbnode_t node; + char *fname; + PACKET *pkt; + iobuf_t a; + struct parse_packet_ctx_s parsectx; + + if (!*arg_string) + { + tty_printf (_("Command expects a filename argument\n")); + break; + } + + if (*arg_string == DIRSEP_C) + fname = xstrdup (arg_string); + else if (*arg_string == '~') + fname = make_filename (arg_string, NULL); + else + fname = make_filename (gnupg_homedir (), arg_string, NULL); + + /* Open that file. */ + a = iobuf_open (fname); + if (a && is_secured_file (iobuf_get_fd (a))) + { + iobuf_close (a); + a = NULL; + gpg_err_set_errno (EPERM); + } + if (!a) + { + tty_printf (_("Can't open '%s': %s\n"), + fname, strerror (errno)); + xfree (fname); + break; + } + + /* Parse and check that file. */ + pkt = xmalloc (sizeof *pkt); + init_packet (pkt); + init_parse_packet (&parsectx, a); + err = parse_packet (&parsectx, pkt); + deinit_parse_packet (&parsectx); + iobuf_close (a); + iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char *) fname); + if (!err && pkt->pkttype != PKT_PUBLIC_KEY + && pkt->pkttype != PKT_PUBLIC_SUBKEY) + err = GPG_ERR_NO_PUBKEY; + if (err) + { + tty_printf (_("Error reading key from '%s': %s\n"), + fname, gpg_strerror (err)); + xfree (fname); + free_packet (pkt, NULL); + xfree (pkt); + break; + } + + xfree (fname); + node = new_kbnode (pkt); + + /* Always treat the packet as a public subkey and append. */ + pkt->pkttype = PKT_PUBLIC_SUBKEY; + err = append_subkey_to_keyblock + (ctrl, keyblock, pkt->pkt.public_key, + ask_key_flags (pkt->pkt.public_key->pubkey_algo, 1, 0)); + if (!err || gpg_err_code (err) == GPG_ERR_EEXIST) + { + err = 0; + merge_keys_and_selfsig (ctrl, keyblock); + modified = 1; + redisplay = 1; + } + release_kbnode (node); + } + break; + #ifdef ENABLE_CARD_SUPPORT case cmdADDCARDKEY: if (!card_generate_subkey (ctrl, keyblock)) diff --git a/g10/keygen.c b/g10/keygen.c index 0456dbab5..2314dd8f6 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -57,6 +57,10 @@ #define PQC_STD_KEY_PARAM_PRI "bp384/cert,sign" #define PQC_STD_KEY_PARAM_SUB "kyber768_bp256/encr" #define PQC_STD_KEY_PARAM PQC_STD_KEY_PARAM_PRI "+" PQC_STD_KEY_PARAM_SUB +#define PQC9980_STD_KEY_PARAM_PRI "ietf27/cert,sign" +#define PQC9980_STD_KEY_PARAM_SUB "mlk768_bp384/encr" +#define PQC9980_STD_KEY_PARAM PQC9980_STD_KEY_PARAM_PRI \ + "+" PQC9980_STD_KEY_PARAM_SUB /* When generating keys using the streamlined key generation dialog, use this as a default expiration interval. */ @@ -66,6 +70,7 @@ const char *default_expiration_interval = "3y"; #define KEYGEN_FLAG_NO_PROTECTION 1 #define KEYGEN_FLAG_TRANSIENT_KEY 2 #define KEYGEN_FLAG_CREATE_V5_KEY 4 +#define KEYGEN_FLAG_CREATE_V6_KEY 8 /* Maximum number of supported algorithm preferences. */ #define MAX_PREFS 30 @@ -1468,6 +1473,9 @@ write_keybinding (ctrl_t ctrl, kbnode_t keyblock, /* The required libgcrypt 1.11 won't yet claim a compliant RNG. */ && gnupg_rng_is_compliant (CO_DE_VS)) oduap.cpl_notation = "de-vs"; + else if ((use & PUBKEY_USAGE_ENC) + && opt.compliance == CO_FIPS) + oduap.cpl_notation = "fips"; else oduap.cpl_notation = NULL; oduap.pk = sub_pk; @@ -1580,19 +1588,45 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, goto leave; } - err = openpgp_oid_from_str (oidstr, &array[0]); - if (err) - goto leave; + if (algo == PUBKEY_ALGO_ED25519 + || algo == PUBKEY_ALGO_X25519) + { + err = 0; + array[0] = get_mpi_from_sexp (list, "q", GCRYMPI_FMT_OPAQUE); + if (!array[0]) + { + err = gpg_error (GPG_ERR_NO_OBJ); + goto leave; + } + } + else if (IS_PUBKEY_ALGO_MLK (algo)) + { + err = 0; + array[0] = get_mpi_from_sexp_strip_0x40 (list, "q"); + if (!array[0]) + { + err = gpg_error (GPG_ERR_NO_OBJ); + goto leave; + } + } + else + { + err = openpgp_oid_from_str (oidstr, &array[0]); + if (err) + goto leave; - err = sexp_extract_param_sos (list, "q", &array[1]); - if (err) - goto leave; + err = sexp_extract_param_sos (list, "q", &array[1]); + if (err) + goto leave; + } gcry_sexp_release (list); list = NULL; - if (algo == PUBKEY_ALGO_KYBER) + if (algo == PUBKEY_ALGO_KYBER || IS_PUBKEY_ALGO_MLK (algo)) { + int arrayidx = algo == PUBKEY_ALGO_KYBER? 2 : 1; + if (!sexp2) { err = gpg_error (GPG_ERR_MISSING_VALUE); @@ -1620,9 +1654,9 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, err = gpg_error (GPG_ERR_NO_OBJ); /* required parameter not found */ goto leave; } - array[2] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_OPAQUE); + array[arrayidx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_OPAQUE); gcry_sexp_release (l2); - if (!array[2]) + if (!array[arrayidx]) { err = gpg_error (GPG_ERR_INV_OBJ); /* required parameter invalid */ goto leave; @@ -1801,8 +1835,12 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, } } - /* For X448 and Kyber we force the use of v5 packets. */ - if (curve_is_448 (s_key) || algo == PUBKEY_ALGO_KYBER) + /* For X448 and Kyber we force the use of v5 packets. For the 9980 + * algos we use v6 keys. */ + if (algo == PUBKEY_ALGO_ED25519 + || algo == PUBKEY_ALGO_X25519) + *keygen_flags |= KEYGEN_FLAG_CREATE_V6_KEY; + else if (curve_is_448 (s_key) || algo == PUBKEY_ALGO_KYBER) *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; /* Build a public key packet. */ @@ -1817,7 +1855,8 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, } pk->timestamp = timestamp; - pk->version = (*keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; + pk->version = (*keygen_flags & KEYGEN_FLAG_CREATE_V6_KEY)? 6 : + (*keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; @@ -1957,16 +1996,19 @@ common_gen (const char *keyparms, const char *keyparms2, } pk->timestamp = timestamp; - pk->version = (keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; + pk->version = (keygen_flags & KEYGEN_FLAG_CREATE_V6_KEY)? 6 : + (keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; - if (algo == PUBKEY_ALGO_KYBER) + if (algo == PUBKEY_ALGO_KYBER || IS_PUBKEY_ALGO_MLK (algo)) err = ecckey_from_sexp (pk->pkey, s_key, s_key2, algo, pk->version); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA - || algo == PUBKEY_ALGO_ECDH ) + || algo == PUBKEY_ALGO_ECDH + || algo == PUBKEY_ALGO_X25519 + || algo == PUBKEY_ALGO_ED25519) err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo, pk->version); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); @@ -2167,7 +2209,9 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, log_assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA - || algo == PUBKEY_ALGO_ECDH); + || algo == PUBKEY_ALGO_ECDH + || algo == PUBKEY_ALGO_X25519 + || algo == PUBKEY_ALGO_ED25519); if (!curve || !*curve) return gpg_error (GPG_ERR_UNKNOWN_CURVE); @@ -2194,6 +2238,24 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); } + else if (algo == PUBKEY_ALGO_X25519) + { + *keygen_flags |= KEYGEN_FLAG_CREATE_V6_KEY; + keyparms = xtryasprintf + ("(genkey(ecc(curve ietf25)(flags%s)))", + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + else if (algo == PUBKEY_ALGO_ED25519) + { + *keygen_flags |= KEYGEN_FLAG_CREATE_V6_KEY; + keyparms = xtryasprintf + ("(genkey(ecc(curve Ed25519)(flags eddsa%s)))", + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } else if (algo == PUBKEY_ALGO_EDDSA && !strcmp (curve, "Ed448")) { *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; @@ -2264,7 +2326,7 @@ gen_kyber (int algo, unsigned int nbits, const char *curve, kbnode_t pub_root, char *keyparms1; const char *keyparms2; - log_assert (algo == PUBKEY_ALGO_KYBER); + log_assert (algo == PUBKEY_ALGO_KYBER || IS_PUBKEY_ALGO_MLK (algo)); if (nbits == 768) keyparms2 = "(genkey(kyber768))"; @@ -2278,7 +2340,55 @@ gen_kyber (int algo, unsigned int nbits, const char *curve, kbnode_t pub_root, *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; - if (!strcmp (curve, "Curve25519") || !ascii_strcasecmp (curve, "cv25519")) + if (algo == PUBKEY_ALGO_MLK768_25519) + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve ietf25)(flags%s)))", + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + else if (algo == PUBKEY_ALGO_MLK768_NP384) + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve nistp384)(flags%s)))", + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + else if (algo == PUBKEY_ALGO_MLK768_BP384) + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve bp384)(flags%s)))", + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + else if (algo == PUBKEY_ALGO_MLK1024_448) + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve X448)(flags%s)))", + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + else if (algo == PUBKEY_ALGO_MLK1024_NP521) + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve nistp521)(flags%s)))", + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + else if (algo == PUBKEY_ALGO_MLK1024_BP512) + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve bp512)(flags%s)))", + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + else if (!strcmp (curve, "Curve25519")||!ascii_strcasecmp (curve, "cv25519")) { curve = "Curve25519"; keyparms1 = xtryasprintf @@ -3039,12 +3149,19 @@ get_keysize_range (int algo, unsigned int *min, unsigned int *max) break; case PUBKEY_ALGO_EDDSA: + case PUBKEY_ALGO_ED25519: *min=255; - *max=441; + *max=441; /* FIXME: Really? What about X448? */ def=255; break; case PUBKEY_ALGO_KYBER: + case PUBKEY_ALGO_MLK768_25519: + case PUBKEY_ALGO_MLK768_NP384: + case PUBKEY_ALGO_MLK768_BP384: + case PUBKEY_ALGO_MLK1024_448: + case PUBKEY_ALGO_MLK1024_NP521: + case PUBKEY_ALGO_MLK1024_BP512: *min = 768; *max = 1024; def = 768; @@ -3071,7 +3188,7 @@ fixup_keysize (unsigned int nbits, int algo, int silent) { nbits = ((nbits + 63) / 64) * 64; } - else if (algo == PUBKEY_ALGO_EDDSA) + else if (algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ED25519) { if (nbits < 256) nbits = 255; @@ -3087,7 +3204,7 @@ fixup_keysize (unsigned int nbits, int algo, int silent) else nbits = 521; } - else if (algo == PUBKEY_ALGO_KYBER) + else if (algo == PUBKEY_ALGO_KYBER || IS_PUBKEY_ALGO_MLK (algo)) { /* (in reality the numbers are not bits) */ if (nbits < 768) @@ -3901,12 +4018,14 @@ do_create (int algo, unsigned int nbits, const char *curve, kbnode_t pub_root, common_gen_cb, common_gen_cb_parm); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA - || algo == PUBKEY_ALGO_ECDH) + || algo == PUBKEY_ALGO_ECDH + || algo == PUBKEY_ALGO_X25519 + || algo == PUBKEY_ALGO_ED25519) err = gen_ecc (algo, curve, pub_root, timestamp, expiredate, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); - else if (algo == PUBKEY_ALGO_KYBER) + else if (algo == PUBKEY_ALGO_KYBER || IS_PUBKEY_ALGO_MLK (algo)) err = gen_kyber (algo, nbits, curve, pub_root, timestamp, expiredate, is_subkey, keygen_flags, passphrase, @@ -4067,20 +4186,76 @@ parse_key_parameter_part (ctrl_t ctrl, size = strstr (string, "768_")? 768 : 1024; is_pqc = 1; } - else if (!ascii_strcasecmp (string, "dil3")) + else if (!ascii_strcasecmp (string, "mlk768") && RFC9980) { - algo = PUBKEY_ALGO_DIL3_25519; + curve = "ietf25"; + algo = PUBKEY_ALGO_MLK768_25519; + size = 768; is_pqc = 1; + keyversion = 6; + } + else if (!ascii_strcasecmp (string, "mlk768_np384") && RFC9980) + { + curve = "nistp384"; + algo = PUBKEY_ALGO_MLK768_NP384; + size = 768; + is_pqc = 1; + keyversion = 6; + } + else if (!ascii_strcasecmp (string, "mlk768_bp384") && RFC9980) + { + curve = "bp384"; + algo = PUBKEY_ALGO_MLK768_BP384; + size = 768; + is_pqc = 1; + keyversion = 6; + } + else if (!ascii_strcasecmp (string, "mlk1024") && RFC9980) + { + curve = "X448"; + algo = PUBKEY_ALGO_MLK1024_448; + size = 1024; + is_pqc = 1; + keyversion = 6; + } + else if (!ascii_strcasecmp (string, "mlk1024_np521") && RFC9980) + { + curve = "nistp521"; + algo = PUBKEY_ALGO_MLK1024_NP521; + size = 1024; + is_pqc = 1; + keyversion = 6; + } + else if (!ascii_strcasecmp (string, "mlk1024_bp512") && RFC9980) + { + curve = "bp512"; + algo = PUBKEY_ALGO_MLK1024_BP512; + size = 1024; + is_pqc = 1; + keyversion = 6; + } + else if (!ascii_strcasecmp (string, "ietf25") && RFC9980) + { + curve = "Ed25519"; + algo = PUBKEY_ALGO_X25519; + size = 255; + keyversion = 6; + } + else if (!ascii_strcasecmp (string, "ietf27") && RFC9980) + { + curve = "Ed25519"; + algo = PUBKEY_ALGO_ED25519; + size = 255; + keyversion = 6; } - else if (!ascii_strcasecmp (string, "dil5")) + else if (!ascii_strcasecmp (string, "mld65") && RFC9980) { - algo = PUBKEY_ALGO_DIL5_448; + algo = PUBKEY_ALGO_MLD65_25519; is_pqc = 1; } - else if (!ascii_strcasecmp (string, "sphinx") - || !ascii_strcasecmp (string, "sphinx_sha2")) + else if (!ascii_strcasecmp (string, "mld87") && RFC9980) { - algo = PUBKEY_ALGO_SPHINX_SHA2; + algo = PUBKEY_ALGO_MLD87_448; is_pqc = 1; } else if ((curve = openpgp_is_curve_supported (string, &algo, &size))) @@ -4295,7 +4470,7 @@ parse_key_parameter_part (ctrl_t ctrl, if (!keyuse) { if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA - || algo == PUBKEY_ALGO_DSA) + || algo == PUBKEY_ALGO_DSA || algo == PUBKEY_ALGO_ED25519) keyuse = PUBKEY_USAGE_SIG; else if (algo == PUBKEY_ALGO_RSA) keyuse = for_subkey? PUBKEY_USAGE_ENC : PUBKEY_USAGE_SIG; @@ -4303,7 +4478,7 @@ parse_key_parameter_part (ctrl_t ctrl, keyuse = PUBKEY_USAGE_ENC; } else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA - || algo == PUBKEY_ALGO_DSA) + || algo == PUBKEY_ALGO_DSA || algo == PUBKEY_ALGO_ED25519) { keyuse &= ~PUBKEY_USAGE_ENC; /* Forbid encryption. */ } @@ -4332,7 +4507,9 @@ parse_key_parameter_part (ctrl_t ctrl, } /* Ed448, X448 and the PQC algos must only be used as v5 keys. */ - if (is_448 || is_pqc) + if (keyversion == 6) + ; + else if (is_448 || is_pqc) { if (keyversion == 4) log_info (_("WARNING: v4 is specified, but overridden by v5.\n")); @@ -4484,6 +4661,8 @@ parse_key_parameter_string (ctrl_t ctrl, string = FUTURE_STD_KEY_PARAM; else if (!ascii_strcasecmp (string, "pqc")) string = PQC_STD_KEY_PARAM; + else if (!ascii_strcasecmp (string, "pqc9980") && RFC9980) + string = PQC9980_STD_KEY_PARAM; else if (!ascii_strcasecmp (string, "card")) string = "card/cert,sign+card/encr"; @@ -5598,6 +5777,7 @@ quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr, || !ascii_strcasecmp (algostr, "future-default") || !ascii_strcasecmp (algostr, "futuredefault") || !ascii_strcasecmp (algostr, "pqc") + || (!ascii_strcasecmp (algostr, "pqc9980") && RFC9980) || !ascii_strcasecmp (algostr, "card")) && (!*usagestr || !ascii_strcasecmp (usagestr, "default") || !strcmp (usagestr, "-"))) diff --git a/g10/keyid.c b/g10/keyid.c index 7bd1771a6..2cb0a4347 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -103,6 +103,7 @@ char * pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize) { const char *prefix = NULL; + const char *fixed = NULL; int dual = 0; char *curve; const char *name; @@ -119,16 +120,23 @@ pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize) case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: prefix = "rsa"; break; + case PUBKEY_ALGO_KYBER: prefix = "ky"; dual = 1; break; case PUBKEY_ALGO_ELGAMAL_E: prefix = "elg"; break; case PUBKEY_ALGO_DSA: prefix = "dsa"; break; case PUBKEY_ALGO_ELGAMAL: prefix = "xxx"; break; case PUBKEY_ALGO_ECDH: case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: prefix = ""; break; - case PUBKEY_ALGO_KYBER: prefix = "ky"; dual = 1; break; - case PUBKEY_ALGO_DIL3_25519: prefix = "dil3"; break; - case PUBKEY_ALGO_DIL5_448: prefix = "dil5"; break; - case PUBKEY_ALGO_SPHINX_SHA2: prefix = "sphinx_sha2"; break; + case PUBKEY_ALGO_X25519: fixed = "ietf25"; break; + case PUBKEY_ALGO_ED25519: fixed = "ietf27"; break; + case PUBKEY_ALGO_MLD65_25519: fixed = "mld65"; break; + case PUBKEY_ALGO_MLD87_448: fixed = "mld87"; break; + case PUBKEY_ALGO_MLK768_25519: fixed = "mlk768"; break; + case PUBKEY_ALGO_MLK768_NP384: fixed = "mlk768_np384"; break; + case PUBKEY_ALGO_MLK768_BP384: fixed = "mlk768_bp384"; break; + case PUBKEY_ALGO_MLK1024_448: fixed = "mlk1024"; break; + case PUBKEY_ALGO_MLK1024_NP521:fixed = "mlk1024_np521"; break; + case PUBKEY_ALGO_MLK1024_BP512:fixed = "mlk1024_bp512"; break; } @@ -162,6 +170,8 @@ pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize) snprintf (buffer, bufsize, "E_error"); xfree (curve); } + else if (fixed) + snprintf (buffer, bufsize, "%s", fixed); else snprintf (buffer, bufsize, "unknown_%u", (unsigned int)pk->pubkey_algo); @@ -312,11 +322,13 @@ compare_pubkey_string (const char *astr, const char *bstr) -/* Hash a public key and allow to specify the to be used format. - * Note that if the v5 format is requested for a v4 key, a 0x04 as - * version is hashed instead of the 0x05. */ +/* Hash a public key and allow to specify the to be used format. Note + * that if the v5 format is requested for a v4 key, a 0x04 as version + * is hashed instead of the 0x05. If SIG is not NULL it is tested for + * a version 6 signature and used appropiate. */ static void -do_hash_public_key (gcry_md_hd_t md, PKT_public_key *pk, int use_v5) +do_hash_public_key (gcry_md_hd_t md, PKT_public_key *pk, int force_v5, + PKT_signature *sig) { unsigned int n; unsigned int nn[PUBKEY_MAX_NPKEY]; @@ -326,7 +338,7 @@ do_hash_public_key (gcry_md_hd_t md, PKT_public_key *pk, int use_v5) size_t nbytes; int npkey = pubkey_get_npkey (pk->pubkey_algo); - n = use_v5? 10 : 6; + n = (force_v5 || pk->version >= 5)? 10 : 6; /* FIXME: We can avoid the extra malloc by calling only the first mpi_print here which computes the required length and calling the real mpi_print only at the end. The speed advantage would only be @@ -427,12 +439,24 @@ do_hash_public_key (gcry_md_hd_t md, PKT_public_key *pk, int use_v5) } } - if (use_v5) + if (force_v5 || pk->version == 5) { gcry_md_putc ( md, 0x9a ); /* ctb */ gcry_md_putc ( md, n >> 24 ); /* 4 byte length header (upper bits) */ gcry_md_putc ( md, n >> 16 ); } + else if (pk->version == 6) + { + const void *salt; + + if (sig && sig->version == 6 && sig->salt + && gcry_mpi_get_flag (sig->salt, GCRYMPI_FLAG_OPAQUE) + && (salt = gcry_mpi_get_opaque (sig->salt, &nbits)) && nbits) + gcry_md_write (md, salt, (nbits+7)/8); + gcry_md_putc ( md, 0x9b ); /* ctb */ + gcry_md_putc ( md, n >> 24 ); /* 4 byte length header (upper bits) */ + gcry_md_putc ( md, n >> 16 ); + } else { gcry_md_putc ( md, 0x99 ); /* ctb */ @@ -447,7 +471,7 @@ do_hash_public_key (gcry_md_hd_t md, PKT_public_key *pk, int use_v5) gcry_md_putc ( md, pk->pubkey_algo ); - if (use_v5) /* Hash the 32 bit length */ + if (force_v5 || pk->version >= 5) /* Hash the 32 bit length */ { n -= 10; gcry_md_putc ( md, n >> 24 ); @@ -475,11 +499,12 @@ do_hash_public_key (gcry_md_hd_t md, PKT_public_key *pk, int use_v5) /* Hash a public key. This function is useful for v4 and v5 - * fingerprints and for v3 or v4 key signing. */ + * fingerprints and for v3 or v4 key signing. To partly support v6 + * signatures SIG must be given. */ void -hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) +hash_public_key (gcry_md_hd_t md, PKT_public_key *pk, PKT_signature *sig) { - do_hash_public_key (md, pk, (pk->version == 5)); + do_hash_public_key (md, pk, 0, sig); } @@ -743,16 +768,16 @@ compute_fingerprint (PKT_public_key *pk) gcry_md_hd_t md; size_t len; - if (gcry_md_open (&md, pk->version == 5 ? GCRY_MD_SHA256 : GCRY_MD_SHA1, 0)) + if (gcry_md_open (&md, pk->version >= 5 ? GCRY_MD_SHA256 : GCRY_MD_SHA1, 0)) BUG (); - hash_public_key (md, pk); + hash_public_key (md, pk, NULL); gcry_md_final (md); dp = gcry_md_read (md, 0); len = gcry_md_get_algo_dlen (gcry_md_get_algo (md)); log_assert (len <= MAX_FINGERPRINT_LEN); memcpy (pk->fpr, dp, len); pk->fprlen = len; - if (pk->version == 5) + if (pk->version >= 5) { pk->keyid[0] = buf32_to_u32 (dp); pk->keyid[1] = buf32_to_u32 (dp+4); @@ -1120,12 +1145,12 @@ v5_fingerprint_from_pk (PKT_public_key *pk, byte *array, size_t *ret_len) const byte *dp; gcry_md_hd_t md; - if (pk->version == 5) + if (pk->version >= 5) return fingerprint_from_pk (pk, array, ret_len); if (gcry_md_open (&md, GCRY_MD_SHA256, 0)) BUG (); - do_hash_public_key (md, pk, 1); + do_hash_public_key (md, pk, 5, NULL); gcry_md_final (md); dp = gcry_md_read (md, 0); if (!array) @@ -1331,9 +1356,11 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array, int get_second) gcry_sexp_t s_pkey; if (DBG_PACKET) - log_debug ("get_keygrip for public key%s\n", get_second?" (second)":""); + log_debug ("get_keygrip for public key%s (v%d,a=%d)\n", + get_second?" (second)":"", pk->version, pk->pubkey_algo); - if (get_second && pk->pubkey_algo != PUBKEY_ALGO_KYBER) + if (get_second && !(pk->pubkey_algo == PUBKEY_ALGO_KYBER + || IS_PUBKEY_ALGO_MLK (pk->pubkey_algo))) return gpg_error (GPG_ERR_FALSE); switch (pk->pubkey_algo) @@ -1383,6 +1410,18 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array, int get_second) } break; + case PUBKEY_ALGO_X25519: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve ietf25)(q%m)))", + pk->pkey[0]); + break; + + case PUBKEY_ALGO_ED25519: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve Ed25519)" + "(flags eddsa)(q%m)))", pk->pkey[0]); + break; + case PUBKEY_ALGO_KYBER: if (get_second) { @@ -1410,6 +1449,72 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array, int get_second) } break; + case PUBKEY_ALGO_MLK768_25519: + if (get_second) + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(kyber768(p%m)))", + pk->pkey[1]); + else + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve ietf25)(q%m)))", + pk->pkey[0]); + break; + + case PUBKEY_ALGO_MLK768_NP384: + if (get_second) + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(kyber768(p%m)))", + pk->pkey[1]); + else + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve nistp384)(q%m)))", + pk->pkey[0]); + break; + + case PUBKEY_ALGO_MLK768_BP384: + if (get_second) + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(kyber768(p%m)))", + pk->pkey[1]); + else + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve bp384)(q%m)))", + pk->pkey[0]); + break; + + case PUBKEY_ALGO_MLK1024_448: + if (get_second) + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(kyber1024(p%m)))", + pk->pkey[1]); + else + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve X448)(q%m)))", + pk->pkey[0]); + break; + + case PUBKEY_ALGO_MLK1024_NP521: + if (get_second) + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(kyber1024(p%m)))", + pk->pkey[1]); + else + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve nistp521)(q%m)))", + pk->pkey[0]); + break; + + case PUBKEY_ALGO_MLK1024_BP512: + if (get_second) + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(kyber1024(p%m)))", + pk->pkey[1]); + else + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve bp512)(q%m)))", + pk->pkey[0]); + break; + default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; @@ -1451,16 +1556,19 @@ hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip) char *buf; unsigned char grip[KEYGRIP_LEN]; unsigned char grip2[KEYGRIP_LEN]; + int composite = 0; *r_grip = NULL; err = keygrip_from_pk (pk, grip, 0); if (!err) { - if (pk->pubkey_algo == PUBKEY_ALGO_KYBER) + if (pk->pubkey_algo == PUBKEY_ALGO_KYBER + || IS_PUBKEY_ALGO_MLK (pk->pubkey_algo)) { err = keygrip_from_pk (pk, grip2, 1); if (err) goto leave; + composite = 1; buf = xtrymalloc (2 * KEYGRIP_LEN * 2 + 1 + 1); } else @@ -1473,7 +1581,7 @@ hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip) } bin2hex (grip, KEYGRIP_LEN, buf); - if (pk->pubkey_algo == PUBKEY_ALGO_KYBER) + if (composite) { buf[2*KEYGRIP_LEN] = ','; bin2hex (grip2, KEYGRIP_LEN, buf+2*KEYGRIP_LEN+1); diff --git a/g10/keylist.c b/g10/keylist.c index aabffe9bb..a5adcc007 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -2007,6 +2007,7 @@ print_compliance_flags (PKT_public_key *pk, if (!keylength) keylength = nbits_from_pk (pk); + /* FIXME:CO_FIPS */ if (pk->version == 5) { es_fputs (gnupg_status_compliance_flag (CO_GNUPG), es_stdout); diff --git a/g10/main.h b/g10/main.h index a93e6364d..1f761e6da 100644 --- a/g10/main.h +++ b/g10/main.h @@ -23,6 +23,7 @@ #include "../common/types.h" #include "../common/iobuf.h" #include "../common/util.h" +#include "../common/name-value.h" #include "keydb.h" #include "keyedit.h" @@ -416,6 +417,7 @@ const char *impex_filter_getval (void *cookie, const char *propname); gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, kbnode_t sec_keyblock, int batch, int force, int only_marked); +nvc_t seckey_packet_to_nvc (PKT_public_key *pk); int collapse_uids (kbnode_t *keyblock); int collapse_subkeys (kbnode_t *keyblock); diff --git a/g10/mainproc.c b/g10/mainproc.c index fcbf6c648..068c6d4d7 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -581,13 +581,17 @@ proc_encrypted (CTX c, PACKET *pkt) unsigned int compliance_de_vs = 0; enum gcry_cipher_modes ciphermode; int unknown_ciphermode; + int seipdv2_cipher_algo = 0; if (pkt) { - if (pkt->pkttype == PKT_ENCRYPTED_AEAD) + if (pkt->pkttype == PKT_ENCRYPTED_OCB) c->seen_pkt_encrypted_aead = 1; if (pkt->pkttype == PKT_ENCRYPTED_MDC) - c->seen_pkt_encrypted_mdc = 1; + { + c->seen_pkt_encrypted_mdc = 1; + seipdv2_cipher_algo = pkt->pkt.encrypted->cipher_algo; + } } else /* No PKT indicates the add-recipients mode. */ log_assert (c->ctrl->modify_recipients); @@ -627,7 +631,8 @@ proc_encrypted (CTX c, PACKET *pkt) else if (c->sesenc_list) { c->dek = xmalloc_secure_clear (sizeof *c->dek); - result = get_session_key (c->ctrl, c->sesenc_list, c->dek); + result = get_session_key (c->ctrl, c->sesenc_list, c->dek, + seipdv2_cipher_algo); if (is_status_enabled ()) { struct seskey_enc_list *list; @@ -953,6 +958,7 @@ proc_encrypted (CTX c, PACKET *pkt) /* The --require-compliance option allows one to simplify decryption in * de-vs compliance mode by just looking at the exit status. */ + /* FIXME:CO_FIPS */ if (opt.flags.require_compliance && opt.compliance == CO_DE_VS && compliance_de_vs != (4|2|1) @@ -1020,14 +1026,25 @@ proc_plaintext( CTX c, PACKET *pkt ) { if (n->pkt->pkttype == PKT_ONEPASS_SIG) { + PKT_onepass_sig *ops = n->pkt->pkt.onepass_sig; + const void *salt; + unsigned int saltlen; + /* The onepass signature case. */ - if (n->pkt->pkt.onepass_sig->digest_algo) + if (ops->digest_algo) { if (!opt.skip_verify) - gcry_md_enable (c->mfx.md, - n->pkt->pkt.onepass_sig->digest_algo); + gcry_md_enable (c->mfx.md, ops->digest_algo); any = 1; + + /* Double-Urgs: To properly support that salt thingy we + * would have to have a hash context for each signature. */ + if (ops->version == 6 && ops->salt + && gcry_mpi_get_flag (ops->salt, GCRYMPI_FLAG_OPAQUE) + && (salt = gcry_mpi_get_opaque (ops->salt, &saltlen)) + && saltlen) + gcry_md_write (c->mfx.md, salt, (saltlen+7)/8); } } else if (n->pkt->pkttype == PKT_GPG_CONTROL @@ -1749,7 +1766,7 @@ do_proc_packets (CTX c, iobuf_t a, int keep_dek_and_list) case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: - case PKT_ENCRYPTED_AEAD:proc_encrypted (c, pkt); break; + case PKT_ENCRYPTED_OCB: proc_encrypted (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; default: newpkt = 0; break; } @@ -1765,7 +1782,7 @@ do_proc_packets (CTX c, iobuf_t a, int keep_dek_and_list) case PKT_PUBKEY_ENC: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: - case PKT_ENCRYPTED_AEAD: + case PKT_ENCRYPTED_OCB: write_status_text( STATUS_UNEXPECTED, "0" ); rc = GPG_ERR_UNEXPECTED; goto leave; @@ -1805,7 +1822,7 @@ do_proc_packets (CTX c, iobuf_t a, int keep_dek_and_list) case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: - case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break; + case PKT_ENCRYPTED_OCB: proc_encrypted (c, pkt); break; case PKT_PLAINTEXT: proc_plaintext (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break; @@ -1833,7 +1850,7 @@ do_proc_packets (CTX c, iobuf_t a, int keep_dek_and_list) case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: - case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break; + case PKT_ENCRYPTED_OCB: proc_encrypted (c, pkt); break; case PKT_PLAINTEXT: proc_plaintext (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break; @@ -2543,6 +2560,20 @@ check_sig_and_print (CTX c, kbnode_t node) print_matching_notations (sig); } + /* Print a note if a signature salt was used despite of a + * non-vulnerable hash algo. */ + if (sig && sig->salt && !(sig->digest_algo == DIGEST_ALGO_SHA1 + || sig->digest_algo == DIGEST_ALGO_RMD160 + || sig->digest_algo == DIGEST_ALGO_MD5)) + { + unsigned int saltlen; + + if (opt.verbose + && gcry_mpi_get_flag (sig->salt, GCRYMPI_FLAG_OPAQUE) + && gcry_mpi_get_opaque (sig->salt, &saltlen) && saltlen) + log_info ("Note: signature has a deliberate covert channel\n"); + } + /* Fill PKSTRBUF with the algostring in case we later need it. */ if (pk) pubkey_string (pk, pkstrbuf, sizeof pkstrbuf); @@ -2663,7 +2694,7 @@ check_sig_and_print (CTX c, kbnode_t node) gnupg_status_compliance_flag (CO_DE_VS), NULL); else if (opt.flags.require_compliance - && opt.compliance == CO_DE_VS) + && (opt.compliance == CO_DE_VS||opt.compliance == CO_FIPS)) { log_error (_("operation forced to fail due to" " unfulfilled compliance rules\n")); @@ -2743,7 +2774,10 @@ proc_tree (CTX c, kbnode_t node) /* Check all signatures. */ if (!c->any.data) { + PKT_onepass_sig *ops = node->pkt->pkt.onepass_sig; int use_textmode = 0; + const void *salt; + unsigned int saltlen; free_md_filter_context (&c->mfx); /* Prepare to create all requested message digests. */ @@ -2759,6 +2793,13 @@ proc_tree (CTX c, kbnode_t node) if (n1 && n1->pkt->pkt.onepass_sig->sig_class == 0x01) use_textmode = 1; + /* Double-Urgs: To properly support that salt thingy we + * would have to have a hash context for each signature. */ + if (ops->version == 6 && ops->salt + && gcry_mpi_get_flag (ops->salt, GCRYMPI_FLAG_OPAQUE) + && (salt = gcry_mpi_get_opaque (ops->salt, &saltlen)) && saltlen) + gcry_md_write (c->mfx.md, salt, (saltlen+7)/8); + /* Ask for file and hash it. */ if (c->sigs_only) { diff --git a/g10/misc.c b/g10/misc.c index dc8b6f8f8..9f02b5ed3 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -641,6 +641,11 @@ openpgp_aead_test_algo (aead_algo_t algo) case AEAD_ALGO_NONE: break; + case AEAD_ALGO_GCM: + if (RFC9980) + return 0; + break; + case AEAD_ALGO_EAX: case AEAD_ALGO_OCB: return 0; @@ -661,6 +666,7 @@ openpgp_aead_algo_name (aead_algo_t algo) case AEAD_ALGO_NONE: break; case AEAD_ALGO_EAX: return gnupg_cipher_mode_name (GCRY_CIPHER_MODE_EAX); case AEAD_ALGO_OCB: return gnupg_cipher_mode_name (GCRY_CIPHER_MODE_OCB); + case AEAD_ALGO_GCM: return gnupg_cipher_mode_name (GCRY_CIPHER_MODE_GCM); } return "?"; @@ -687,6 +693,15 @@ openpgp_aead_algo_info (aead_algo_t algo, enum gcry_cipher_modes *r_mode, *r_noncelen = 16; break; + case AEAD_ALGO_GCM: + if (RFC9980) + { + *r_mode = GCRY_CIPHER_MODE_GCM; + *r_noncelen = 12; + break; + } + /* FALLTHRU */ + default: log_error ("unsupported AEAD algo %d\n", algo); return gpg_error (GPG_ERR_INV_CIPHER_MODE); @@ -752,6 +767,26 @@ openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use) case PUBKEY_ALGO_KYBER: ga = GCRY_PK_KEM; break; + case PUBKEY_ALGO_ED25519: + if (RFC9980) + ga = GCRY_PK_EDDSA; + break; + + case PUBKEY_ALGO_X25519: + if (RFC9980) + ga = GCRY_PK_KEM; + break; + + case PUBKEY_ALGO_MLK768_25519: + case PUBKEY_ALGO_MLK768_NP384: + case PUBKEY_ALGO_MLK768_BP384: + case PUBKEY_ALGO_MLK1024_448: + case PUBKEY_ALGO_MLK1024_NP521: + case PUBKEY_ALGO_MLK1024_BP512: + if (RFC9980) + ga = GCRY_PK_KEM; + break; + default: break; } @@ -802,14 +837,27 @@ openpgp_pk_algo_usage ( int algo ) case PUBKEY_ALGO_EDDSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; break; + case PUBKEY_ALGO_ED25519: + if (RFC9980) + use= PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; + break; + case PUBKEY_ALGO_X25519: + if (RFC9980) + use= PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; + break; case PUBKEY_ALGO_KYBER: + case PUBKEY_ALGO_MLK768_25519: + case PUBKEY_ALGO_MLK768_NP384: + case PUBKEY_ALGO_MLK768_BP384: + case PUBKEY_ALGO_MLK1024_448: + case PUBKEY_ALGO_MLK1024_NP521: + case PUBKEY_ALGO_MLK1024_BP512: use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; break; - case PUBKEY_ALGO_DIL3_25519: - case PUBKEY_ALGO_DIL5_448: - case PUBKEY_ALGO_SPHINX_SHA2: + case PUBKEY_ALGO_MLD65_25519: + case PUBKEY_ALGO_MLD87_448: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG; break; @@ -837,13 +885,21 @@ openpgp_pk_algo_name (pubkey_algo_t algo) case PUBKEY_ALGO_ECDSA: return "ECDSA"; case PUBKEY_ALGO_EDDSA: return "EDDSA"; case PUBKEY_ALGO_KYBER: return "Kyber"; + case PUBKEY_ALGO_X25519: return "ietf25"; + case PUBKEY_ALGO_ED25519: return "ietf27"; + case PUBKEY_ALGO_MLK768_25519: return "mlk768"; + case PUBKEY_ALGO_MLK768_NP384: return "mlk768_np384"; + case PUBKEY_ALGO_MLK768_BP384: return "mlk768_bp384"; + case PUBKEY_ALGO_MLK1024_448: return "mlk1024"; + case PUBKEY_ALGO_MLK1024_NP521:return "mlk1024_np521"; + case PUBKEY_ALGO_MLK1024_BP512:return "mlk1024_bp512"; default: return "?"; } } /* Explicit mapping of OpenPGP digest algos to Libgcrypt. */ -/* FIXME: We do not yes use it everywhere. */ +/* FIXME: We do not yet use it everywhere. */ enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo) { @@ -1450,6 +1506,10 @@ compliance_failure(void) case CO_DE_VS: ver="DE-VS applications"; break; + + case CO_FIPS: + ver="FIPS applications"; + break; } log_info(_("this message may not be usable by %s\n"),ver); @@ -1742,7 +1802,12 @@ pubkey_get_npkey (pubkey_algo_t algo) case PUBKEY_ALGO_ELGAMAL: return 3; case PUBKEY_ALGO_EDDSA: return 2; case PUBKEY_ALGO_KYBER: return 3; - default: return 0; + case PUBKEY_ALGO_X25519: return 1; + case PUBKEY_ALGO_ED25519: return 1; + default: + if (IS_PUBKEY_ALGO_MLK (algo)) + return 2; + return 0; } } @@ -1763,7 +1828,12 @@ pubkey_get_nskey (pubkey_algo_t algo) case PUBKEY_ALGO_ELGAMAL: return 4; case PUBKEY_ALGO_EDDSA: return 3; case PUBKEY_ALGO_KYBER: return 5; - default: return 0; + case PUBKEY_ALGO_X25519: return 2; + case PUBKEY_ALGO_ED25519: return 2; + default: + if (IS_PUBKEY_ALGO_MLK (algo)) + return 4; + return 0; } } @@ -1782,6 +1852,7 @@ pubkey_get_nsig (pubkey_algo_t algo) case PUBKEY_ALGO_ECDSA: return 2; case PUBKEY_ALGO_ELGAMAL: return 2; case PUBKEY_ALGO_EDDSA: return 2; + case PUBKEY_ALGO_ED25519: return 1; default: return 0; } } @@ -1803,7 +1874,11 @@ pubkey_get_nenc (pubkey_algo_t algo) case PUBKEY_ALGO_ELGAMAL: return 2; case PUBKEY_ALGO_EDDSA: return 0; case PUBKEY_ALGO_KYBER: return 3; - default: return 0; + case PUBKEY_ALGO_X25519: return 2; + default: + if (IS_PUBKEY_ALGO_MLK (algo)) + return 3; + return 0; } } diff --git a/g10/options.h b/g10/options.h index d13ac3f3b..9c81d083e 100644 --- a/g10/options.h +++ b/g10/options.h @@ -284,6 +284,7 @@ struct unsigned int allow_old_cipher_algos:1; unsigned int allow_weak_digest_algos:1; unsigned int allow_weak_key_signatures:1; + unsigned int allow_9980:1; /* Use the RFC9980 macro in conditions. */ unsigned int large_rsa:1; unsigned int disable_signer_uid:1; unsigned int include_key_block:1; @@ -415,12 +416,14 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; /* Compliance test macros. */ -#define GNUPG (opt.compliance==CO_GNUPG || opt.compliance==CO_DE_VS) +#define GNUPG (opt.compliance==CO_GNUPG || opt.compliance==CO_DE_VS \ + || opt.compliance==CO_FIPS) #define RFC2440 (opt.compliance==CO_RFC2440) #define RFC4880 (opt.compliance==CO_RFC4880) #define PGP7 (opt.compliance==CO_PGP7) #define PGP8 (opt.compliance==CO_PGP8) #define PGPX (PGP7 || PGP8) +#define RFC9980 (opt.flags.allow_9980) /* Various option flags. Note that there should be no common string names between the IMPORT_ and EXPORT_ flags as they can be mixed in @@ -445,6 +448,7 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; #define IMPORT_BULK (1<<17) #define IMPORT_IGNORE_ATTRIBUTES (1<<18) #define IMPORT_FORCE_UPDATE (1<<19) +#define IMPORT_DEBUG_ACCEPT_NO_UID (1<<20) #define EXPORT_LOCAL_SIGS (1<<0) #define EXPORT_ATTRIBUTES (1<<1) @@ -483,6 +487,7 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; #define LIST_SHOW_OWNERTRUST (1<<19) #define LIST_SHOW_TRUSTSIG (1<<20) #define LIST_SHOW_HIDDEN_NOTATIONS (1<<21) +#define LIST_DEBUG_SHOW_SEXP (1<<22) /*Only for parse-packet*/ #define VERIFY_SHOW_PHOTOS (1<<0) #define VERIFY_SHOW_POLICY_URLS (1<<1) diff --git a/g10/packet.h b/g10/packet.h index fb5f8aca8..e335968ff 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -157,7 +157,7 @@ typedef struct { typedef struct { /* The 64-bit keyid. */ u32 keyid[2]; - /* The packet's version. Currently, only version 3 is defined. */ + /* The packet's version. This may be 3 or 6. */ byte version; /* The algorithm used for the public key encryption scheme. */ byte pubkey_algo; @@ -166,6 +166,10 @@ typedef struct { /* Whether to hide the key id. This value is not directly serialized. */ byte throw_keyid; + /* Fingerprint of the key and the used length (i.e. the extended + * keyid). Note that this is only used for version 6 packets. */ + byte fprlen; + byte fpr[MAX_FINGERPRINT_LEN]; /* The encrypted session key. */ gcry_mpi_t data[PUBKEY_MAX_NENC]; } PKT_pubkey_enc; @@ -200,20 +204,22 @@ struct pubkey_enc_info_item /* A one-pass signature packet as defined in RFC 4880, Section 5.4. All fields are serialized. */ typedef struct { - u32 keyid[2]; /* The 64-bit keyid */ - /* The signature's classification (RFC 4880, Section 5.2.1). */ - byte sig_class; - byte digest_algo; /* algorithm used for digest */ - byte pubkey_algo; /* algorithm used for public key scheme */ - /* A message can be signed by multiple keys. In this case, there - are n one-pass signature packets before the message to sign and - n signatures packets after the message. It is conceivable that - someone wants to not only sign the message, but all of the - signatures. Now we need to distinguish between signing the - message and signing the message plus the surrounding - signatures. This is the point of this flag. If set, it means: - I sign all of the data starting at the next packet. */ - byte last; + u32 keyid[2]; /* The 64-bit keyid */ + /* The signature's classification (RFC 4880, Section 5.2.1). */ + byte sig_class; + byte digest_algo; /* algorithm used for digest */ + byte pubkey_algo; /* algorithm used for public key scheme */ + /* A message can be signed by multiple keys. In this case, there + * are n one-pass signature packets before the message to sign and n + * signatures packets after the message. It is conceivable that + * someone wants to not only sign the message, but all of the + * signatures. Now we need to distinguish between signing the + * message and signing the message plus the surrounding signatures. + * This is the point of this flag. If set, it means: I sign all of + * the data starting at the next packet. */ + byte last; + byte version; /* The version of the packet. */ + gcry_mpi_t salt; /* The v6 sigature salt. */ } PKT_onepass_sig; @@ -303,6 +309,7 @@ typedef struct * already been sanitized. */ subpktarea_t *hashed; /* All subpackets with hashed data (v4, v5 only). */ subpktarea_t *unhashed; /* Ditto for unhashed data. */ + gcry_mpi_t salt; /* The v6 sigature salt. */ /* First 2 bytes of the digest. (Serialized. Note: this is not automatically filled in when serializing a signature!) */ byte digest_start[2]; @@ -465,6 +472,7 @@ typedef struct /* Fingerprint of the key. Only valid if FPRLEN is not 0. */ byte fpr[MAX_FINGERPRINT_LEN]; prefitem_t *prefs; /* list of preferences (may be NULL) */ + prefitem_t *dks_prefs; /* direct key signature prefs (may be NULL) */ struct { unsigned int mdc:1; /* MDC feature set. */ @@ -554,6 +562,12 @@ typedef struct { was encoded using partial body length headers (new format). Note: this is ignored when encrypting. */ byte is_partial; + /* This flag is only set if this is a SEIPD packet (tag 18). It is + * neither set for a SED packet (tag 9) or a OCB Encrypted Data + * packet (tag 20). */ + byte seipd; + /* The version of the packet. */ + byte version; /* If 0, MDC is disabled. Otherwise, the MDC method that was used (only DIGEST_ALGO_SHA1 has ever been defined). */ byte mdc_method; @@ -994,7 +1008,8 @@ gpg_error_t check_signature (ctrl_t ctrl, /*-- pubkey-enc.c --*/ -gpg_error_t get_session_key (ctrl_t ctrl, struct seskey_enc_list *k, DEK *dek); +gpg_error_t get_session_key (ctrl_t ctrl, struct seskey_enc_list *k, DEK *dek, + int seipdv2_cipher_algo); gpg_error_t get_override_session_key (DEK *dek, const char *string); /*-- compress.c --*/ diff --git a/g10/parse-packet.c b/g10/parse-packet.c index a9d6dd264..aaa96b3c5 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -38,6 +38,11 @@ #include "../common/mbox-util.h" +/* It is better to always allow parsing the 9980 packets but we keep + * the macro for documentaion reasons. */ +#undef RFC9980 +#define RFC9980 1 + static int mpi_print_mode; static int list_mode; static estream_t listfp; @@ -81,9 +86,9 @@ static int parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb); static int parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb, int partial); -static gpg_error_t parse_encrypted_aead (IOBUF inp, int pkttype, - unsigned long pktlen, PACKET *packet, - int partial); +static gpg_error_t parse_encrypted_ocb (iobuf_t inp, int pkttype, + unsigned long pktlen, PACKET *packet, + int partial); static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb); static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen, @@ -201,9 +206,9 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure) * memory (allocated using gcry_xmalloc_secure). */ static gpg_error_t -read_octet_string (iobuf_t inp, unsigned long *pktlen, - unsigned int nlength, unsigned int nbytes, - int secure, gcry_mpi_t *r_data) +read_raw_octet_string (iobuf_t inp, unsigned long *pktlen, + unsigned int nlength, unsigned int nbytes, + int secure, gcry_mpi_t *r_data) { gpg_error_t err; int c, i; @@ -256,7 +261,7 @@ read_octet_string (iobuf_t inp, unsigned long *pktlen, if (nbytes > *pktlen) { - log_error ("octet string larger than packet (%u octets)\n", nbytes); + log_error ("octet string larger than packet (%u/%lu)\n", nbytes, *pktlen); err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } @@ -282,7 +287,6 @@ read_octet_string (iobuf_t inp, unsigned long *pktlen, } *r_data = gcry_mpi_set_opaque (NULL, buf, nbytes*8); - gcry_mpi_set_flag (*r_data, GCRYMPI_FLAG_USER2); return 0; leave: @@ -291,6 +295,22 @@ read_octet_string (iobuf_t inp, unsigned long *pktlen, } +/* Read a simple octet string (SOS) and mark the result as such a + * value. For details see read_raw_octet_string. */ +static gpg_error_t +read_sos_octet_string (iobuf_t inp, unsigned long *pktlen, + unsigned int nlength, unsigned int nbytes, + int secure, gcry_mpi_t *r_data) +{ + gpg_error_t err; + + err = read_raw_octet_string (inp, pktlen, nlength, nbytes, secure, r_data); + if (!err) + gcry_mpi_set_flag (*r_data, GCRYMPI_FLAG_USER2); + return err; +} + + /* Read an external representation of an SOS and return the opaque MPI with GCRYMPI_FLAG_USER2. The external format is a 16-bit unsigned value stored in network byte order giving information for the @@ -747,7 +767,7 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos, again: log_assert (!pkt->pkt.generic); - if (retpos || list_mode) + if (retpos || list_mode || opt.verbose > 1) { pos = iobuf_tell (inp); if (retpos) @@ -850,7 +870,7 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos, case PKT_PLAINTEXT: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: - case PKT_ENCRYPTED_AEAD: + case PKT_ENCRYPTED_OCB: case PKT_COMPRESSED: iobuf_set_partial_body_length_mode (inp, c & 0xff); pktlen = 0; /* To indicate partial length. */ @@ -1034,8 +1054,8 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos, case PKT_MDC: rc = parse_mdc (inp, pkttype, pktlen, pkt, new_ctb); break; - case PKT_ENCRYPTED_AEAD: - rc = parse_encrypted_aead (inp, pkttype, pktlen, pkt, partial); + case PKT_ENCRYPTED_OCB: + rc = parse_encrypted_ocb (inp, pkttype, pktlen, pkt, partial); break; case PKT_GPG_CONTROL: rc = parse_gpg_control (inp, pkttype, pktlen, pkt, partial); @@ -1049,6 +1069,17 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos, break; } + if (gpg_err_code (rc) == GPG_ERR_INV_PACKET && opt.verbose > 1) + { + log_error ("parse_packet: read error: %s\n", gpg_strerror (rc)); + print_further_info + ("packet %u off=%lu ctb=%02x tag=%d hlen=%d plen=%lu%s%s", + ctx->n_parsed_packets, + (unsigned long)pos, ctb, pkttype, hdrlen, pktlen, + partial? (new_ctb ? " partial" : " indeterminate") :"", + new_ctb? " new-ctb":""); + } + /* Store a shallow copy of certain packets in the context. */ free_packet (NULL, ctx); if (!rc && (pkttype == PKT_PUBLIC_KEY @@ -1457,6 +1488,7 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { int rc = 0; + int is_v6 = 0; int i, ndata; unsigned int n; PKT_pubkey_enc *k; @@ -1472,7 +1504,11 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, } k->version = iobuf_get_noeof (inp); pktlen--; - if (k->version != 2 && k->version != 3) + if (k->version == 2 || k->version == 3) + ; + else if (RFC9980 && k->version == 6) + is_v6 = 1; + else { log_error ("packet(%d) with unknown version %d\n", pkttype, k->version); if (list_mode) @@ -1480,10 +1516,66 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } - k->keyid[0] = read_32 (inp); - pktlen -= 4; - k->keyid[1] = read_32 (inp); - pktlen -= 4; + + if (is_v6) + { + int keyver; + + k->fprlen = iobuf_get_noeof (inp); /* Actually the 1 + fprlen. */ + pktlen--; + if (k->fprlen) + { + keyver = iobuf_get_noeof (inp); + pktlen--; k->fprlen--; + /* fprlen has now the correct value. */ + if (pktlen < k->fprlen || k->fprlen > MAX_FINGERPRINT_LEN) + { + log_error ("packet(%d) too short for fingerprint (%d)\n", + pkttype, k->fprlen); + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + if (iobuf_read (inp, k->fpr, k->fprlen) != k->fprlen) + { + log_error ("premature eof while reading " + "fingerprint from packet(%d)\n", pkttype); + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + pktlen -= k->fprlen; + if (k->fprlen == 20 && keyver == 4) + { + k->keyid[0] = buf32_to_u32 (k->fpr+12); + k->keyid[1] = buf32_to_u32 (k->fpr+16); + } + else if (k->fprlen == 32 && keyver >= 5) + { + k->keyid[0] = buf32_to_u32 (k->fpr); + k->keyid[1] = buf32_to_u32 (k->fpr+4); + } + else + { + log_error ("packet(%d) inconsistent fingerprint (v=%d,n=%d)\n", + pkttype, keyver, k->fprlen); + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + } + } + else + { + k->keyid[0] = read_32 (inp); + pktlen -= 4; + k->keyid[1] = read_32 (inp); + pktlen -= 4; + } + + if (!pktlen) + { + log_error ("packet(%d) too short for public-key algo\n", pkttype); + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } k->pubkey_algo = iobuf_get_noeof (inp); pktlen--; k->throw_keyid = 0; /* Only used as flag for build_packet. */ @@ -1518,6 +1610,38 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, if (rc) goto leave; } + else if (k->pubkey_algo == PUBKEY_ALGO_X25519 && RFC9980) + { + log_assert (ndata == 2); + /* Get the ephemeral public key. */ + rc = read_raw_octet_string (inp, &pktlen, 0, 32, 0, k->data + 0); + if (rc) + goto leave; + if (pktlen < 2) + { + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + n = iobuf_get_noeof (inp); + pktlen--; + if (list_mode) + es_fprintf (listfp, "\tlength octet: %u\n", n); + if (!is_v6) + { + k->seskey_algo = iobuf_get_noeof (inp); + if (!n || !pktlen) + { + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + pktlen--; + n--; + } + /* Get the encrypted symmetric key (keylen+8 due to AESWRAP). */ + rc = read_raw_octet_string (inp, &pktlen, 0, n, 0, k->data + 1); + if (rc) + goto leave; + } else if (k->pubkey_algo == PUBKEY_ALGO_KYBER) { log_assert (ndata == 3); @@ -1531,7 +1655,7 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, goto leave; } /* Get the Kyber ciphertext. */ - rc = read_octet_string (inp, &pktlen, 4, 0, 0, k->data + 1); + rc = read_sos_octet_string (inp, &pktlen, 4, 0, 0, k->data + 1); if (rc) goto leave; /* Get the algorithm id for the session key. */ @@ -1543,7 +1667,56 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, k->seskey_algo = iobuf_get_noeof (inp); pktlen--; /* Get the encrypted symmetric key. */ - rc = read_octet_string (inp, &pktlen, 1, 0, 0, k->data + 2); + rc = read_sos_octet_string (inp, &pktlen, 1, 0, 0, k->data + 2); + if (rc) + goto leave; + } + else if (IS_PUBKEY_ALGO_MLK (k->pubkey_algo)) + { + log_assert (ndata == 3); + /* Get the ephemeral public key. */ + rc = read_raw_octet_string + (inp, &pktlen, 0, + (k->pubkey_algo == PUBKEY_ALGO_MLK768_25519? 32 : + k->pubkey_algo == PUBKEY_ALGO_MLK768_NP384? 97 : + k->pubkey_algo == PUBKEY_ALGO_MLK768_BP384? 97 : + k->pubkey_algo == PUBKEY_ALGO_MLK1024_448? 56 : + k->pubkey_algo == PUBKEY_ALGO_MLK1024_NP521? 133 : + k->pubkey_algo == PUBKEY_ALGO_MLK1024_BP512? 129 : 0), + 0, k->data + 0); + if (rc) + goto leave; + + /* Get the Kyber ciphertext. */ + rc = read_raw_octet_string (inp, &pktlen, 0, + (IS_PUBKEY_ALGO_MLK768 (k->pubkey_algo)? + 1088 : 1568), + 0, k->data + 1); + if (rc) + goto leave; + /* Get the size octet and algo id for the session key. */ + if (pktlen < 2) + { + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + n = iobuf_get_noeof (inp); + pktlen--; + if (list_mode) + es_fprintf (listfp, "\tlength octet: %u\n", n); + if (!is_v6) + { + k->seskey_algo = iobuf_get_noeof (inp); + if (!n || !pktlen) + { + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + pktlen--; + n--; + } + /* Get the encrypted symmetric key (keylen+8 due to AESWRAP). */ + rc = read_raw_octet_string (inp, &pktlen, 0, n, 0, k->data + 2); if (rc) goto leave; } @@ -2235,8 +2408,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, PKT_signature * sig) { int md5_len = 0; - unsigned n; - int is_v4or5 = 0; + unsigned int n; + int is_v4plus = 0; int rc = 0; int i, ndata; @@ -2249,8 +2422,9 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, } sig->version = iobuf_get_noeof (inp); pktlen--; - if (sig->version == 4 || sig->version == 5) - is_v4or5 = 1; + if (sig->version == 4 || sig->version == 5 + || (sig->version == 6 && RFC9980)) + is_v4plus = 1; else if (sig->version != 2 && sig->version != 3) { log_error ("packet(%d) with unknown version %d\n", @@ -2261,7 +2435,7 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, goto leave; } - if (!is_v4or5) + if (!is_v4plus) { if (pktlen == 0) goto underflow; @@ -2272,7 +2446,7 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, goto underflow; sig->sig_class = iobuf_get_noeof (inp); pktlen--; - if (!is_v4or5) + if (!is_v4plus) { if (pktlen < 12) goto underflow; @@ -2291,12 +2465,22 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, pktlen--; sig->flags.exportable = 1; sig->flags.revocable = 1; - if (is_v4or5) /* Read subpackets. */ + if (is_v4plus) /* Read subpackets. */ { - if (pktlen < 2) - goto underflow; - n = read_16 (inp); - pktlen -= 2; /* Length of hashed data. */ + if (sig->version == 6) + { + if (pktlen < 4) + goto underflow; + n = read_32 (inp); + pktlen -= 4; /* Length of hashed data. */ + } + else + { + if (pktlen < 2) + goto underflow; + n = read_16 (inp); + pktlen -= 2; /* Length of hashed data. */ + } if (pktlen < n) goto underflow; if (n > 30000) @@ -2324,10 +2508,20 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, } pktlen -= n; } - if (pktlen < 2) - goto underflow; - n = read_16 (inp); - pktlen -= 2; /* Length of unhashed data. */ + if (sig->version == 6) + { + if (pktlen < 4) + goto underflow; + n = read_32 (inp); + pktlen -= 4; /* Length of unhashed data. */ + } + else + { + if (pktlen < 2) + goto underflow; + n = read_16 (inp); + pktlen -= 2; /* Length of unhashed data. */ + } if (pktlen < n) goto underflow; if (n > 10000) @@ -2365,7 +2559,23 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, sig->digest_start[1] = iobuf_get_noeof (inp); pktlen--; - if (is_v4or5 && sig->pubkey_algo) /* Extract required information. */ + if (sig->version == 6) + { + int saltlen; + + /* Note that we allow for a zero length salt. */ + saltlen = iobuf_get (inp); + if (saltlen < 0 || !pktlen) + rc = gpg_error (GPG_ERR_INV_PACKET); + else if (saltlen) + rc = read_raw_octet_string (inp, &pktlen, 0, saltlen, 0, &sig->salt); + pktlen--; /* Adjust for saltlen octet read above. */ + + if (rc) + goto leave; + } + + if (is_v4plus && sig->pubkey_algo) /* Extract required information. */ { const byte *p; size_t len; @@ -2392,7 +2602,7 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, sig->keyid[0] = buf32_to_u32 (p + 1 + 12); sig->keyid[1] = buf32_to_u32 (p + 1 + 16); } - else if (p && len == 33 && p[0] == 5) + else if (p && len == 33 && (p[0] == 5 || p[0] == 6)) { sig->keyid[0] = buf32_to_u32 (p + 1 ); sig->keyid[1] = buf32_to_u32 (p + 1 + 4); @@ -2512,7 +2722,14 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, (ulong) sig->keyid[0], (ulong) sig->keyid[1], sig->version, (ulong) sig->timestamp, md5_len, sig->sig_class, sig->digest_algo, sig->digest_start[0], sig->digest_start[1]); - if (is_v4or5) + if (sig->version == 6 && sig->salt) + { + es_fprintf (listfp, "\tsalt: "); + mpi_print (listfp, sig->salt, 1 /* Always print the salt. */); + es_putc ('\n', listfp); + } + + if (is_v4plus) { parse_sig_subpkt (sig, 1, SIGSUBPKT_LIST_HASHED, NULL); parse_sig_subpkt (sig, 0, SIGSUBPKT_LIST_UNHASHED, NULL); @@ -2548,13 +2765,22 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, { for (i = 0; i < ndata; i++) { - n = pktlen; - if (sig->pubkey_algo == PUBKEY_ALGO_ECDSA - || sig->pubkey_algo == PUBKEY_ALGO_EDDSA) - sig->data[i] = sos_read (inp, &n, 0); + if (sig->pubkey_algo == PUBKEY_ALGO_ED25519 && RFC9980) + { + rc = read_raw_octet_string (inp, &pktlen, 0, 64, 0, sig->data+i); + if (rc) + goto leave; + } else - sig->data[i] = mpi_read (inp, &n, 0); - pktlen -= n; + { + n = pktlen; + if (sig->pubkey_algo == PUBKEY_ALGO_ECDSA + || sig->pubkey_algo == PUBKEY_ALGO_EDDSA) + sig->data[i] = sos_read (inp, &n, 0); + else + sig->data[i] = mpi_read (inp, &n, 0); + pktlen -= n; + } if (list_mode) { es_fprintf (listfp, "\tdata: "); @@ -2585,7 +2811,6 @@ static int parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen, PKT_onepass_sig * ops) { - int version; int rc = 0; if (pktlen < 13) @@ -2596,11 +2821,11 @@ parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen, rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } - version = iobuf_get_noeof (inp); + ops->version = iobuf_get_noeof (inp); pktlen--; - if (version != 3) + if (!(ops->version == 3 || (RFC9980 && ops->version == 6))) { - log_error ("onepass_sig with unknown version %d\n", version); + log_error ("onepass_sig with unknown version %d\n", ops->version); if (list_mode) es_fputs (":onepass_sig packet: [unknown version]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); @@ -2612,21 +2837,63 @@ parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen, pktlen--; ops->pubkey_algo = iobuf_get_noeof (inp); pktlen--; - ops->keyid[0] = read_32 (inp); - pktlen -= 4; - ops->keyid[1] = read_32 (inp); - pktlen -= 4; + + if (ops->version == 6) + { + /* FIXME: We should save the fingerprint and check it against + * the regular packet. Howeever, we have not done that for + * keyid either. */ + unsigned char fpr[32]; + + rc = read_raw_octet_string (inp, &pktlen, 1, 0, 0, &ops->salt); + if (rc) + goto leave; + + if (pktlen < sizeof fpr) + { + log_error ("packet(%d) too short for fingerprint (%lu/%zu)\n", + pkttype, pktlen, sizeof fpr); + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + if (iobuf_read (inp, fpr, sizeof fpr) != sizeof fpr) + { + log_error ("premature eof while reading " + "fingerprint from packet(%d)\n", pkttype); + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + pktlen -= sizeof fpr; + + ops->keyid[0] = buf32_to_u32 (fpr); + ops->keyid[1] = buf32_to_u32 (fpr+4); + } + else + { + ops->keyid[0] = read_32 (inp); + pktlen -= 4; + ops->keyid[1] = read_32 (inp); + pktlen -= 4; + } + ops->last = iobuf_get_noeof (inp); pktlen--; if (list_mode) - es_fprintf (listfp, - ":onepass_sig packet: keyid %08lX%08lX\n" - "\tversion %d, sigclass 0x%02x, digest %d, pubkey %d, " - "last=%d\n", - (ulong) ops->keyid[0], (ulong) ops->keyid[1], - version, ops->sig_class, - ops->digest_algo, ops->pubkey_algo, ops->last); - + { + es_fprintf (listfp, + ":onepass_sig packet: keyid %08lX%08lX\n" + "\tversion %d, sigclass 0x%02x, digest %d, pubkey %d, " + "last=%d\n", + (ulong) ops->keyid[0], (ulong) ops->keyid[1], + ops->version, ops->sig_class, + ops->digest_algo, ops->pubkey_algo, ops->last); + if (ops->version == 6 && ops->salt) + { + es_fprintf (listfp, "\tsalt: "); + mpi_print (listfp, ops->salt, 1); + es_putc ('\n', listfp); + } + } leave: iobuf_skip_rest (inp, pktlen, 0); @@ -2644,7 +2911,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, int npkey, nskey; u32 keyid[2]; PKT_public_key *pk; - int is_v5; + int is_v5 = 0; + int is_v6 = 0; unsigned int pkbytes; /* For v5 keys: Number of bytes in the public * key material. For v4 keys: 0. */ @@ -2678,9 +2946,11 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, return 0; } else if (version == 4) - is_v5 = 0; + ; else if (version == 5) is_v5 = 1; + else if (version == 6 && RFC9980) + is_v6 = 1; else if (version == 2 || version == 3) { /* Not anymore supported since 2.1. Use an older gpg version @@ -2725,7 +2995,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, max_expiredate = 0; algorithm = iobuf_get_noeof (inp); pktlen--; - if (is_v5) + if (is_v5 || is_v6) { pkbytes = read_32 (inp); pktlen -= 4; @@ -2742,7 +3012,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, pkttype == PKT_PUBLIC_SUBKEY ? "public sub" : pkttype == PKT_SECRET_SUBKEY ? "secret sub" : "??", version, algorithm, timestamp, expiredate); - if (is_v5) + if (is_v5 || is_v6) es_fprintf (listfp, ", pkbytes %u\n", pkbytes); else es_fprintf (listfp, "\n"); @@ -2790,7 +3060,55 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, else if (algorithm == PUBKEY_ALGO_KYBER && i == 2) { /* Read the four-octet count prefixed Kyber public key. */ - err = read_octet_string (inp, &pktlen, 4, 0, 0, pk->pkey+i); + err = read_sos_octet_string (inp, &pktlen, 4, 0, 0, pk->pkey+i); + } + else if (algorithm == PUBKEY_ALGO_X25519 && RFC9980) + { + err = read_raw_octet_string (inp, &pktlen, 0, 32, + 0, pk->pkey+i); + } + else if (algorithm == PUBKEY_ALGO_MLK768_25519 && RFC9980) + { + err = read_raw_octet_string (inp, &pktlen, 0, i==0? 32 : 1184, + 0, pk->pkey+i); + } + else if ((algorithm == PUBKEY_ALGO_MLK768_NP384 + || algorithm == PUBKEY_ALGO_MLK768_BP384) && RFC9980) + { + err = read_raw_octet_string (inp, &pktlen, 0, i==0? 97 : 1184, + 0, pk->pkey+i); + } + else if (algorithm == PUBKEY_ALGO_MLK1024_448 && RFC9980) + { + if (is_v6) + err = read_raw_octet_string (inp, &pktlen, 0, i==0? 56 : 1568, + 0, pk->pkey+i); + else + err = gpg_error (GPG_ERR_INV_PACKET); + } + else if (algorithm == PUBKEY_ALGO_MLK1024_NP521 && RFC9980) + { + if (is_v6) + err = read_raw_octet_string (inp, &pktlen, 0, i==0? 133 : 1568, + 0, pk->pkey+i); + else + err = gpg_error (GPG_ERR_INV_PACKET); + } + else if (algorithm == PUBKEY_ALGO_MLK1024_BP512 && RFC9980) + { + if (is_v6) + err = read_raw_octet_string (inp, &pktlen, 0, i==0? 129 : 1568, + 0, pk->pkey+i); + else + err = gpg_error (GPG_ERR_INV_PACKET); + } + else if (algorithm == PUBKEY_ALGO_X25519 && RFC9980) + { + err = read_raw_octet_string (inp, &pktlen, 0, 32, 0, pk->pkey+i); + } + else if (algorithm == PUBKEY_ALGO_ED25519 && RFC9980) + { + err = read_raw_octet_string (inp, &pktlen, 0, 32, 0, pk->pkey+i); } else { @@ -2865,7 +3183,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, ski->algo = iobuf_get_noeof (inp); pktlen--; - if (is_v5) + if (is_v5 || (is_v6 && ski->algo)) { unsigned int protcount = 0; @@ -3166,7 +3484,6 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, /* Not encrypted. */ for (i = npkey; i < nskey; i++) { - if (pktlen < 2) /* At least two bytes for the length. */ { err = gpg_error (GPG_ERR_INV_PACKET); @@ -3174,10 +3491,66 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, } if (algorithm == PUBKEY_ALGO_KYBER && i == npkey+1) { - err = read_octet_string (inp, &pktlen, 4, 0, 1, pk->pkey+i); + err = read_sos_octet_string (inp, &pktlen, 4, 0, + 1, pk->pkey+i); if (err) goto leave; } + else if (algorithm == PUBKEY_ALGO_X25519 && RFC9980) + { + err = read_raw_octet_string (inp, &pktlen, 0, 32, + 0, pk->pkey+i); + } + else if (algorithm == PUBKEY_ALGO_MLK768_25519 && RFC9980) + { + err = read_raw_octet_string (inp, &pktlen, 0, + i == npkey? 32 : 64, + 0, pk->pkey+i); + } + else if ((algorithm == PUBKEY_ALGO_MLK768_NP384 + || algorithm == PUBKEY_ALGO_MLK768_BP384) && RFC9980) + { + err = read_raw_octet_string (inp, &pktlen, 0, + i == npkey? 48 : 64, + 0, pk->pkey+i); + } + else if (algorithm == PUBKEY_ALGO_MLK1024_448 && RFC9980) + { + if (is_v6) + err = read_raw_octet_string (inp, &pktlen, 0, + i == npkey? 56 : 64, + 0, pk->pkey+i); + else + err = gpg_error (GPG_ERR_INV_PACKET); + } + else if (algorithm == PUBKEY_ALGO_MLK1024_NP521 && RFC9980) + { + if (is_v6) + err = read_raw_octet_string (inp, &pktlen, 0, + i == npkey? 66 : 64, + 0, pk->pkey+i); + else + err = gpg_error (GPG_ERR_INV_PACKET); + } + else if (algorithm == PUBKEY_ALGO_MLK1024_BP512 && RFC9980) + { + if (is_v6) + err = read_raw_octet_string (inp, &pktlen, 0, + i == npkey? 64 : 64, + 0, pk->pkey+i); + else + err = gpg_error (GPG_ERR_INV_PACKET); + } + else if (algorithm == PUBKEY_ALGO_X25519 && RFC9980) + { + err = read_raw_octet_string (inp, &pktlen, 0, 32, + 0, pk->pkey+i); + } + else if (algorithm == PUBKEY_ALGO_ED25519 && RFC9980) + { + err = read_raw_octet_string (inp, &pktlen, 0, 32, + 0, pk->pkey+i); + } else { unsigned int n = pktlen; @@ -3205,18 +3578,21 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, if (err) goto leave; - if (pktlen < 2) - { - if (opt.verbose) - log_info ("checksum is missing (remaining bytes: %lu)\n", - pktlen); - err = gpg_error (GPG_ERR_INV_PACKET); - goto leave; - } - ski->csum = read_16 (inp); - pktlen -= 2; - if (list_mode) - es_fprintf (listfp, "\tchecksum: %04hx\n", ski->csum); + if (!is_v6) + { + if (pktlen < 2) + { + if (opt.verbose) + log_info ("checksum is missing (remaining bytes: %lu)\n", + pktlen); + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + ski->csum = read_16 (inp); + pktlen -= 2; + if (list_mode) + es_fprintf (listfp, "\tchecksum: %04hx\n", ski->csum); + } } } @@ -3224,6 +3600,21 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, if (list_mode) es_fprintf (listfp, "\tkeyid: %08lX%08lX\n", (ulong) keyid[0], (ulong) keyid[1]); + if (list_mode && (pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY) + && (opt.list_options & LIST_DEBUG_SHOW_SEXP)) + { + nvc_t nvc = seckey_packet_to_nvc (pk); + + es_fputs ("\ts-exp: ", listfp); + if (!nvc) + es_fputs ("[error getting key]\n", listfp); + else + { + nvc_write (nvc, listfp); + nvc_release (nvc); + } + } + leave: iobuf_skip_rest (inp, pktlen, 0); @@ -3756,31 +4147,43 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen, ed->buf = NULL; ed->new_ctb = new_ctb; ed->is_partial = partial; + ed->seipd = 0; + ed->version = 0; ed->aead_algo = 0; + ed->mdc_method = 0; ed->cipher_algo = 0; /* Only used with AEAD. */ ed->chunkbyte = 0; /* Only used with AEAD. */ if (pkttype == PKT_ENCRYPTED_MDC) { - /* Fixme: add some pktlen sanity checks. */ - int version; - - version = iobuf_get_noeof (inp); + ed->seipd = 1; + ed->version = iobuf_get_noeof (inp); if (orig_pktlen) pktlen--; - if (version != 1) + if (ed->version == 1) + ed->mdc_method = DIGEST_ALGO_SHA1; + else if (ed->version == 2 && RFC9980) + { + ed->cipher_algo = iobuf_get_noeof (inp); + if (orig_pktlen) + pktlen--; + ed->aead_algo = iobuf_get_noeof (inp); + if (orig_pktlen) + pktlen--; + ed->chunkbyte = iobuf_get_noeof (inp); + if (orig_pktlen) + pktlen--; + } + else { log_error ("encrypted_mdc packet with unknown version %d\n", - version); + ed->version); if (list_mode) es_fputs (":encrypted data packet: [unknown version]\n", listfp); /*skip_rest(inp, pktlen); should we really do this? */ rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } - ed->mdc_method = DIGEST_ALGO_SHA1; } - else - ed->mdc_method = 0; /* A basic sanity check. We need at least an 8 byte IV plus the 2 detection bytes. Note that we don't known the algorithm and thus @@ -3803,13 +4206,17 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen, if (list_mode) { + es_fprintf (listfp, ":encrypted data packet:\n\tversion: %d\n", + ed->version); + if (ed->mdc_method) + es_fprintf (listfp, "\t mdc: %d\n", ed->mdc_method); + if (ed->version == 2) + es_fprintf (listfp, "\t cipher: %u\n\t aead: %u\n\t cbyte: %u\n", + ed->cipher_algo, ed->aead_algo, ed->chunkbyte); if (orig_pktlen) - es_fprintf (listfp, ":encrypted data packet:\n\tlength: %lu\n", - orig_pktlen); + es_fprintf (listfp, "\t length: %lu\n", orig_pktlen); else - es_fprintf (listfp, ":encrypted data packet:\n\tlength: unknown\n"); - if (ed->mdc_method) - es_fprintf (listfp, "\tmdc_method: %d\n", ed->mdc_method); + es_fprintf (listfp, "\t length: unknown\n"); } ed->buf = inp; @@ -3850,14 +4257,14 @@ parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen, } +/* Note that PKTLEN may be 0 to indicate partial length encoding. */ static gpg_error_t -parse_encrypted_aead (iobuf_t inp, int pkttype, unsigned long pktlen, - PACKET *pkt, int partial) +parse_encrypted_ocb (iobuf_t inp, int pkttype, unsigned long pktlen, + PACKET *pkt, int partial) { int rc = 0; PKT_encrypted *ed; unsigned long orig_pktlen = pktlen; - int version; ed = pkt->pkt.encrypted = xtrymalloc (sizeof *pkt->pkt.encrypted); if (!ed) @@ -3867,6 +4274,8 @@ parse_encrypted_aead (iobuf_t inp, int pkttype, unsigned long pktlen, ed->buf = NULL; ed->new_ctb = 1; /* (packet number requires a new CTB anyway.) */ ed->is_partial = partial; + ed->seipd = 0; + ed->version = 0; ed->mdc_method = 0; /* A basic sanity check. We need one version byte, one algo byte, * one aead algo byte, one chunkbyte, at least 15 byte IV. */ @@ -3874,21 +4283,21 @@ parse_encrypted_aead (iobuf_t inp, int pkttype, unsigned long pktlen, { log_error ("packet(%d) too short\n", pkttype); if (list_mode) - es_fputs (":aead encrypted packet: [too short]\n", listfp); + es_fputs (":ocb encrypted packet: [too short]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); iobuf_skip_rest (inp, pktlen, partial); goto leave; } - version = iobuf_get_noeof (inp); + ed->version = iobuf_get_noeof (inp); if (orig_pktlen) pktlen--; - if (version != 1) + if (ed->version != 1) { - log_error ("aead encrypted packet with unknown version %d\n", - version); + log_error ("ocb encrypted packet with unknown version %d\n", + ed->version); if (list_mode) - es_fputs (":aead encrypted packet: [unknown version]\n", listfp); + es_fputs (":ocb encrypted packet: [unknown version]\n", listfp); /*skip_rest(inp, pktlen); should we really do this? */ rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; @@ -3910,7 +4319,7 @@ parse_encrypted_aead (iobuf_t inp, int pkttype, unsigned long pktlen, if (list_mode) { - es_fprintf (listfp, ":aead encrypted packet: cipher=%u aead=%u cb=%u\n", + es_fprintf (listfp, ":ocb encrypted packet: cipher=%u aead=%u cb=%u\n", ed->cipher_algo, ed->aead_algo, ed->chunkbyte); if (orig_pktlen) es_fprintf (listfp, "\tlength: %lu\n", orig_pktlen); diff --git a/g10/pkclist.c b/g10/pkclist.c index 87d95a4e5..6d727f2b6 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -1546,7 +1546,7 @@ select_algo_from_prefs(PK_LIST pk_list, int preftype, Instead it is better to use SHA-256 as implicit algorithm (which will be the case for rfc4880bis anyway). */ - if (opt.compliance == CO_DE_VS) + if (opt.compliance == CO_DE_VS || opt.compliance == CO_FIPS) implicit = DIGEST_ALGO_SHA256; else implicit = DIGEST_ALGO_SHA1; diff --git a/g10/pkglue.c b/g10/pkglue.c index 1fa0fc4dd..41a2a5801 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -50,6 +50,86 @@ get_mpi_from_sexp (gcry_sexp_t sexp, const char *item, int mpifmt) } +/* This is the same as get_mpi_from_sexp but removes a 0x40 prefix + * from the requested parameter. An opaque MPI is returned on + * success, NULL on error. */ +gcry_mpi_t +get_mpi_from_sexp_strip_0x40 (gcry_sexp_t sexp, const char *item) +{ + gcry_sexp_t list; + size_t len; + const char *p; + char *buffer; + gcry_mpi_t result = NULL; + + list = gcry_sexp_find_token (sexp, item, 0); + if (!list) + return NULL; + + p = gcry_sexp_nth_data (list, 1, &len); + if (!p || !len) + goto leave; + + /* If we have a parameter of at least 256 bits with an odd length in + * octets and the first octet is 0x40 we remove that octet. The + * 0x40 indicates native point format and is for example used when + * we create Curve25519 key. */ + if ((len & 1) && len > 32 && *p == 0x40) + { + p++; + len--; + } + buffer = xtrymalloc (len); + if (!buffer) + goto leave; + memcpy (buffer, p, len); + result = gcry_mpi_set_opaque (NULL, buffer, len*8); + buffer = NULL; + + leave: + gcry_sexp_release (list); + return result; +} + + +/* Return an opaque MPI with the concatenated values of the "r" and + * "s" parameters from SEXP. Return NULL on error*/ +gcry_mpi_t +get_r_s_mpi_from_sexp (gcry_sexp_t sexp) +{ + gcry_sexp_t rlist, slist; + size_t rlen, slen; + const char *r, *s; + char *buffer; + gcry_mpi_t result = NULL; + + rlist = gcry_sexp_find_token (sexp, "r", 0); + if (!rlist) + return NULL; + slist = gcry_sexp_find_token (sexp, "s", 0); + if (!slist) + goto leave; + + r = gcry_sexp_nth_data (rlist, 1, &rlen); + s = gcry_sexp_nth_data (slist, 1, &slen); + if (!r || !s || !rlen || !slen || rlen != slen) + goto leave; + + buffer = xtrymalloc (rlen+slen); + if (!buffer) + goto leave; + memcpy (buffer, r, rlen); + memcpy (buffer+rlen, s, slen); + result = gcry_mpi_set_opaque (NULL, buffer, (rlen+slen)*8); + buffer = NULL; + + leave: + gcry_sexp_release (rlist); + gcry_sexp_release (slist); + return result; +} + + /* * SOS (Simply, Octet String) is an attempt to handle opaque octet * string in OpenPGP, where well-formed MPI cannot represent octet @@ -238,6 +318,13 @@ pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash, xfree (curve); } } + else if (pkalgo == PUBKEY_ALGO_ED25519) + { + rc = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve Ed25519)" + "(flags eddsa)(q%m)))", + pkey[0]); + } else return GPG_ERR_PUBKEY_ALGO; @@ -257,6 +344,13 @@ pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash, if (gcry_sexp_build (&s_hash, NULL, fmt, hash)) BUG (); /* gcry_sexp_build should never fail. */ } + else if (pkalgo == PUBKEY_ALGO_ED25519) + { + if (gcry_sexp_build (&s_hash, NULL, + "(data(flags eddsa)(hash-algo sha512)(value %m))", + hash)) + BUG (); /* gcry_sexp_build should never fail. */ + } else { if (gcry_sexp_build (&s_hash, NULL, "%m", hash)) @@ -381,6 +475,23 @@ pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash, if (s != data[1]) gcry_mpi_release (s); } + else if (pkalgo == PUBKEY_ALGO_ED25519) + { + const unsigned char *p; + unsigned int nbits; + + if (!gcry_mpi_get_flag (data[0], GCRYMPI_FLAG_OPAQUE)) + rc = gpg_error (GPG_ERR_BAD_MPI); + else + { + p = gcry_mpi_get_opaque (data[0], &nbits); + if ((nbits+7)/8 != 64) + rc = gpg_error (GPG_ERR_BAD_MPI); + else + rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(eddsa(r%b)(s%b)))", + 32, p, 32, p+32); + } + } else if (pkalgo == PUBKEY_ALGO_ELGAMAL || pkalgo == PUBKEY_ALGO_ELGAMAL_E) { if (!data[0] || !data[1]) @@ -430,6 +541,7 @@ do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, const char *curve; const struct gnupg_ecc_params *ecc; enum gcry_kem_algos kyber_algo; + int is_rfc9980, only_ecc; const unsigned char *ecc_pubkey; size_t ecc_pubkey_len; @@ -450,32 +562,55 @@ do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, unsigned char kyber_ss[GCRY_KEM_MLKEM1024_SHARED_LEN]; size_t kyber_ct_len, kyber_ss_len; - char fixedinfo[1+MAX_FINGERPRINT_LEN]; - int fixedlen; - unsigned char kek[32]; /* AES-256 is mandatory. */ size_t kek_len = 32; /* For later error checking we make sure the array is cleared. */ resarr[0] = resarr[1] = resarr[2] = NULL; + only_ecc = 0; + if (pk->pubkey_algo == PUBKEY_ALGO_X25519) + is_rfc9980 = only_ecc = 1; + else if (IS_PUBKEY_ALGO_MLK (pk->pubkey_algo)) + is_rfc9980 = 1; + else + is_rfc9980 = 0; + /* As of now we use KEM only for the combined Kyber and thus a * second public key is expected. Right now we take the keys * directly from the PK->data elements. */ - ecc_oid = openpgp_oid_to_str (pk->pkey[0]); - if (!ecc_oid) + if (is_rfc9980) { - err = gpg_error_from_syserror (); - log_error ("%s: error getting OID for ECC key\n", __func__); - goto leave; + /* Note: We need t use the canonical names here. */ + switch (pk->pubkey_algo) + { + case PUBKEY_ALGO_X25519: + case PUBKEY_ALGO_MLK768_25519: curve = "ietf25"; break; + case PUBKEY_ALGO_MLK768_NP384: curve = "NIST P-384"; break; + case PUBKEY_ALGO_MLK768_BP384: curve = "brainpoolP384r1"; break; + case PUBKEY_ALGO_MLK1024_448: curve = "X448"; break; + case PUBKEY_ALGO_MLK1024_NP521: curve = "NIST P-521"; break; + case PUBKEY_ALGO_MLK1024_BP512: curve = "brainpoolP512r1"; break; + default: curve = "e_no_such_curve"; break; + } } - curve = openpgp_oid_to_curve (ecc_oid, 1); - if (!curve) + else { - err = gpg_error (GPG_ERR_INV_DATA); - log_error ("%s: error getting curve for ECC key\n", __func__); - goto leave; + ecc_oid = openpgp_oid_to_str (pk->pkey[0]); + if (!ecc_oid) + { + err = gpg_error_from_syserror (); + log_error ("%s: error getting OID for ECC key\n", __func__); + goto leave; + } + curve = openpgp_oid_to_curve (ecc_oid, 1); + if (!curve) + { + err = gpg_error (GPG_ERR_INV_DATA); + log_error ("%s: error getting curve for ECC key\n", __func__); + goto leave; + } } ecc = gnupg_get_ecc_params (curve); if (!ecc) @@ -489,15 +624,16 @@ do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, ecc_hash_algo = ecc->hash_algo; ecc_ss_len = gcry_md_get_algo_dlen (ecc_hash_algo); - ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); + ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[is_rfc9980?0:1], &nbits); ecc_pubkey_len = (nbits+7)/8; if (ecc_pubkey_len != ecc->pubkey_len) { if (ecc->kem_algo == GCRY_KEM_RAW_X25519 && ecc_pubkey_len == ecc->pubkey_len - 1) - /* For Curve25519, we also accept no prefix in the point - * representation. */ - ; + { + /* For Curve25519, we also accept no prefix in the point + * representation. */ + } else { if (opt.verbose) @@ -510,7 +646,7 @@ do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, if (ecc->kem_algo == GCRY_KEM_RAW_X25519) { - if (!strcmp (ecc_oid, "1.3.6.1.4.1.3029.1.5.1")) + if (ecc_oid && !strcmp (ecc_oid, "1.3.6.1.4.1.3029.1.5.1")) log_info ("Warning: " "legacy OID for cv25519 accepted during development\n"); /* Optional prefix handling */ @@ -523,7 +659,8 @@ do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, if (DBG_CRYPTO) { - log_debug ("ECC curve: %s\n", ecc_oid); + log_debug ("KEM algo: %d\n", ecc->kem_algo); + log_debug ("ECC curve: %s\n", ecc_oid? ecc_oid:curve); log_printhex (ecc_pubkey, ecc_pubkey_len, "ECC pubkey:"); } @@ -536,7 +673,7 @@ do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, { if (opt.verbose) log_info ("%s: gcry_kem_encap for ECC (%s) failed\n", - __func__, ecc_oid); + __func__, ecc_oid? ecc_oid:curve); goto leave; } if (DBG_CRYPTO) @@ -544,78 +681,140 @@ do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, log_printhex (ecc_ct, ecc_ct_len, "ECC ephem:"); log_printhex (ecc_ecdh, ecc_ecdh_len, "ECC ecdh:"); } - err = gnupg_ecc_kem_simple_kdf (ecc_ss, ecc_ss_len, - ecc_hash_algo, - ecc_ecdh, ecc_ecdh_len, - ecc_ct, ecc_ct_len, - ecc_pubkey, ecc_pubkey_len); - if (err) + if (!is_rfc9980) { - if (opt.verbose) - log_info ("%s: kdf for ECC failed\n", __func__); - goto leave; + err = gnupg_ecc_kem_simple_kdf (ecc_ss, ecc_ss_len, + ecc_hash_algo, + ecc_ecdh, ecc_ecdh_len, + ecc_ct, ecc_ct_len, + ecc_pubkey, ecc_pubkey_len); + if (err) + { + if (opt.verbose) + log_info ("%s: kdf for ECC failed\n", __func__); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (ecc_ss, ecc_ss_len, "ECC shared:"); } - if (DBG_CRYPTO) - log_printhex (ecc_ss, ecc_ss_len, "ECC shared:"); - kyber_pubkey = gcry_mpi_get_opaque (pk->pkey[2], &nbits); - kyber_pubkey_len = (nbits+7)/8; - if (kyber_pubkey_len == GCRY_KEM_MLKEM768_PUBKEY_LEN) - { - kyber_algo = GCRY_KEM_MLKEM768; - kyber_ct_len = GCRY_KEM_MLKEM768_ENCAPS_LEN; - kyber_ss_len = GCRY_KEM_MLKEM768_SHARED_LEN; - } - else if (kyber_pubkey_len == GCRY_KEM_MLKEM1024_PUBKEY_LEN) + if (only_ecc) { - kyber_algo = GCRY_KEM_MLKEM1024; - kyber_ct_len = GCRY_KEM_MLKEM1024_ENCAPS_LEN; - kyber_ss_len = GCRY_KEM_MLKEM1024_SHARED_LEN; + kek_len = gcry_cipher_get_algo_keylen (seskey_algo); + if (ecc_ct_len != 32 || ecc_pubkey_len != 32 || ecc_ecdh_len != 32 + || kek_len > sizeof kek) + err = gpg_error (GPG_ERR_INV_LENGTH); + else + { + gcry_kdf_hd_t kdfhd; + const char *kdfinfo; + unsigned long kdfparam[1]; + unsigned char inputbuf[32+32+32]; + + kdfparam[0] = kek_len; + kdfinfo = "OpenPGP X25519"; + memcpy (inputbuf, ecc_ct, 32); + memcpy (inputbuf+32, ecc_pubkey, 32); + memcpy (inputbuf+64, ecc_ecdh, 32); + err = gcry_kdf_open (&kdfhd, GCRY_KDF_HKDF, GCRY_MAC_HMAC_SHA256, + kdfparam, 1, + inputbuf, sizeof inputbuf, + NULL, 0, NULL, 0, + kdfinfo, strlen (kdfinfo)); + if (!err) + { + err = gcry_kdf_compute (kdfhd, NULL); + if (!err) + err = gcry_kdf_final (kdfhd, kek_len, kek); + gcry_kdf_close (kdfhd); + } + wipememory (inputbuf, sizeof inputbuf); + } } - else + else /* !only_ecc */ { - if (opt.verbose) - log_info ("%s: Kyber public key length invalid (%zu)\n", - __func__, kyber_pubkey_len); - err = gpg_error (GPG_ERR_INV_DATA); - goto leave; - } - if (DBG_CRYPTO) - log_printhex (kyber_pubkey, kyber_pubkey_len, "|!trunc|Kyber pubkey:"); + kyber_pubkey = gcry_mpi_get_opaque (pk->pkey[is_rfc9980?1:2], &nbits); + kyber_pubkey_len = (nbits+7)/8; + if (kyber_pubkey_len == GCRY_KEM_MLKEM768_PUBKEY_LEN) + { + kyber_algo = GCRY_KEM_MLKEM768; + kyber_ct_len = GCRY_KEM_MLKEM768_ENCAPS_LEN; + kyber_ss_len = GCRY_KEM_MLKEM768_SHARED_LEN; + } + else if (kyber_pubkey_len == GCRY_KEM_MLKEM1024_PUBKEY_LEN) + { + kyber_algo = GCRY_KEM_MLKEM1024; + kyber_ct_len = GCRY_KEM_MLKEM1024_ENCAPS_LEN; + kyber_ss_len = GCRY_KEM_MLKEM1024_SHARED_LEN; + } + else + { + if (opt.verbose) + log_info ("%s: Kyber public key length invalid (%zu)\n", + __func__, kyber_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (kyber_pubkey, kyber_pubkey_len, "|!trunc|Kyber pubkey:"); - err = gcry_kem_encap (kyber_algo, - kyber_pubkey, kyber_pubkey_len, - kyber_ct, kyber_ct_len, - kyber_ss, kyber_ss_len, - NULL, 0); - if (err) - { - if (opt.verbose) - log_info ("%s: gcry_kem_encap for ECC failed\n", __func__); - goto leave; - } + err = gcry_kem_encap (kyber_algo, + kyber_pubkey, kyber_pubkey_len, + kyber_ct, kyber_ct_len, + kyber_ss, kyber_ss_len, + NULL, 0); + if (err) + { + if (opt.verbose) + log_info ("%s: gcry_kem_encap for ECC failed\n", __func__); + goto leave; + } - if (DBG_CRYPTO) - { - log_printhex (kyber_ct, kyber_ct_len, "|!trunc|Kyber ephem:"); - log_printhex (kyber_ss, kyber_ss_len, "Kyber shared:"); - } + if (DBG_CRYPTO) + { + log_printhex (kyber_ct, kyber_ct_len, "|!trunc|Kyber ephem:"); + log_printhex (kyber_ss, kyber_ss_len, "Kyber shared:"); + } + if (is_rfc9980) + { + char fixedinfo[1+22]; /* algid || domSep || len(domSep) */ - fixedinfo[0] = seskey_algo; - v5_fingerprint_from_pk (pk, fixedinfo+1, NULL); - fixedlen = 33; + fixedinfo[0] = pk->pubkey_algo; + memcpy (fixedinfo+1, "OpenPGPCompositeKDFv1\x15", 22); + + err = gnupg_kem_combiner_sha3_256 (kek, kek_len, + ecc_ecdh, ecc_ct_len, + ecc_ct, ecc_ct_len, + ecc_pubkey, ecc_pubkey_len, + kyber_ss, kyber_ss_len, + fixedinfo, sizeof fixedinfo); + } + else + { + char fixedinfo[1+MAX_FINGERPRINT_LEN]; + int fixedlen; + + fixedinfo[0] = seskey_algo; + v5_fingerprint_from_pk (pk, fixedinfo+1, NULL); + fixedlen = 33; + + err = gnupg_kem_combiner (kek, kek_len, + ecc_ss, ecc_ss_len, + ecc_ct, ecc_ct_len, + kyber_ss, kyber_ss_len, + kyber_ct, kyber_ct_len, + fixedinfo, fixedlen); + } + + if (err) + { + if (opt.verbose) + log_info ("%s: KEM combiner failed\n", __func__); + goto leave; + } + } /* !only_ecc */ - err = gnupg_kem_combiner (kek, kek_len, - ecc_ss, ecc_ss_len, ecc_ct, ecc_ct_len, - kyber_ss, kyber_ss_len, kyber_ct, kyber_ct_len, - fixedinfo, fixedlen); - if (err) - { - if (opt.verbose) - log_info ("%s: KEM combiner failed\n", __func__); - goto leave; - } if (DBG_CRYPTO) log_printhex (kek, kek_len, "KEK:"); @@ -650,17 +849,33 @@ do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, if (DBG_CRYPTO) log_printhex (seskey, seskey_len, "seskey:"); - enc_seskey_len = 1 + seskey_len + 8; - enc_seskey = xtrymalloc (enc_seskey_len); - if (!enc_seskey || enc_seskey_len > 254) + if (is_rfc9980) { - err = gpg_error_from_syserror (); - goto leave; + enc_seskey_len = seskey_len + 8; + enc_seskey = xtrymalloc (enc_seskey_len); + if (!enc_seskey) + { + err = gpg_error_from_syserror (); + goto leave; + } + + err = gcry_cipher_encrypt (hd, enc_seskey, enc_seskey_len, + seskey, seskey_len); } + else + { + enc_seskey_len = 1 + seskey_len + 8; + enc_seskey = xtrymalloc (enc_seskey_len); + if (!enc_seskey || enc_seskey_len > 254) + { + err = gpg_error_from_syserror (); + goto leave; + } - enc_seskey[0] = enc_seskey_len - 1; - err = gcry_cipher_encrypt (hd, enc_seskey+1, enc_seskey_len-1, - seskey, seskey_len); + enc_seskey[0] = enc_seskey_len - 1; + err = gcry_cipher_encrypt (hd, enc_seskey+1, enc_seskey_len-1, + seskey, seskey_len); + } if (err) { log_error ("%s: wrapping session key failed\n", __func__); @@ -669,12 +884,13 @@ do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, if (DBG_CRYPTO) log_printhex (enc_seskey, enc_seskey_len, "enc_seskey:"); - resarr[0] = gcry_mpi_set_opaque_copy (NULL, ecc_ct, 8 * ecc_ct_len); - if (resarr[0]) - resarr[1] = gcry_mpi_set_opaque_copy (NULL, kyber_ct, 8 * kyber_ct_len); - if (resarr[1]) - resarr[2] = gcry_mpi_set_opaque_copy (NULL, enc_seskey, 8 * enc_seskey_len); - if (!resarr[0] || !resarr[1] || !resarr[2]) + i= 0; + resarr[i++] = gcry_mpi_set_opaque_copy (NULL, ecc_ct, 8 * ecc_ct_len); + if (!only_ecc) + resarr[i++] = gcry_mpi_set_opaque_copy (NULL, kyber_ct, 8 * kyber_ct_len); + resarr[i++] = gcry_mpi_set_opaque_copy (NULL, enc_seskey, 8 * enc_seskey_len); + + if (!resarr[0] || !resarr[1] || !(resarr[2] || only_ecc)) { err = gpg_error_from_syserror (); for (i=0; i < 3; i++) @@ -879,7 +1095,7 @@ do_encrypt_ecdh (PKT_public_key *pk, gcry_mpi_t data, gcry_mpi_t *resarr) goto leave; } - err = gnupg_ecc_kem_kdf (kek, kek_len, 1, kdf_hash_algo, + err = gnupg_ecc_kem_kdf (kek, kek_len, GCRY_KDF_ONESTEP_KDF, kdf_hash_algo, shared_secret, shared_secretlen, kdf_params, kdf_params_len); xfree (kdf_params); @@ -1022,6 +1238,9 @@ pk_encrypt (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, return do_encrypt_rsa_elg (pk, data, resarr); else if (algo == PUBKEY_ALGO_RSA || algo == PUBKEY_ALGO_RSA_E) return do_encrypt_rsa_elg (pk, data, resarr); + else if (RFC9980 && (algo == PUBKEY_ALGO_X25519 + || IS_PUBKEY_ALGO_MLK (algo))) + return do_encrypt_kem (pk, data, seskey_algo, resarr); else return gpg_error (GPG_ERR_PUBKEY_ALGO); } diff --git a/g10/pkglue.h b/g10/pkglue.h index 655d5c083..b55545bd7 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -24,6 +24,8 @@ /*-- pkglue.c --*/ gcry_mpi_t get_mpi_from_sexp (gcry_sexp_t sexp, const char *item, int mpifmt); +gcry_mpi_t get_mpi_from_sexp_strip_0x40 (gcry_sexp_t sexp, const char *item); +gcry_mpi_t get_r_s_mpi_from_sexp (gcry_sexp_t sexp); gpg_error_t sexp_extract_param_sos (gcry_sexp_t sexp, const char *param, gcry_mpi_t *r_sos); gpg_error_t sexp_extract_param_sos_nlz (gcry_sexp_t sexp, const char *param, diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 396e125d9..7e65b81f3 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -39,7 +39,8 @@ static gpg_error_t get_it (ctrl_t ctrl, struct seskey_enc_list *k, - DEK *dek, PKT_public_key *sk, u32 *keyid); + DEK *dek, PKT_public_key *sk, u32 *keyid, + int seipdv2_cipher_algo); /* Check that the given algo is mentioned in one of the valid user-ids. */ @@ -47,15 +48,17 @@ static int is_algo_in_prefs (kbnode_t keyblock, preftype_t type, int algo) { kbnode_t k; + prefitem_t *prefs; for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = k->pkt->pkt.user_id; - prefitem_t *prefs = uid->prefs; - if (uid->created && prefs && !uid->flags.revoked && !uid->flags.expired) + prefs = uid->prefs; + if (uid->created && prefs + && !uid->flags.revoked && !uid->flags.expired) { for (; prefs->type; prefs++) if (prefs->type == type && prefs->value == algo) @@ -63,6 +66,14 @@ is_algo_in_prefs (kbnode_t keyblock, preftype_t type, int algo) } } } + /* In rfc-9980 mode also check direct key signature prefs. */ + if (RFC9980 && (k = keyblock) && k->pkt->pkttype == PKT_PUBLIC_KEY + && (prefs = k->pkt->pkt.public_key->dks_prefs)) + { + for (; prefs->type; prefs++) + if (prefs->type == type && prefs->value == algo) + return 1; + } return 0; } @@ -70,9 +81,12 @@ is_algo_in_prefs (kbnode_t keyblock, preftype_t type, int algo) /* * Get the session key from a pubkey enc packet and return it in DEK, * which should have been allocated in secure memory by the caller. + * SEIPDV2_CIPHER_ALGO may be passed from the encrypted packet in case + * of a SEIPDv2 packet. */ gpg_error_t -get_session_key (ctrl_t ctrl, struct seskey_enc_list *list, DEK *dek) +get_session_key (ctrl_t ctrl, struct seskey_enc_list *list, DEK *dek, + int seipdv2_cipher_algo) { PKT_public_key *sk = NULL; gpg_error_t err; @@ -122,7 +136,9 @@ get_session_key (ctrl_t ctrl, struct seskey_enc_list *list, DEK *dek) || k->u.pub.pubkey_algo == PUBKEY_ALGO_KYBER || k->u.pub.pubkey_algo == PUBKEY_ALGO_RSA || k->u.pub.pubkey_algo == PUBKEY_ALGO_RSA_E - || k->u.pub.pubkey_algo == PUBKEY_ALGO_ELGAMAL)) + || k->u.pub.pubkey_algo == PUBKEY_ALGO_ELGAMAL + || k->u.pub.pubkey_algo == PUBKEY_ALGO_X25519 + || IS_PUBKEY_ALGO_MLK (k->u.pub.pubkey_algo))) continue; if (openpgp_pk_test_algo2 (k->u.pub.pubkey_algo, PUBKEY_USAGE_ENC)) @@ -152,7 +168,7 @@ get_session_key (ctrl_t ctrl, struct seskey_enc_list *list, DEK *dek) else continue; - err = get_it (ctrl, k, dek, sk, keyid); + err = get_it (ctrl, k, dek, sk, keyid, seipdv2_cipher_algo); k->result = err; if (!err) { @@ -231,8 +247,8 @@ ecdh_sexp_build (gcry_sexp_t *r_s_data, struct seskey_enc_list *enc, static gpg_error_t -get_it (ctrl_t ctrl, - struct seskey_enc_list *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) +get_it (ctrl_t ctrl, struct seskey_enc_list *enc, DEK *dek, + PKT_public_key *sk, u32 *keyid, int seipdv2_cipher_algo) { gpg_error_t err; byte *frame = NULL; @@ -254,6 +270,19 @@ get_it (ctrl_t ctrl, if (err) goto leave; + if (enc->u.pub.version == 6 && !enc->u.pub.seskey_algo && RFC9980 + && (sk->pubkey_algo == PUBKEY_ALGO_X25519 + || IS_PUBKEY_ALGO_MLK (sk->pubkey_algo))) + { + if (seipdv2_cipher_algo) + dek->algo = seipdv2_cipher_algo; + else + { + log_info ("Warning: No symmetric algo yet known - assuming AES256\n"); + dek->algo = CIPHER_ALGO_AES256; + } + } + /* Convert the data to an S-expression. */ if (sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL || sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E) @@ -273,8 +302,21 @@ get_it (ctrl_t ctrl, err = gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))", enc->u.pub.data[0]); } - else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) + else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) err = ecdh_sexp_build (&s_data, enc, sk); + else if (sk->pubkey_algo == PUBKEY_ALGO_X25519) + { + if (!enc->u.pub.data[0] || !enc->u.pub.data[1]) + err = gpg_error (GPG_ERR_BAD_MPI); + else + err = gcry_sexp_build (&s_data, NULL, + "(enc-val(ecc(t%d)(c%d)(h%d)(e%m)(s%m)" + "(kdf-params%s)))", + 9580, dek->algo, + GCRY_MAC_HMAC_SHA256, + enc->u.pub.data[0], enc->u.pub.data[1], + "OpenPGP X25519"); + } else if (sk->pubkey_algo == PUBKEY_ALGO_KYBER) { char fixedinfo[1+MAX_FINGERPRINT_LEN]; @@ -303,6 +345,25 @@ get_it (ctrl_t ctrl, enc->u.pub.data[2], enc->u.pub.seskey_algo, fixedlen, fixedinfo); } + else if (IS_PUBKEY_ALGO_MLK (sk->pubkey_algo)) + { + char fixedinfo[1+22]; /* algid || domSep || len(domSep) */ + + fixedinfo[0] = sk->pubkey_algo; + memcpy (fixedinfo+1, "OpenPGPCompositeKDFv1\x15", 22); + + if (!enc->u.pub.data[0] || !enc->u.pub.data[1] || !enc->u.pub.data[2]) + err = gpg_error (GPG_ERR_BAD_MPI); + else + err = gcry_sexp_build (&s_data, NULL, + "(enc-val(pqc(t%d)(e%m)(k%m)(s%m)(c%d)(fixed-info%b)))", + 2, /* Use key combiner sha3-256 */ + enc->u.pub.data[0], + enc->u.pub.data[1], + enc->u.pub.data[2], + enc->u.pub.seskey_algo, + (int)sizeof fixedinfo, fixedinfo); + } else err = gpg_error (GPG_ERR_BUG); @@ -311,7 +372,6 @@ get_it (ctrl_t ctrl, /* Decrypt. */ desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1); - err = agent_pkdecrypt (NULL, keygrip, desc, sk->keyid, sk->main_keyid, sk->pubkey_algo, s_data, &frame, &nframe, &padding); @@ -341,14 +401,20 @@ get_it (ctrl_t ctrl, log_printhex (frame, nframe, "DEK frame:"); frameidx = 0; - if (sk->pubkey_algo == PUBKEY_ALGO_KYBER) + if (sk->pubkey_algo == PUBKEY_ALGO_KYBER + || IS_PUBKEY_ALGO_MLK (sk->pubkey_algo)) { if (nframe != 32 && opt.flags.require_pqc_encryption) { log_info (_("WARNING: session key is not quantum-resistant\n")); } dek->keylen = nframe; - dek->algo = enc->u.pub.seskey_algo; + if (!dek->algo) + dek->algo = enc->u.pub.seskey_algo; + } + else if (sk->pubkey_algo == PUBKEY_ALGO_X25519) + { + dek->keylen = nframe; } else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) { diff --git a/g10/seskey.c b/g10/seskey.c index 2fe8e9de7..c2ddbc68a 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -86,7 +86,9 @@ encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits) if (DBG_CRYPTO) log_debug ("encode_session_key: encoding %d byte DEK", dek->keylen); - if (openpgp_pk_algo == PUBKEY_ALGO_KYBER) + if (openpgp_pk_algo == PUBKEY_ALGO_KYBER + || openpgp_pk_algo == PUBKEY_ALGO_X25519 + || IS_PUBKEY_ALGO_MLK (openpgp_pk_algo)) { /* Straightforward encoding w/o extra checksum as used by ECDH. */ nframe = dek->keylen; @@ -277,7 +279,8 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) log_assert (hash_algo); log_assert (pk); - if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA) + if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA + || pk->pubkey_algo == PUBKEY_ALGO_ED25519) { /* EdDSA signs data of arbitrary length. Thus no special treatment is required. */ diff --git a/g10/sig-check.c b/g10/sig-check.c index e0ca30ae2..0ccab513a 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -564,6 +564,11 @@ check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig, if (sig->hashed) { n = sig->hashed->len; + if (sig->version >= 6) /* (v6 and later use 4 octets) */ + { + gcry_md_putc (digest, (n >> 24)); + gcry_md_putc (digest, (n >> 16)); + } gcry_md_putc (digest, (n >> 8) ); gcry_md_putc (digest, n ); gcry_md_write (digest, sig->hashed->data, n); @@ -573,12 +578,20 @@ check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig, { /* Two octets for the (empty) length of the hashed * section. */ + if (sig->version >= 6) + { + gcry_md_putc (digest, 0); + gcry_md_putc (digest, 0); + } gcry_md_putc (digest, 0); gcry_md_putc (digest, 0); - n = 6; + n = 6; } + if (sig->version >= 6) + n += 2; /* Fixup for the two extra octets hashed. */ + /* Hash data from the literal data packet. */ - if (sig->version >= 5 + if (sig->version >= 5 && sig->version != 6 && (sig->sig_class == 0x00 || sig->sig_class == 0x01)) { /* - One octet content format @@ -596,7 +609,7 @@ check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig, i = 0; buf[i++] = sig->version; buf[i++] = 0xff; - if (sig->version >= 5) + if (sig->version >= 5 && sig->version != 6) { #if SIZEOF_SIZE_T > 4 buf[i++] = n >> 56; @@ -621,7 +634,7 @@ check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig, /* Convert the digest to an MPI. */ result = encode_md_value (pk, digest, sig->digest_algo ); if (!result) - return GPG_ERR_GENERAL; + return GPG_ERR_GENERAL; /* Verify the signature. */ if (DBG_CLOCK && sig->sig_class <= 0x01) @@ -793,7 +806,7 @@ check_revocation_keys (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig) if (gcry_md_open (&md, sig->digest_algo, 0)) BUG (); - hash_public_key (md, pk); + hash_public_key (md, pk, sig); /* Note: check_signature only checks that the signature * is good. It does not fail if the key is revoked. */ err = check_signature (ctrl, sig, md, NULL, 0, NULL, @@ -838,8 +851,8 @@ check_backsig (PKT_public_key *main_pk,PKT_public_key *sub_pk, rc = gcry_md_open (&md, backsig->digest_algo,0); if (!rc) { - hash_public_key(md,main_pk); - hash_public_key(md,sub_pk); + hash_public_key (md, main_pk, backsig); + hash_public_key (md, sub_pk, backsig); rc = check_signature_end (sub_pk, backsig, md, NULL, 0, NULL, NULL, NULL); cache_sig_result(backsig,rc); gcry_md_close(md); @@ -1022,27 +1035,29 @@ check_signature_over_key_or_uid (ctrl_t ctrl, PKT_public_key *signer, * a bug. */ if (gcry_md_open (&md, sig->digest_algo, 0)) BUG (); + if (DBG_HASHING) + gcry_md_debug (md, "keysig"); /* Hash the relevant data. */ if (IS_KEY_SIG (sig) || IS_KEY_REV (sig)) { log_assert (packet->pkttype == PKT_PUBLIC_KEY); - hash_public_key (md, packet->pkt.public_key); + hash_public_key (md, packet->pkt.public_key, sig); rc = check_signature_end_simple (signer, sig, md, NULL, 0); } else if (IS_BACK_SIG (sig)) { log_assert (packet->pkttype == PKT_PUBLIC_KEY); - hash_public_key (md, packet->pkt.public_key); - hash_public_key (md, signer); + hash_public_key (md, packet->pkt.public_key, sig); + hash_public_key (md, signer, sig); rc = check_signature_end_simple (signer, sig, md, NULL, 0); } else if (IS_SUBKEY_SIG (sig) || IS_SUBKEY_REV (sig)) { log_assert (packet->pkttype == PKT_PUBLIC_SUBKEY); - hash_public_key (md, pripk); - hash_public_key (md, packet->pkt.public_key); + hash_public_key (md, pripk, sig); + hash_public_key (md, packet->pkt.public_key, NULL); rc = check_signature_end_simple (signer, sig, md, NULL, 0); } else if (IS_UID_SIG (sig) || IS_UID_REV (sig)) @@ -1060,7 +1075,7 @@ check_signature_over_key_or_uid (ctrl_t ctrl, PKT_public_key *signer, } else { - hash_public_key (md, pripk); + hash_public_key (md, pripk, sig); hash_uid_packet (packet->pkt.user_id, md, sig); rc = check_signature_end_simple (signer, sig, md, NULL, 0); } diff --git a/g10/sign.c b/g10/sign.c index 4fa11fe27..1a773e9b3 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -101,7 +101,8 @@ mk_notation_policy_etc (ctrl_t ctrl, PKT_signature *sig, { ndmanu = name_value_to_notation ("manu", - gnupg_manu_notation_value (with_manu == 23? CO_DE_VS : CO_GNUPG)); + gnupg_manu_notation_value (with_manu == 23? CO_DE_VS : + with_manu == 140? CO_FIPS : CO_GNUPG)); ndmanu->next = nd; nd = ndmanu; } @@ -356,6 +357,11 @@ hash_sigversion_to_magic (gcry_md_hd_t md, const PKT_signature *sig, if (sig->hashed) { n = sig->hashed->len; + if (sig->version >= 6) + { + gcry_md_putc (md, (n >> 24) ); + gcry_md_putc (md, (n >> 16) ); + } gcry_md_putc (md, (n >> 8) ); gcry_md_putc (md, n ); gcry_md_write (md, sig->hashed->data, n ); @@ -363,10 +369,18 @@ hash_sigversion_to_magic (gcry_md_hd_t md, const PKT_signature *sig, } else { + if (sig->version >= 6) + { + gcry_md_putc (md, 0); + gcry_md_putc (md, 0); + } gcry_md_putc (md, 0); /* Always hash the length of the subpacket. */ gcry_md_putc (md, 0); n = 6; } + if (sig->version >= 6) + n += 2; /* Fixup for the two extra octets hashed. */ + /* Hash data from the literal data packet. */ if (sig->version >= 5 && (sig->sig_class == 0x00 || sig->sig_class == 0x01)) { @@ -401,7 +415,7 @@ hash_sigversion_to_magic (gcry_md_hd_t md, const PKT_signature *sig, i = 0; buf[i++] = sig->version; /* Hash convention version. */ buf[i++] = 0xff; /* Not any sig type value. */ - if (sig->version >= 5) + if (sig->version >= 5 && sig->version != 6) { /* Note: We don't hashed any data larger than 2^32 and thus we * can always use 0 here. See also note below. */ @@ -540,6 +554,12 @@ do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig, if (!err) err = sexp_extract_param_sos (s_sigval, "s", &sig->data[1]); } + else if (pksk->pubkey_algo == PUBKEY_ALGO_ED25519) + { + sig->data[0] = get_r_s_mpi_from_sexp (s_sigval); + if (!sig->data[0]) + err = gpg_error (GPG_ERR_NO_OBJ); + } else { sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_USG); @@ -990,6 +1010,9 @@ write_signature_packets (ctrl_t ctrl, if (opt.compliance == CO_DE_VS && gnupg_rng_is_compliant (CO_DE_VS)) with_manu = 23; /* FIXME: Also check that the algos are compliant?*/ + else if (opt.compliance == CO_FIPS + && gnupg_rng_is_compliant (CO_FIPS)) + with_manu = 140; else if (!(opt.compat_flags & COMPAT_NO_MANU)) with_manu = 1; else @@ -1864,7 +1887,9 @@ make_keysig_packet (ctrl_t ctrl, || sigclass == SIGCLASS_CERTREV || sigclass == SIGCLASS_SUBREV ); - if (pksk->version >= 5) + if (pksk->version == 6 && RFC9980) + sigversion = 6; + else if (pksk->version >= 5) sigversion = 5; else sigversion = 4; @@ -1884,6 +1909,10 @@ make_keysig_packet (ctrl_t ctrl, else digest_algo = DIGEST_ALGO_SHA256; } + else if (pksk->pubkey_algo == PUBKEY_ALGO_ED25519) + { + digest_algo = DIGEST_ALGO_SHA256; + } else /* Use the default. */ digest_algo = DEFAULT_DIGEST_ALGO; @@ -1895,15 +1924,17 @@ make_keysig_packet (ctrl_t ctrl, if (gcry_md_open (&md, digest_algo, 0)) BUG (); + if (DBG_HASHING) + gcry_md_debug (md, "mkkeysig"); /* Hash the public key certificate. */ - hash_public_key (md, pk); + hash_public_key (md, pk, NULL); if (sigclass == SIGCLASS_SUBKEY || sigclass == SIGCLASS_BACKSIG || sigclass == SIGCLASS_SUBREV) { /* Hash the subkey binding/backsig/revocation. */ - hash_public_key (md, subpk); + hash_public_key (md, subpk, NULL); if ((subpk->pubkey_usage & PUBKEY_USAGE_RENC)) signhints |= SIGNHINT_ADSK; } @@ -1946,6 +1977,9 @@ make_keysig_packet (ctrl_t ctrl, if (opt.compliance == CO_DE_VS && gnupg_rng_is_compliant (CO_DE_VS)) with_manu = 23; /* Always in de-vs mode. */ + else if (opt.compliance == CO_FIPS + && gnupg_rng_is_compliant (CO_FIPS)) + with_manu = 140; else if (!(opt.compat_flags & COMPAT_NO_MANU)) with_manu = 1; } @@ -2032,10 +2066,10 @@ update_keysig_packet (ctrl_t ctrl, BUG (); /* Hash the public key certificate and the user id. */ - hash_public_key (md, pk); + hash_public_key (md, pk, NULL); if (orig_sig->sig_class == 0x18) - hash_public_key (md, subpk); + hash_public_key (md, subpk, orig_sig); else hash_uid (md, orig_sig->version, uid); diff --git a/g10/t-keydb-get-keyblock.c b/g10/t-keydb-get-keyblock.c index 90ce6e9a6..01d1fa516 100644 --- a/g10/t-keydb-get-keyblock.c +++ b/g10/t-keydb-get-keyblock.c @@ -67,3 +67,11 @@ do_test (int argc, char *argv[]) release_kbnode (kb1); xfree (ctrl); } + + +nvc_t +seckey_packet_to_nvc (PKT_public_key *pk) +{ + (void)pk; + return NULL; +} diff --git a/g10/t-keydb.c b/g10/t-keydb.c index 4c78dac48..264c80e0a 100644 --- a/g10/t-keydb.c +++ b/g10/t-keydb.c @@ -105,3 +105,11 @@ do_test (int argc, char *argv[]) keydb_release (hd2); xfree (ctrl); } + + +nvc_t +seckey_packet_to_nvc (PKT_public_key *pk) +{ + (void)pk; + return NULL; +} diff --git a/g10/t-keyid.c b/g10/t-keyid.c index b1ae9f691..b2a4b4094 100644 --- a/g10/t-keyid.c +++ b/g10/t-keyid.c @@ -29,6 +29,7 @@ #include "gpg.h" #include "keydb.h" #include "../common/t-support.h" +#include "../common/name-value.h" @@ -132,3 +133,11 @@ main (int argc, char **argv) return !!errcount; } + + +nvc_t +seckey_packet_to_nvc (PKT_public_key *pk) +{ + (void)pk; + return NULL; +} diff --git a/g10/t-stutter.c b/g10/t-stutter.c index 503a92004..c26eca2e5 100644 --- a/g10/t-stutter.c +++ b/g10/t-stutter.c @@ -611,3 +611,10 @@ do_test (int argc, char *argv[]) xfree (filename); } + +nvc_t +seckey_packet_to_nvc (PKT_public_key *pk) +{ + (void)pk; + return NULL; +} diff --git a/g10/test-stubs.c b/g10/test-stubs.c index 16d10972d..ee94e9c40 100644 --- a/g10/test-stubs.c +++ b/g10/test-stubs.c @@ -308,11 +308,13 @@ import_included_key_block (ctrl_t ctrl, kbnode_t keyblock) * No encryption here but mainproc links to these functions. */ gpg_error_t -get_session_key (ctrl_t ctrl, struct seskey_enc_list *k, DEK *dek) +get_session_key (ctrl_t ctrl, struct seskey_enc_list *k, DEK *dek, + int seipdv2_cipher_algo) { (void)ctrl; (void)k; (void)dek; + (void)seipdv2_cipher_algo; return GPG_ERR_GENERAL; } diff --git a/g10/test.c b/g10/test.c index f6c697a35..388546849 100644 --- a/g10/test.c +++ b/g10/test.c @@ -25,6 +25,7 @@ #define INCLUDED_BY_MAIN_MODULE 1 #include "gpg.h" +#include "../common/name-value.h" /* A unit test consists of one or more tests. Tests can be broken into groups and each group can consist of one or more tests. */ diff --git a/kbx/keybox-blob.c b/kbx/keybox-blob.c index 18444b140..d7e219a7a 100644 --- a/kbx/keybox-blob.c +++ b/kbx/keybox-blob.c @@ -763,7 +763,7 @@ _keybox_create_openpgp_blob (KEYBOXBLOB *r_blob, /* Check whether we need a blob with 32 bit fingerprints. We could * use this always but for backward compatibility we do this only for * v5 keys. */ - if (info->primary.version == 5) + if (info->primary.version >= 5) need_fpr32 = 1; else { diff --git a/kbx/keybox-openpgp.c b/kbx/keybox-openpgp.c index 87823d84f..8b4e35845 100644 --- a/kbx/keybox-openpgp.c +++ b/kbx/keybox-openpgp.c @@ -233,6 +233,13 @@ keygrip_from_keyparm (int algo, struct keyparm_s *kp, unsigned char *grip) } break; + case PUBKEY_ALGO_ED25519: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve Ed25519)" + "(flags eddsa)(q%b)))", + kp[0].len, kp[0].mpi); + break; + case PUBKEY_ALGO_KYBER: /* There is no space in the BLOB for a second grip, thus for now * we store only the ECC keygrip. */ @@ -253,6 +260,50 @@ keygrip_from_keyparm (int algo, struct keyparm_s *kp, unsigned char *grip) } break; + case PUBKEY_ALGO_X25519: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve ietf25)(q%b)))", + kp[0].len, kp[0].mpi); + break; + + case PUBKEY_ALGO_MLK768_25519: + /* There is no space in the BLOB for the a second grip of a + * composite algo, thus for now we store only the ECC keygrip. */ + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve ietf25)(q%b)))", + kp[0].len, kp[0].mpi); + break; + + case PUBKEY_ALGO_MLK768_NP384: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve nistp384)(q%b)))", + kp[0].len, kp[0].mpi); + break; + + case PUBKEY_ALGO_MLK768_BP384: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve bp384)(q%b)))", + kp[0].len, kp[0].mpi); + break; + + case PUBKEY_ALGO_MLK1024_448: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve X448)(q%b)))", + kp[0].len, kp[0].mpi); + break; + + case PUBKEY_ALGO_MLK1024_NP521: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve nistp521)(q%b)))", + kp[0].len, kp[0].mpi); + break; + + case PUBKEY_ALGO_MLK1024_BP512: + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(ecc(curve bp512)(q%b)))", + kp[0].len, kp[0].mpi); + break; + default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; @@ -294,6 +345,7 @@ parse_key (const unsigned char *data, size_t datalen, gcry_md_hd_t md; int is_ecc = 0; int is_kyber = 0; + int is_9980 = 0; /* Algorithm version from 9980 et al. */ int is_v5; /* unsigned int pkbytes; for v5: # of octets of the public key params. */ struct keyparm_s keyparm[OPENPGP_MAX_NPKEY]; @@ -302,9 +354,9 @@ parse_key (const unsigned char *data, size_t datalen, if (datalen < 5) return gpg_error (GPG_ERR_INV_PACKET); version = *data++; datalen--; - if (version < 2 || version > 5 ) + if (version < 2 || version > 6) return gpg_error (GPG_ERR_INV_PACKET); /* Invalid version. */ - is_v5 = version == 5; + is_v5 = (version >= 5); /*timestamp = ((data[0]<<24)|(data[1]<<16)|(data[2]<<8)|(data[3]));*/ data +=4; datalen -=4; @@ -352,11 +404,26 @@ parse_key (const unsigned char *data, size_t datalen, npkey = 2; is_ecc = 1; break; + case PUBKEY_ALGO_X25519: + npkey = 1; + is_9980 = 1; + break; + case PUBKEY_ALGO_ED25519: + npkey = 1; + is_9980 = 1; + break; case PUBKEY_ALGO_KYBER: npkey = 3; is_kyber = 1; break; - default: /* Unknown algorithm. */ + default: + if (IS_PUBKEY_ALGO_MLK (algorithm)) + { + npkey = 2; + is_9980 = 1; + break; + } + /* Unknown algorithm. */ return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); } @@ -397,6 +464,30 @@ parse_key (const unsigned char *data, size_t datalen, keyparm[i].mpi = data; keyparm[i].len = nbytes; } + else if (is_9980) + { + if (algorithm == PUBKEY_ALGO_ED25519) + nbytes = 32; + else if (algorithm == PUBKEY_ALGO_X25519) + nbytes = 32; + else if (algorithm == PUBKEY_ALGO_MLK768_25519) + nbytes = !i? 32 : 1184; + else if (algorithm == PUBKEY_ALGO_MLK768_NP384 + || algorithm == PUBKEY_ALGO_MLK768_BP384) + nbytes = !i? 97 : 1184; + else if (algorithm == PUBKEY_ALGO_MLK1024_448) + nbytes = !i? 56 : 1568; + else if (algorithm == PUBKEY_ALGO_MLK1024_NP521) + nbytes = !i? 133 : 1568; + else if (algorithm == PUBKEY_ALGO_MLK1024_BP512) + nbytes = !i? 129 : 1568; + else + BUG (); + if (datalen < nbytes) + return gpg_error (GPG_ERR_INV_PACKET); + keyparm[i].mpi = data; + keyparm[i].len = nbytes; + } else { nbits = ((data[0]<<8)|(data[1])); @@ -414,11 +505,10 @@ parse_key (const unsigned char *data, size_t datalen, } n = data - data_start; - /* Note: Starting here we need to jump to leave on error. */ /* For non-ECC, make sure the MPIs are unsigned. */ - if (!is_ecc && !is_kyber) + if (!is_ecc && !is_kyber && !is_9980) for (i=0; i < npkey; i++) { if (!keyparm[i].len || (keyparm[i].mpi[0] & 0x80)) @@ -476,11 +566,11 @@ parse_key (const unsigned char *data, size_t datalen, large enough. FIXME: Factor this out to a shared fingerprint function. */ - if (version == 5) + if (version >= 5) { if (5 + n < sizeof hashbuffer ) { - hashbuffer[0] = 0x9a; /* CTB */ + hashbuffer[0] = version==6? 0x9b : 0x9a; /* CTB */ hashbuffer[1] = (n >> 24);/* 4 byte length header. */ hashbuffer[2] = (n >> 16); hashbuffer[3] = (n >> 8); @@ -493,7 +583,7 @@ parse_key (const unsigned char *data, size_t datalen, err = gcry_md_open (&md, GCRY_MD_SHA256, 0); if (err) return err; /* Oops */ - gcry_md_putc (md, 0x9a ); /* CTB */ + gcry_md_putc (md, version==6? 0x9b : 0x9a ); /* CTB */ gcry_md_putc (md, (n >> 24)); /* 4 byte length header. */ gcry_md_putc (md, (n >> 16)); gcry_md_putc (md, (n >> 8)); diff --git a/sm/decrypt.c b/sm/decrypt.c index b4dbceb35..6138326d6 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -559,7 +559,7 @@ ecdh_derive_kek (unsigned char *kek_key, unsigned int kek_len, if (err) return err; - err = gnupg_ecc_kem_kdf (kek_key, kek_len, 0, hash_algo, + err = gnupg_ecc_kem_kdf (kek_key, kek_len, GCRY_KDF_X963_KDF, hash_algo, secret, secretlen, (char *)kdf_params, kdf_params_len); return err; diff --git a/sm/gpgsm.c b/sm/gpgsm.c index a4c222b55..c4442f5da 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -110,6 +110,7 @@ enum cmd_and_opt_values { oDebugNoChainValidation, oDebugIgnoreExpiration, oDebugForceECDHSHA1KDF, + oDebugNoLibgcrypt, oLogFile, oNoLogFile, oAuditLog, @@ -297,6 +298,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oDebugNoChainValidation, "debug-no-chain-validation", "@"), ARGPARSE_s_n (oDebugIgnoreExpiration, "debug-ignore-expiration", "@"), ARGPARSE_s_n (oDebugForceECDHSHA1KDF, "debug-force-ecdh-sha1kdf", "@"), + ARGPARSE_s_n (oDebugNoLibgcrypt, "debug-no-libgcrypt", "@"), ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write server mode logs to FILE")), ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"), @@ -562,8 +564,16 @@ static unsigned int parent_cache_stats; #define DEFAULT_CIPHER_ALGO "AES256-CBC" #define DEFAULT_CIPHER_ALGO_DE_VS "AES256-GCM" -/* A flag to track whether --cipher-algo was used. */ -static int cipher_algo_option_seen; +/* + * Collection of options used only in this module. + */ +static struct { + /* A flag to track whether --cipher-algo was used. */ + unsigned int cipher_algo_option_seen; + /* Do not enable Libgcrypt debugging. */ + unsigned int no_libgcrypt_debug; +} mopt; + static char *build_list (const char *text, const char *(*mapf)(int), int (*chkf)(int)); @@ -824,9 +834,9 @@ set_debug (void) if (opt.debug) opt.quiet = 0; - if (opt.debug & DBG_MPI_VALUE) + if ((opt.debug & DBG_MPI_VALUE) && !mopt.no_libgcrypt_debug) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2); - if (opt.debug & DBG_CRYPTO_VALUE ) + if ((opt.debug & DBG_CRYPTO_VALUE) && !mopt.no_libgcrypt_debug) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); @@ -1330,6 +1340,7 @@ main ( int argc, char **argv) case oDebugNoChainValidation: opt.no_chain_validation = 1; break; case oDebugIgnoreExpiration: opt.ignore_expiration = 1; break; case oDebugForceECDHSHA1KDF: opt.force_ecdh_sha1kdf = 1; break; + case oDebugNoLibgcrypt: mopt.no_libgcrypt_debug = 1; break; case oCompatibilityFlags: if (parse_compatibility_flags (pargs.r.ret_str, &opt.compat_flags, @@ -1468,7 +1479,7 @@ main ( int argc, char **argv) case oCipherAlgo: opt.def_cipher_algoid = pargs.r.ret_str; - cipher_algo_option_seen = 1; + mopt.cipher_algo_option_seen = 1; break; case oDisableCipherAlgo: @@ -1732,7 +1743,7 @@ main ( int argc, char **argv) /* In de-vs mode switch the default cipher. */ - if (!cipher_algo_option_seen && opt.compliance == CO_DE_VS) + if (!mopt.cipher_algo_option_seen && opt.compliance == CO_DE_VS) opt.def_cipher_algoid = DEFAULT_CIPHER_ALGO_DE_VS; /* Must do this after dropping setuid, because the mapping functions diff --git a/tests/openpgp/Makefile.am b/tests/openpgp/Makefile.am index 870a3ad8c..e761f69d4 100644 --- a/tests/openpgp/Makefile.am +++ b/tests/openpgp/Makefile.am @@ -57,6 +57,8 @@ XTESTS = \ encrypt.scm \ encrypt-multifile.scm \ encrypt-dsa.scm \ + encrypt-kyber.scm \ + encrypt-mlk.scm \ compression.scm \ seat.scm \ clearsig.scm \ @@ -229,7 +231,17 @@ priv_keys = privkeys/50B2D4FA4122C212611048BC5FC31BD44393626E.asc \ privkeys/A1ABFD89944870D04039D40C218EE127254AEEE9.key \ privkeys/A87B85D88DB8B2B5A62A9958C8F2878F49605D09.key \ privkeys/D54E9B75C3541D95C45E430DAC9645E9FB62C668.key \ - privkeys/EAD718DCE3D2F33A20BFC8BA617844DEF3FFAF3A.key + privkeys/EAD718DCE3D2F33A20BFC8BA617844DEF3FFAF3A.key \ + privkeys/F27FC04CB01723A4CB6F5399F7B86CCD82C0169C.key \ + privkeys/7FF53317DEDAC5BB0299C0A754D106F0621C8257.key \ + privkeys/4046964D48529FC7DE554542F4A658591BE27AF2.key \ + privkeys/138E37A9418DA4C544BDDCDD11432C01ECEEAED4.key \ + privkeys/8D69D5C7F91781175A899B1453CEF28BAD533991.key \ + privkeys/59BE88218A145F87826ABBD49817ED8F26E7DA50.key \ + privkeys/5F7A0A9BB0B5519F4E2BB2A91B742AE218886888.key \ + privkeys/4C726A3962D6114D5066551A4143FF3A0414394D.key \ + privkeys/2E99977AF7BF5029064E39A12FA866A1A79F89C4.key \ + privkeys/AD0EF479060A5B9DC2B0AD14168770867DE53EB0.key sample_keys = samplekeys/README \ @@ -262,7 +274,10 @@ sample_keys = samplekeys/README \ samplekeys/pqc-sample-2.key.asc \ samplekeys/pqc-sample-3.key.asc \ samplekeys/pqc-sample-4.key.asc \ - samplekeys/pqc-sample-5.key.asc + samplekeys/pqc-sample-5.key.asc \ + samplekeys/ietf27-mlk768-v4-pub.asc \ + samplekeys/ietf27-mlk768-v6-pub.asc \ + samplekeys/ietf27-mlk-nistbrain-pub.asc sample_msgs = samplemsgs/clearsig-1-key-1.asc \ samplemsgs/clearsig-2-keys-1.asc \ @@ -300,7 +315,11 @@ sample_msgs = samplemsgs/clearsig-1-key-1.asc \ samplemsgs/pqc-sample-2.enc.asc \ samplemsgs/pqc-sample-3.enc.asc \ samplemsgs/pqc-sample-4.enc.asc \ - samplemsgs/pqc-sample-5.enc.asc + samplemsgs/pqc-sample-5.enc.asc \ + samplemsgs/pqc-mlk1024_np521.enc.asc \ + samplemsgs/pqc-mlk768_np384.enc.asc \ + samplemsgs/pqc-mlk1024_bp512.enc.asc \ + samplemsgs/pqc-mlk768_bp384.enc.asc EXTRA_DIST = defs.scm trust-pgp/common.scm $(XTESTS) $(TEST_FILES) \ mkdemodirs signdemokey $(priv_keys) $(sample_keys) \ diff --git a/tests/openpgp/defs.scm b/tests/openpgp/defs.scm index c3b05ddb7..5e366e5d8 100644 --- a/tests/openpgp/defs.scm +++ b/tests/openpgp/defs.scm @@ -326,7 +326,7 @@ (for-each (lambda (line) (display line port) (newline port)) lines)))) -(define (create-gpghome) +(define (create-gpghome-with-mode modeflag) (log "Creating test environment...") (srandom (getpid)) @@ -355,6 +355,7 @@ "allow-weak-digest-algos" "allow-old-cipher-algos" "ignore-mdc-error" + (if (= modeflag 9980) "allow-9980" "") (if have-opt-always-trust "no-auto-check-trustdb" "#no-auto-check-trustdb") (string-append "agent-program " @@ -375,12 +376,21 @@ (string-append "pinentry-program " (tool 'pinentry)) "disable-scdaemon")) +(define (create-gpghome) + (create-gpghome-with-mode 0)) + + ;; Initialize the test environment, install appropriate configuration ;; and start the agent, without any keys. (define (setup-environment) (create-gpghome) (start-agent)) +(define (setup-9980-environment) + (create-gpghome-with-mode 9980) + (create-sample-files) + (start-agent)) + (define (setup-environment-no-atexit) (create-gpghome) (start-agent #t)) diff --git a/tests/openpgp/encrypt-kyber.scm b/tests/openpgp/encrypt-kyber.scm new file mode 100644 index 000000000..27258d17d --- /dev/null +++ b/tests/openpgp/encrypt-kyber.scm @@ -0,0 +1,73 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2026 g10 Code GmbH +;; +;; 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 <http://www.gnu.org/licenses/>. + +(load (in-srcdir "tests" "openpgp" "defs.scm")) +(setup-legacy-environment) + +(info "Importing public PQC keys.") +(call-check + `(,(tool 'gpg) --import + ,(in-srcdir "tests" "openpgp" "samplekeys/pqc-sample-1.key.asc") + ,(in-srcdir "tests" "openpgp" "samplekeys/pqc-sample-2.key.asc") + ,(in-srcdir "tests" "openpgp" "samplekeys/pqc-sample-3.key.asc") + ,(in-srcdir "tests" "openpgp" "samplekeys/pqc-sample-4.key.asc") + ,(in-srcdir "tests" "openpgp" "samplekeys/pqc-sample-5.key.asc"))) + +(info "Importing secret PQC keys.") +(for-each + (lambda (name) + (file-copy (in-srcdir "tests" "openpgp" "privkeys" + (string-append name ".key")) + (string-append "private-keys-v1.d/" name ".key"))) + '("DC60E0AE48E0F14E8FD7C9C36E18C6651E99BA93" + "2F4CD0990D56D41A74456668469E3139A7960CD4" + "8B2E1355C97C34E0AC1CBC9DFDF2526BFE8990A7" + "F5DB116462B7BD2FA83A4453C4DFA2AE8604FB59" + "8F9ABF3E5BBFC50D168DD524EB8F7263E7B33859" + "A1598F57316F7FEC3F946895E35A7D2EAE8D3A13" + "A87B85D88DB8B2B5A62A9958C8F2878F49605D09" + "D54E9B75C3541D95C45E430DAC9645E9FB62C668" + "EAD718DCE3D2F33A20BFC8BA617844DEF3FFAF3A" + "702F599E35E6E0BE68E6FDF25D887229D42780F7" + "19C87B74004E9839F3D56992B0A9943BF90B56F7" + "7C31A4A632A49C4E8B1C8CBA53976ADFF714510F" + "A1ABFD89944870D04039D40C218EE127254AEEE9" + "513906BEA5A40F25C9D6EBBCEF62D0784E7235A5" + "6EC551A7895031EE4543A1C789E16E6A6C229CFC")) + +(for-each-p + "Checking encryption using Kyber keys" + (lambda (keyname) + (tr:do + (tr:open "data-9000") + (tr:gpg "" `(--yes --encrypt --recipient ,keyname)) + (tr:gpg "" '(--yes --decrypt)) + (tr:assert-identity "data-9000"))) + '("pqc-sample-1" "pqc-sample-2" "pqc-sample-3" "pqc-sample-4" "pqc-sample-5")) + + +(for-each-p + "Checking encryption using Kyber keys with AES128, AES192, and AES256" + (lambda (algo) + (tr:do + (tr:open "data-9000") + (tr:gpg "" `(--yes --encrypt --cipher-algo ,algo -r "pqc-sample-1")) + (tr:gpg "" '(--yes --decrypt)) + (tr:assert-identity "data-9000"))) + '("aes128" "aes192" "aes256")) diff --git a/tests/openpgp/encrypt-mlk.scm b/tests/openpgp/encrypt-mlk.scm new file mode 100644 index 000000000..9ed553f69 --- /dev/null +++ b/tests/openpgp/encrypt-mlk.scm @@ -0,0 +1,101 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2026 g10 Code GmbH +;; +;; 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 <http://www.gnu.org/licenses/>. + +(load (in-srcdir "tests" "openpgp" "defs.scm")) +(setup-9980-environment) + +(info "Importing public MLK key.") +(call-check + `(,(tool 'gpg) --import + ,(in-srcdir "tests" "openpgp" "samplekeys/ietf27-mlk-nistbrain-pub.asc"))) + +;; Note that we have one key withh several encryption subkeys. +(info "Importing secret MLK keys.") +(for-each + (lambda (name) + (file-copy (in-srcdir "tests" "openpgp" "privkeys" + (string-append name ".key")) + (string-append "private-keys-v1.d/" name ".key"))) + '("7FF53317DEDAC5BB0299C0A754D106F0621C8257" ; ietf27 (primary_ + "4046964D48529FC7DE554542F4A658591BE27AF2" ; mlk1024_np521 + "138E37A9418DA4C544BDDCDD11432C01ECEEAED4" + "8D69D5C7F91781175A899B1453CEF28BAD533991" ; mlk768_np384 + "59BE88218A145F87826ABBD49817ED8F26E7DA50" + "5F7A0A9BB0B5519F4E2BB2A91B742AE218886888" ; mlk1024_bp51 + "4C726A3962D6114D5066551A4143FF3A0414394D" + "2E99977AF7BF5029064E39A12FA866A1A79F89C4" ; mlk768_bp384 + "AD0EF479060A5B9DC2B0AD14168770867DE53EB0")) + +(for-each-p + "Checking encryption using MLK subkeys" + (lambda (keyname) + (tr:do + (tr:open "data-9000") + (tr:gpg "" `(--yes --encrypt --recipient ,keyname)) + (tr:gpg "" '(--yes --decrypt)) + (tr:assert-identity "data-9000"))) + '("3838F412C21DF580!" + "175BCF4EA2AC9C2C!" + "D56E2B801D217D01!" + "B416466A0828115F!")) + + +(define file-plain-vectors + '(("pqc-mlk1024_np521.enc.asc" + "There are more things in heaven and earth, +Horatio, than are dreamt of in your philosophy. +\t\t-- Wm. Shakespeare, \"Hamlet\"\n") + + ("pqc-mlk768_np384.enc.asc" + "You will always have good luck in your personal affairs.\n") + + ("pqc-mlk1024_bp512.enc.asc" + "Alas, how love can trifle with itself! +\t\t-- William Shakespeare, \"The Two Gentlemen of Verona\"\n") + + ("pqc-mlk768_bp384.enc.asc" + "What I tell you three times is true. +\t\t-- Lewis Carroll\n"))) + + +;; Debug helper. Use instead of assert-same and pass output to cat -eT +(define (tr:myassert-same reference) + (lambda (tmpfiles source) + (let ((output (call-with-input-file source read-all))) + (if (not (string=? output reference)) + ((display "\n===========\n") + (display output) + (display "-----------\n") + (display reference) + (display "===========\n") + (fail "mismatch")))) + (list tmpfiles source #f))) + + +(for-each-p' + "Checking decryption of supplied MLK messages" + (lambda (vector) + (let ((file (car vector)) + (plain (cadr vector))) + (tr:do + (tr:open (in-srcdir "tests" "openpgp" "samplemsgs" file)) + (tr:gpg "" '(--yes --decrypt)) + (tr:assert-same plain)))) + (lambda (vector) (car vector)) + file-plain-vectors) diff --git a/tests/openpgp/privkeys/138E37A9418DA4C544BDDCDD11432C01ECEEAED4.key b/tests/openpgp/privkeys/138E37A9418DA4C544BDDCDD11432C01ECEEAED4.key new file mode 100644 index 000000000..a3ee98d0a --- /dev/null +++ b/tests/openpgp/privkeys/138E37A9418DA4C544BDDCDD11432C01ECEEAED4.key @@ -0,0 +1,141 @@ +Created: 20260828T193041 +Link: 4046964D48529FC7DE554542F4A658591BE27AF2 +Key: (private-key (kyber1024 (p + #58DA6E6AE349DAC7C5DC093F97A974FB95B073D27A04 + 10A506186E0536335F8C3BFB58C280B5B27F24A9DCA770C1E13F9177989A174E08273F + F4E19F5AA9693AE0009B098376857075436702C39AF465A1B22A673E605B8DB72EFCB9 + A89282BBA49024A1A82A2A205EA46617A8C84E898248347C732BC24EC56A3923A25D70 + 307CFA40A7BD48211641A3F4D6CBF0422718D30F42A15525936D55E040C99A88807B03 + 1F31BFB5F27F46E7CE5AB260CB968D044618AC43BC72D03A33F4333242CFA065C11770 + 2272780C4105CE78103907420A70EB34C4F81C14F181D2290B74561EFC0CC4B0E94C26 + 936B5754B4CF84BDE661A55C5A350C01207D37207FE3C08AF89F4FAA9536D6071D1945 + BBA6153D55389328217A6620FA6040233C1C0518051B2B01F5021E6A76C1B90B0A534B + A781343AE3345FE76944245692F60B25BC435164988E1733A88AE31B79259C3DCC814A + 107E9DCC6ED760102C6C62AE08CC21C9642480B427E01C0C41230EF395D1C19113916F + B17C183591CF2DCBA2CA7516368C378A9842FC93ACCD524CDD716ECD1B19EF508D87A3 + 60F75A47C9F81437A38DBCC9C343B2B47B230EEFC6A81EF1453E19A3D5DC26CB061BB3 + 670BFDF65A9BC714D9A73DF9F7CA6C36C90DB452BAC54D68D442CD1997483C32D772CF + DD7A6FDBEA219D452DF2538DE54A263FAB67DD028581484FE533A36CC54E3CE905C216 + 49AE3A6072C33DE085C678E6C98D10A55C581E0A73172BFA0D98164611D69EB071227B + 81CD7DBA9846414A6CC33ED4696A9C0CB8169CA5BB873AA4FAB5AF002318D6B5961699 + 1A144A6563CE2D729E76061F8D9017AB420BB5D3913AA0151F2450AD80661F36450579 + BCB7260081F692DA869E43BC37C8647F4CCA2E0EE70AE4A20C62C78A3A26507FBA9FB2 + 405B2D05B5B1C064E2D0BCEF8591E287AC460B869456988BD8C52C02CCB8309448A002 + 0C930278B16D23222152510B67864697C140B78830B4E46F9DC6B45A082C6688ACD6F4 + AACCAC2A22033F23A54E6277CC774C786B39944092A7D31A506215B97512B90618CDC4 + A4B3A2C14EF88BAF4E744F2D993969D61CD612A3E72835F7830FC1C0A7F3A703A38C26 + 84A43013BA9D6248C75D86A3FF4040C09CA910CB2745A707C1FA444F0183D0076E37FC + CDEDA7094AC1670C58A61AB995D09059B465082BDC67F0C63DA0EACADC7C742165A2F5 + 90A2DDF52ED308C560F928E265C849D8A0844702F70A1470642D989165AAF2C181599F + A3C795266A2FA2AC120AC199BCE54EF49673B27C55EF4BCC56780BBAD1784BE585B0BB + 3A7C936FF604C94A5014A757A0A9204503285BC6E32AEBD864DA1A2B80F5010C3B573B + 4649BC5C4EDC9B9C4C189CAB140A6B350DDBC3B68AE03647779387394F002B8D42DB06 + 7604A30CA8245B864F5A419FE9947BD8182D6E0C82F7145DD897CF6E7A5C53818BD112 + CFF0779334621BAA91AB26367B2538A07E1BA1F0F02689320E031686EE0C8FDA966D77 + 484C0EB72F2C507A88DB1419F2AF18E052DE007786EC3E1698C0F9E11309E2603D8C79 + 27B238AD0CCF39E46CF38B3F96D2C1099AA93DB4980A3A1C4B73291A76B39B84B6C774 + 7A1F18AE4506A2AB40BDA7B94B6B913A496A8E5F36A048E03E82FA175DFBA26564C040 + 2C39A25A2BA6F050F1F181BFA452847C9CD44A5E0B204F5A897244654B6D698ACC2265 + 78506F42666CDBA90D87370674DC8F19825DAA4851A2035B36912625B29BFB44A7BA48 + 6C51AA968387084A4B9F6EA908DA840C0497A4601A7A74061AB5924456BC1D08B568F6 + F6A400FD523DE2C2BF119961BB21DD312F39E04C36D81E170598737BC3FE6656EF8238 + 66C500FCB2A827843F6F93290E5761D30962A26BB833A3591B921EDF901F98B09C1A88 + 6A9DD81375D9A05274326D92338E08883A650D37E952B2B426FF884B74B513A7A2AEBD + 83AE6A671E456A570CA53C723A21C0F3235250CCD26790651958734BB96C6A10630B7D + DBC0936B038C01ABC82E9190177221F472568ACC91C8EA319C3842AB93C7BF7630FC20 + 9402E33DDCFBBABD109B8CE03D783470475638A7443BE670C1EE75048366751F744A83 + 816DB9F96DB4F24335784D3DD1773055C2DB79A0D5193B2AD74E54627CF01396FE66B6 + 959394E1BA9B06302CDA746DF381D6F0354F86D92C56BED6923165A2C090D8822B19A1 + 992CB1940164#)(s #A4F0CC4FCBA3F1DC397EF0825863313B736A39405A80445370F1 + 593AC1C3DF282CF03064F2A32E6549222AA4AE15080615EA9B09D0726717CDC9480530 + C696DA1CB74A50589DC6952E2BC8A78774DF80C624455BC5DA8BC7F53E7CBA0B433359 + E0BA55C1236F0E73794B708639A09B7F10839278BC25F67FC7C05A3BA110B3032E8847 + 294AB7165A379B9E5B1B5241302A4A2672F28809EC6E5C45A13E450A01A0684BE437A9 + 53B2BE8AA78A1645A363BF65A19296CB01420416F1A7B096B25B12EB09A7727AD43996 + 4AFBC2DF97CB2AA800E61A5DD2577EB977B25D90C5BC476C59F0112076C146B5451F1C + A860863BD5E8A780AB442637AF2FBC02C5C27841AB720C858D2B1A0CDF4A73FC52B812 + 30AD7341A73F5C5E6BF0AB5F46C2B087BAAA58961AC24C84BBA463C69A6459B56E9A46 + 65DC362CB66F6CBB75A6867C00D2618BD088DAFB76DBF64692D93ACDF9B6E1D8CC8694 + A9BF80184EDC435403ACE5721F4C882A39C020CD92CEAA05BBD2319D47286AB09B1EF6 + 3042FBDC04B7074F315AC3617B6EF6B6847F23C446BBB0AB1142792B5C3BAC8B5422A4 + 9C0582A665601B5B57C7E78E99187435A77068D88947E9407F3C4FA0310CB620867AB1 + 8707BB148C0008D5F43FD4CBB85D9C901F88C8CBFB5B25037FC2940B2203151F46651E + 79BF680433F7EC35B4C28123BA77C0308D46A72224839221346C80B73172178AB3206B + 75170DD647B9B7D95B2B17AC5C973A29C05FB383700ACB4DFC229AAEE32C8ECB7BBB2B + 4071E1A1135A149E8C11FBF09227AC7E4DB23A429647EF4960BC272D174429105017FA + A37737001A48D34D16E179B2D6B880BA44C7E49F66A76AD385349BB603340771D79041 + 15D1276BA65EEA464E1CB34B97109E82BA1EEF78ADD5BA854C630E674444C6AC134F60 + 9A3FF0083EE28D6B0A295DC25861A15F334A1F41970960BC11E9A53BB9600FC2F06C26 + 7B4281DA9EDAA8C307B301C7FA20F240779D600FDBC3A95539CEDA324818B3AAE18460 + DF2C341B784243D61FCC48327BD636CA29C335A8BDFFFAA0B0E90842740C7A765EF87A + C8247858FE35213332BE0910CB6886BA1F17902A617BB54CCE321251F080893783CF28 + C880AB8531D03C68E55B284FAB63E9964B323A8A8B9C2506523993387B733B003B5B40 + 0B801B89852CE4B00A476C7E9906379B419440ABBBBAAB0193882F7FA8812338C88FE4 + CD1394B958D5CA7BD533D9421C6F956C5420365C061B80EB7BDAF350FA4B6AE42C7FC1 + FC23D9C825944B5F1212181A28935436788220337DE819A8867AB129CAC055AA37A584 + EB40244BBB30D7138861F8AAF3A141CE93C518561EF67A80A57C13BAE45A90300323F1 + 4377EC4885A66B9F0C0F81A6A237625EFF2342E5A11768D8A46AF8B60FC4526FA6AF31 + 783E5C84C7B243C91176CE248C148AF57CD769495ADB79EC6B00D2B583D76B3FC2192E + 0559A43AEAABB2A522F5F394249190CD751338431D109488CA28A8E38C9B1B8A306A70 + A36953A7CA5BC505A01554523900563FC0461A9576A1A662CA6DD04911474F431A514A + 038141C94F52B46CB90AC623641D92A960FC761E61B25F5B601CF59103256B89A6FBC4 + F9AB6E0ECC6B3F4A0BB06B26673C96C83412110881F26BC3007C91C755A5E190B04A16 + 12753042721067725C1F9FCB8A6FC8002A29215C661630424D64F0CFCFE277B8822A50 + F426C669CF20A4B9F1890447BBAFBBFA6C26753751423129E25B2024C1FCC49FFC85B9 + 841CB599846712928E27CB78EC623DEF771D8C7137C7335126C936A9B5A47FE55F81C7 + 85F7C95F498472C5C965AD0654E8C88485A92DC9626639E00FFBD465A4A61FA6230F98 + 81C3D5E1961EA18313F683647921B1656A93750A054B491A8239224B606A5355EC8A75 + ED051172E1C3EF979D5473B0F5037B42C9318838365F373918C16C79AA7792C266EDFC + A58371262778A16C2290F40405A059837015BB61055CD1B4AA48E89E2C43C0F6988027 + A51982126D456A4725D3152569B2CD2CA7DF6934E40512EE39C76A4164AFE92BC6C939 + 11E033AA4BCCF0991F0190ABF3850A47A634E4535A893885FA78576BC514EF105E9827 + 144A020943129EC622BC657BBA934A6CD6C2A1F811AA48E3B1EFE9C5142C6EC8899DB8 + 480E6E547B58DA6E6AE349DAC7C5DC093F97A974FB95B073D27A0410A506186E053633 + 5F8C3BFB58C280B5B27F24A9DCA770C1E13F9177989A174E08273FF4E19F5AA9693AE0 + 009B098376857075436702C39AF465A1B22A673E605B8DB72EFCB9A89282BBA49024A1 + A82A2A205EA46617A8C84E898248347C732BC24EC56A3923A25D70307CFA40A7BD4821 + 1641A3F4D6CBF0422718D30F42A15525936D55E040C99A88807B031F31BFB5F27F46E7 + CE5AB260CB968D044618AC43BC72D03A33F4333242CFA065C117702272780C4105CE78 + 103907420A70EB34C4F81C14F181D2290B74561EFC0CC4B0E94C26936B5754B4CF84BD + E661A55C5A350C01207D37207FE3C08AF89F4FAA9536D6071D1945BBA6153D55389328 + 217A6620FA6040233C1C0518051B2B01F5021E6A76C1B90B0A534BA781343AE3345FE7 + 6944245692F60B25BC435164988E1733A88AE31B79259C3DCC814A107E9DCC6ED76010 + 2C6C62AE08CC21C9642480B427E01C0C41230EF395D1C19113916FB17C183591CF2DCB + A2CA7516368C378A9842FC93ACCD524CDD716ECD1B19EF508D87A360F75A47C9F81437 + A38DBCC9C343B2B47B230EEFC6A81EF1453E19A3D5DC26CB061BB3670BFDF65A9BC714 + D9A73DF9F7CA6C36C90DB452BAC54D68D442CD1997483C32D772CFDD7A6FDBEA219D45 + 2DF2538DE54A263FAB67DD028581484FE533A36CC54E3CE905C21649AE3A6072C33DE0 + 85C678E6C98D10A55C581E0A73172BFA0D98164611D69EB071227B81CD7DBA9846414A + 6CC33ED4696A9C0CB8169CA5BB873AA4FAB5AF002318D6B59616991A144A6563CE2D72 + 9E76061F8D9017AB420BB5D3913AA0151F2450AD80661F36450579BCB7260081F692DA + 869E43BC37C8647F4CCA2E0EE70AE4A20C62C78A3A26507FBA9FB2405B2D05B5B1C064 + E2D0BCEF8591E287AC460B869456988BD8C52C02CCB8309448A0020C930278B16D2322 + 2152510B67864697C140B78830B4E46F9DC6B45A082C6688ACD6F4AACCAC2A22033F23 + A54E6277CC774C786B39944092A7D31A506215B97512B90618CDC4A4B3A2C14EF88BAF + 4E744F2D993969D61CD612A3E72835F7830FC1C0A7F3A703A38C2684A43013BA9D6248 + C75D86A3FF4040C09CA910CB2745A707C1FA444F0183D0076E37FCCDEDA7094AC1670C + 58A61AB995D09059B465082BDC67F0C63DA0EACADC7C742165A2F590A2DDF52ED308C5 + 60F928E265C849D8A0844702F70A1470642D989165AAF2C181599FA3C795266A2FA2AC + 120AC199BCE54EF49673B27C55EF4BCC56780BBAD1784BE585B0BB3A7C936FF604C94A + 5014A757A0A9204503285BC6E32AEBD864DA1A2B80F5010C3B573B4649BC5C4EDC9B9C + 4C189CAB140A6B350DDBC3B68AE03647779387394F002B8D42DB067604A30CA8245B86 + 4F5A419FE9947BD8182D6E0C82F7145DD897CF6E7A5C53818BD112CFF0779334621BAA + 91AB26367B2538A07E1BA1F0F02689320E031686EE0C8FDA966D77484C0EB72F2C507A + 88DB1419F2AF18E052DE007786EC3E1698C0F9E11309E2603D8C7927B238AD0CCF39E4 + 6CF38B3F96D2C1099AA93DB4980A3A1C4B73291A76B39B84B6C7747A1F18AE4506A2AB + 40BDA7B94B6B913A496A8E5F36A048E03E82FA175DFBA26564C0402C39A25A2BA6F050 + F1F181BFA452847C9CD44A5E0B204F5A897244654B6D698ACC226578506F42666CDBA9 + 0D87370674DC8F19825DAA4851A2035B36912625B29BFB44A7BA486C51AA968387084A + 4B9F6EA908DA840C0497A4601A7A74061AB5924456BC1D08B568F6F6A400FD523DE2C2 + BF119961BB21DD312F39E04C36D81E170598737BC3FE6656EF823866C500FCB2A82784 + 3F6F93290E5761D30962A26BB833A3591B921EDF901F98B09C1A886A9DD81375D9A052 + 74326D92338E08883A650D37E952B2B426FF884B74B513A7A2AEBD83AE6A671E456A57 + 0CA53C723A21C0F3235250CCD26790651958734BB96C6A10630B7DDBC0936B038C01AB + C82E9190177221F472568ACC91C8EA319C3842AB93C7BF7630FC209402E33DDCFBBABD + 109B8CE03D783470475638A7443BE670C1EE75048366751F744A83816DB9F96DB4F243 + 35784D3DD1773055C2DB79A0D5193B2AD74E54627CF01396FE66B6959394E1BA9B0630 + 2CDA746DF381D6F0354F86D92C56BED6923165A2C090D8822B19A1992CB19401649AA7 + 08719B9E2BDDE7E20CC5567610CFACD18E3F71370A8A3DACFCDF5F85162C5B538E24CF + 1D0982761B218F88A7EC8F4B88DBD5B6081CBF096469DF1E544BD1#)(seed + #1D71A1CBB6F355C55F8F972000B3D38EA86216ACD0674FC2CBDB6271E3112F1C5B53 + 8E24CF1D0982761B218F88A7EC8F4B88DBD5B6081CBF096469DF1E544BD1#))) diff --git a/tests/openpgp/privkeys/2E99977AF7BF5029064E39A12FA866A1A79F89C4.key b/tests/openpgp/privkeys/2E99977AF7BF5029064E39A12FA866A1A79F89C4.key new file mode 100644 index 000000000..d492ac682 --- /dev/null +++ b/tests/openpgp/privkeys/2E99977AF7BF5029064E39A12FA866A1A79F89C4.key @@ -0,0 +1,9 @@ +Created: 20260828T193041 +Link: AD0EF479060A5B9DC2B0AD14168770867DE53EB0 +Key: (private-key (ecc (curve brainpoolP384r1) + (q #048409C3D3E50638AB0F6D39ACB372B66F6E14E824CEA1B805A6D963B9 + CA24BB87AFC7045C21A51B9F58949D311BE429B778AD5E95AD149F7451FBB5 + B2FD901C60DB70D335B3693A0A97E5D65363713429609CDE65A1BC300A68AF + 3CEFAF604276#) + (d #8B37DF7B66CA872067C8A8CC7A5FD18EBE60A73B8F97D585C8F5D974E0 + 9C78A148DD6098E06C22CEC2A8D3D134128F60#))) diff --git a/tests/openpgp/privkeys/4046964D48529FC7DE554542F4A658591BE27AF2.key b/tests/openpgp/privkeys/4046964D48529FC7DE554542F4A658591BE27AF2.key new file mode 100644 index 000000000..c6b4e474e --- /dev/null +++ b/tests/openpgp/privkeys/4046964D48529FC7DE554542F4A658591BE27AF2.key @@ -0,0 +1,9 @@ +Created: 20260828T193041 +Link: 138E37A9418DA4C544BDDCDD11432C01ECEEAED4 +Key: (private-key (ecc (curve nistp521)(q + #0400633A8F74C9BCA632F06FEA3257F5A91488756B2451E9F580DDE4A6C1230000CE + 349065E21E8A4FAB156573EA674B7F58FB60E3784AF3E5385D14787D2A8A890C820095 + E8A38FA866E6C6687802478671296299D1E82C73E25ED9BD78C8D5523FE714F7F4171A + 2EFEB5A6C636B29C93DE1EFA3316E4057798BFD49AF24C22C9C209B7BD#)(d + #01AFB6807FA95BDCB9970BE5F6C106AF5C0CD38A08057FA820399F5F2C2F01EA0CD8 + 11E0F2E504496D84284B24A73944D8A4DAEB13F18B776FE670EC946B0816D8C9#))) diff --git a/tests/openpgp/privkeys/4C726A3962D6114D5066551A4143FF3A0414394D.key b/tests/openpgp/privkeys/4C726A3962D6114D5066551A4143FF3A0414394D.key new file mode 100644 index 000000000..1a99ab517 --- /dev/null +++ b/tests/openpgp/privkeys/4C726A3962D6114D5066551A4143FF3A0414394D.key @@ -0,0 +1,141 @@ +Created: 20260828T193041 +Link: 5F7A0A9BB0B5519F4E2BB2A91B742AE218886888 +Key: (private-key (kyber1024 (p + #CAD39F22C51BCAC4367FE7996A87457129C8680997FF4124 + C43578C3A0BA9D91A4DFF96050B39BEF162E160B93622CC3668514D5559D8892810693 + 9420414365E53B2AEAC4305ABED30786A60193B2E65E07C5749E66947CD6C941097850 + 18AB23910FF81550D01650855501AD34389D614419C36E721297A8357CC9C90888C032 + E1371C93F3548AA03540879EDAD6A7B42265C4C2577E74914DE140FAA9C4A73B6B8EFC + 04E5826E22807FB0894071C98492D99B0ED6745BA851AA5951FF8C839B0A5935D41B28 + 58C342C9A6BC2AA3CDE7BDCEC1CFDAE2138DF2B385B7B56D27C234C5063B232691447B + 657B642DB22B681A27D86B8E19651FBDB9416140794D318A4518BB564B357B5C8161AA + 013A92728629A363896EEFE25CA6AAAE5D780579A249AB9262C4C40C6EF64DDAA7A026 + 6CC87E7BC745F75C12AA82B98025AF0B91987C3E17CCB4FB1317649BA47767508DB20A + F0D490A631CC21215C333515F5058ABCE0656287A0FF3B76B381ADE0883734A007F7C7 + 468DE16D30D945BFF117E7E5B853A2A92C521E5AB6012B9986246C818B2BCFAEE7AB6B + C01DEBF7359B9615CD2C7379612F7986B23EDA25D494C1FDC6912F8B9023DB0F45622B + D5826707440AC8847F00DCB3B917B984EA99F1CC7C2D84BAEDB2720011ABC876CF6E00 + A4FECB427B334E502BC14DF0AD7946AC816108068A301E382CEDBC9B89D6B6A0121D90 + B78BC3DC10BDB41C15398FAA21BEEB70BAD392B69A3885DE5664E1101B7C399056D4CA + 763002D9B6C0E119B8F7856208A84CF518A1E81297097BB829376D610B40D3DA52B880 + 147B8C48965805F6327534CB2DEA59087EE37908BC4E344379EA2B356812870975279B + E09E1358A2E73353AC51A15803C1208373B1CC5889F030CF760B32A58812691A85B486 + B3324AF06880CD63376B11B96B77BC3EB036648816091334132810B4B6610C4862C5F0 + 35BBBB47CCE1AD7088B74FA279538343D5DBCBF3089582A378B52B2229DB790906450C + BA802D8AB6426BA5A6615B9F93A118F99E338B67F9EC11C4755E75663C3AB105B1F6CA + E0F983D968832E63281C16ABB676AB30601D2AC7616216934F696A49742A8AA9057593 + 283CBB6E77561DEFA145D7E530D4935A8F13A9A9E465FAE49B457B5B403329FBC96988 + B61D988B68C9ACA9BFE42E9A88198706AC098B683079B2833C1881C24DFF9A7B3BBA95 + 808619132B4E21A86E8601672CB655C9415CFACB847F437596824B86A88435A1665BB2 + 171985615DB59EBD30B47500439532092174C512F6CEB8141500C56B53A7C7A8A73B28 + 8847A87971263723708319EBA2BBB10BBC16230C5BF379B86A03D65A122F052ACE32CE + 7C5BABB92C175C40B557188244F42B01331218183823D9161733BD5021B012726C46B2 + 0150650B6CA566470AB8D5F9CAC7E044F77459A4220305CCB40EE971B093BEAACC80EB + A47E66CA941C7C57F52169C06782C77BCB9834BCD04129D6A3360E44B50923C670B25F + 27086167516089E7CDC4CBBC66B278C40463F503BB5323B86C4836CE99C810512C5118 + 152C608A696CA51DD17326C7244A177C55C229B9293999EA13339706B03170673282A0 + 88267EB28F84427383B3B8A5A06D52F0174ECA05E64BB09D5601C4252629318E71D701 + 3ABB69F428C3EB019F5E2B234F605EB8E8B643B7CBC8A4960C3CB8D66B60EF00600311 + B27E6BC44BD9B91BFABAF59A8708E7B027F20E56D7AB81A65BB75C9EB38A452EBBA94B + 3308F866A89FB1203D370CC0A0B30A50B61D7B0514511C1B86A56E9290D406285118A1 + 04A3387A859D31642BBF250977968C86A7B1B82ACC945124D8B58C03BB6B07698B34EB + 5B30502AA4432C7A144AA68C822B5C41F1E6AED12B9D4F8491732B5762F073F285C5F9 + C584D44420ED3461291B4EA943A37504C94DC5A204A0876E42073A2483FB3A5C26FA11 + 10FBA40E294626524BA9782163D0888FB9AABAD59F326CCEF7AA6110588337D55882E7 + 85B3803CE930B85EA203FD0C9256855BE68CCD0B82487568A24B36C6C87158E185CBBD + 0C1010967530CCAA700005FCB47E7B046C8A2A5C7C35426520855ED08F31955CD1121B + EF1413A1004E9F12CDA4A0A828E6A7D7713B08CBACE6AC9575EC54303C3D8DF764F67B + 562DA0836ACA1C0F2E6D2EC2CED92FAF5A4BC22DFFD8D901A438F9CAE4C8C7F1938958 + E47916E3#)(s #FA3225B5267269001F509CB843C40A0967574BD791514228CCBA2794 + A17CA45684F2D03F1DC4921A7295E6B4A4323803867562FF604C57C51BF4488D2B385A + 5905B87A472841293033009CC635AF27B33E8FF78C893A09F4893ECEA13A294C9569D4 + C1EA50A3AEFAC2B5168049B119548245AFFB9934E70DA43C720D754BBF833589B720A4 + 53206176640AB52A0405A5F7F40F1E2BA295FA6E3AF304862C29BBA2618EE6BD58D837 + 97D506EC1CBA2256C1B032C958F99A46EA52CE14A78A8C3975296A2D2B664B4BA99D11 + 59330965714106260B1AFC8A1758DCA34D8C3B07D8878F26487CB1BDF322869A37CAE5 + 941F275831FE710A94D4B1BFC455B0005540037E39FA8124B48B13ECA51FC6A283BB06 + F8255305AB7F49166CB7731D416910B2584A37199A7DFB06AD941E7EB056F555A94989 + 19DB825018202E8744983E44BA9552547F3A8A0B4375B2C011C795197118C92A8C44B2 + 60C4E9CC1B7B3AA62D01515C6677182479AAB393C8757CC5188DD275C30DC83B38D078 + 5815C418EC790AF20A54A991F4B785D11B5FDE7B6016E8A5BF7626CF823A715BA20DD2 + 13C73417049C4F85B263ACDA6750B68C36C733EBB0CEBE88C7CF6A75BE224532781ADF + 4181C491BE768AC9CD893647E09E8BFB0392A08D99E67771C86BA4F5790BA739892533 + 70B9AF15B16057D697201B93CEB470D0CAAB3CB79D508A3E9C646D0695B7C042856DC6 + 52E7120E4A4130E8D899D3F8544528373A0996991BA63B7B0C38976B028CAC952A63C6 + DBC990EA84D5C95723D840ED0C3E21208C1E311D0DBAA9A4535218A2A872E361F4C544 + 3016047CA9280C6C68E8494F81006A1583B702254512EAB6EEF86ADCC8C3A4B15309A5 + B8E9F354CF0243B2E772BF370D0CFC9D36457F64D78126E322C9D5AEE83B787063AE12 + A1CC7BB77636307A7C227B6F7685D12361A148511209895B5A2C9BC982DB7850507C9D + 2A2AC57BC06F5604480C4601045925430C6785D0A0F6418EEB5CB7BCEAC6CF35119ED0 + 49F79A00FDB86974E6B1D2B5655F32C855109DB2DCA5BBF44F934B7E5ACC61D8A94344 + 762693A11651151E32C858B9E3184F05AFBC2B2DEA3BCCEB587F34A75FB4785DD3C269 + 71218E50EB329AB48B8D69AEB9F02A424030BC41CCCC1594EA182C536480A5F996DEF6 + AA12C25B745663C266BBDC57CC2599A65164874B7978B16C8E96B2A22C6A26830C2E3C + 596BE1368B5D09324E277D7B347AE7891545F98A600B916ED17159C624507CCCA8A4A7 + F627B79B575529D416BA94BC7FAB3F33E57FFA593EEAFB2C81728314390DC2B485A380 + 29562573FA2B0264F00F7552A3724827881683F177632177C5AE12B923B53848D136BE + 46AA5407330405C63DC475B5B1012192882410BFE71A45E79BA1515B142FAA89167C39 + 72E4A2B1D7B290C24B56D47A7BA909EDA68F853697FAD2ADD123B6189B1617D6635D9A + 1E2834258E93494410BA13F82914AC1F9F6A12608C5437BB88E17B797594779B764D8D + A50CD56959D4802F15BBBEF8317AAA869F2B6778334035032137EB1917E52C5C7C6905 + D2B277A61083EC417E33D5B116A565484CC511E6AFF5F464DC46C3A3849D3469B2C5E9 + 05322B8B05018F9D1933754C03EB1B1EC0C93F20161A429058F2DCBFA0A54FDC396EB1 + 4099A236BCF8194BBC9C04009B1E48F13FB779CBC7762E6EE83BA50CA0EFD1448AF523 + 74E561CACA14D48097F927A95A6999D95C3ABABB9F69658DD385C3ADFC106AD92078D6 + 9F4C2520A4A6B4338235E1749AD9A828B520239CD29B51EBCAB13AA2ADD9732AFB5DD2 + 00A95F136FF8A343E205998F5A5797106D4C9748B026B702C362293959EC2194E9101F + 3A1A2517B0AEE3153910946F009BC776E47EC6AC0B82D39A152B025F9553098B83794B + 2C54B32D43491D8A06A499307EAD33BE096943665C50BCC05815A2A4FE010395D5CAF4 + 08058FB0CDAA2042A4D21D76905F4D86BA5CA17E459B939AA5B7FADCCC17414F89AA31 + B8129D9BE431CA051AF570088D718972D43101D8C059AA2F70825BBFC7009863A73739 + 13860B235820878AF1CFEFC406E6FA4BBA6C02211ACCF68869F98737D95B336AB14536 + 58A4C7408AFD2025502AA6D0724F0B588D6F1B2B8CE2720FD565E4D9061258A4535028 + 17858ACAD39F22C51BCAC4367FE7996A87457129C8680997FF4124C43578C3A0BA9D91 + A4DFF96050B39BEF162E160B93622CC3668514D5559D88928106939420414365E53B2A + EAC4305ABED30786A60193B2E65E07C5749E66947CD6C94109785018AB23910FF81550 + D01650855501AD34389D614419C36E721297A8357CC9C90888C032E1371C93F3548AA0 + 3540879EDAD6A7B42265C4C2577E74914DE140FAA9C4A73B6B8EFC04E5826E22807FB0 + 894071C98492D99B0ED6745BA851AA5951FF8C839B0A5935D41B2858C342C9A6BC2AA3 + CDE7BDCEC1CFDAE2138DF2B385B7B56D27C234C5063B232691447B657B642DB22B681A + 27D86B8E19651FBDB9416140794D318A4518BB564B357B5C8161AA013A92728629A363 + 896EEFE25CA6AAAE5D780579A249AB9262C4C40C6EF64DDAA7A0266CC87E7BC745F75C + 12AA82B98025AF0B91987C3E17CCB4FB1317649BA47767508DB20AF0D490A631CC2121 + 5C333515F5058ABCE0656287A0FF3B76B381ADE0883734A007F7C7468DE16D30D945BF + F117E7E5B853A2A92C521E5AB6012B9986246C818B2BCFAEE7AB6BC01DEBF7359B9615 + CD2C7379612F7986B23EDA25D494C1FDC6912F8B9023DB0F45622BD5826707440AC884 + 7F00DCB3B917B984EA99F1CC7C2D84BAEDB2720011ABC876CF6E00A4FECB427B334E50 + 2BC14DF0AD7946AC816108068A301E382CEDBC9B89D6B6A0121D90B78BC3DC10BDB41C + 15398FAA21BEEB70BAD392B69A3885DE5664E1101B7C399056D4CA763002D9B6C0E119 + B8F7856208A84CF518A1E81297097BB829376D610B40D3DA52B880147B8C48965805F6 + 327534CB2DEA59087EE37908BC4E344379EA2B356812870975279BE09E1358A2E73353 + AC51A15803C1208373B1CC5889F030CF760B32A58812691A85B486B3324AF06880CD63 + 376B11B96B77BC3EB036648816091334132810B4B6610C4862C5F035BBBB47CCE1AD70 + 88B74FA279538343D5DBCBF3089582A378B52B2229DB790906450CBA802D8AB6426BA5 + A6615B9F93A118F99E338B67F9EC11C4755E75663C3AB105B1F6CAE0F983D968832E63 + 281C16ABB676AB30601D2AC7616216934F696A49742A8AA9057593283CBB6E77561DEF + A145D7E530D4935A8F13A9A9E465FAE49B457B5B403329FBC96988B61D988B68C9ACA9 + BFE42E9A88198706AC098B683079B2833C1881C24DFF9A7B3BBA95808619132B4E21A8 + 6E8601672CB655C9415CFACB847F437596824B86A88435A1665BB2171985615DB59EBD + 30B47500439532092174C512F6CEB8141500C56B53A7C7A8A73B288847A87971263723 + 708319EBA2BBB10BBC16230C5BF379B86A03D65A122F052ACE32CE7C5BABB92C175C40 + B557188244F42B01331218183823D9161733BD5021B012726C46B20150650B6CA56647 + 0AB8D5F9CAC7E044F77459A4220305CCB40EE971B093BEAACC80EBA47E66CA941C7C57 + F52169C06782C77BCB9834BCD04129D6A3360E44B50923C670B25F27086167516089E7 + CDC4CBBC66B278C40463F503BB5323B86C4836CE99C810512C5118152C608A696CA51D + D17326C7244A177C55C229B9293999EA13339706B03170673282A088267EB28F844273 + 83B3B8A5A06D52F0174ECA05E64BB09D5601C4252629318E71D7013ABB69F428C3EB01 + 9F5E2B234F605EB8E8B643B7CBC8A4960C3CB8D66B60EF00600311B27E6BC44BD9B91B + FABAF59A8708E7B027F20E56D7AB81A65BB75C9EB38A452EBBA94B3308F866A89FB120 + 3D370CC0A0B30A50B61D7B0514511C1B86A56E9290D406285118A104A3387A859D3164 + 2BBF250977968C86A7B1B82ACC945124D8B58C03BB6B07698B34EB5B30502AA4432C7A + 144AA68C822B5C41F1E6AED12B9D4F8491732B5762F073F285C5F9C584D44420ED3461 + 291B4EA943A37504C94DC5A204A0876E42073A2483FB3A5C26FA1110FBA40E29462652 + 4BA9782163D0888FB9AABAD59F326CCEF7AA6110588337D55882E785B3803CE930B85E + A203FD0C9256855BE68CCD0B82487568A24B36C6C87158E185CBBD0C1010967530CCAA + 700005FCB47E7B046C8A2A5C7C35426520855ED08F31955CD1121BEF1413A1004E9F12 + CDA4A0A828E6A7D7713B08CBACE6AC9575EC54303C3D8DF764F67B562DA0836ACA1C0F + 2E6D2EC2CED92FAF5A4BC22DFFD8D901A438F9CAE4C8C7F1938958E47916E3F68DCB7B + CB5A149681F3CDF55FC2EE4BBB3A23A162E17603E5F79F047CDFA704A865C75DE8667F + 13A700E0726BB961E67AAD12BD135C9F8A6384E5285C77A004#)(seed + #E8F9EA3C7F7D44A1BA905971BA6D8C0923770A46E5E489A2346158F2042FDB66A865 + C75DE8667F13A700E0726BB961E67AAD12BD135C9F8A6384E5285C77A004#))) diff --git a/tests/openpgp/privkeys/59BE88218A145F87826ABBD49817ED8F26E7DA50.key b/tests/openpgp/privkeys/59BE88218A145F87826ABBD49817ED8F26E7DA50.key new file mode 100644 index 000000000..80a1eca59 --- /dev/null +++ b/tests/openpgp/privkeys/59BE88218A145F87826ABBD49817ED8F26E7DA50.key @@ -0,0 +1,108 @@ +Created: 20260828T193041 +Link: 8D69D5C7F91781175A899B1453CEF28BAD533991 +Key: (private-key (kyber768 (p + #F9ACB5F49966CD9A8FA74B848E1759F32C0DAD260A2889BBDE218DDC39B17D667A7A + 91683668931A415D0E20784C81300D9316B78A2280D53BFBE02B4BF283828926038637 + 8AF7983AF35FF52311C98C800DDB2A7F797D26D623E9B44313B9508F43AFE0753A7B4C + 0CA842CB1D322B283CC93C682F377821A7C05CC7A0969EF4A3C7C4871E3917CBA8409B + 24A001BB8417973743CA1C229B9FBD9421631178C4E1BD8EF300BF1C35C89A3089E259 + 099AAF53E45DAF9BC445369681BA64BDBCB55CD24E1F78BC5044BDE8873CAFF42FE206 + 521C565C26706A494B1EF7E8B613711BB501B4D7D6B341515A8393B868572396AB86FC + 20BEBB950956E3454177BEC9677389A3524E630A87E3CC149346BB6215950C7387D728 + 7C96AED7B75DB836C97F246EA871640294A0415A307569939726548B88999C68BD0F29 + 96169A4ED1F03462B87CE28B1AAC29B540511004BB874D44C890596FB1D4B769E28E6D + 16A9691329349C1228DCB577701C18215EEC4945EEEC978AF85B4605AD9CF65BADE23C + CA4502D38697456516897A76F7C3A65C5025D56697EA139C501A9AE5553B9E96386C96 + 11C2272BB4D5B6B574A0F60791FD883682137EE7F96A24DB8846778EF6268A2BFC505D + 0619B15B94DE076E11206ED475AE8DE90963BA12D73815C185B68DE04189BB8DAE5A82 + FE868D4959877E1CAE0630B22D41A55495251E26838E68469A5489E5020E05B2476177 + B4D5413E1EF012974A9B5544C268B0A11FA23513829872F363F8E80D5978861EE81971 + 406EBF0A67A921C5992C0611D31DE43BA9A7CB1390C9030509989D4ACF4F9380F20604 + 4E7C81FDAA75B4463BF96041DDE970BE82828E374F4A34C759E64B67359230CA8C1199 + 2D1D4968F22358FF8C2FA12A2A53D83A08AB34D65137B5AA63FC678C71C960A6489789 + 3283266B4BCD738E2F6B45D01C12A9370C6DC040E8E5C79190C88AE88B9FB9BF0E150F + DE5555DB5A43D4D0ACDA891327F5AF1ACBB94F32A3E005CE3EF92D0AD045AF721F02A9 + 8AB947549A815C96117E63BB23CDA130F21C4ACB9612873006AAF37830E2112189A3B8 + 672533D709ABF2CED7D04BDB013A637C42DF9930BC0BA4FC07C10E3AA89FFC4342163A + 802687087623D7914C4D789D4AE51430F977C444CBBB1B927489B90EB3B5253B3286CC + 6F214A00E591B852FB048C0762D6304E914B47F88C9DB76860E1448E04B83C86175457 + 420437715468A02B58B693559AAE61B3A14EA62185A0AC1EC2C2CD210715F5B30F3181 + ECF259EF536641939D0C6A8AD0F168169B5BE200820D32043C3A051D925E4E2298A84C + 0F0B541AF3A8170001773B45B548C92696CC5CC7389D40A3B725691A413B52DE75AEDA + FC16B111491DE6AE72F0963ECB7EE040AFD971A1AF1463BC396450A619826479D2577B + AA060D87D47A693AAE187A0509DA98A1532908C8740752AF6B574113A225BF464391E7 + B082FA738F474B05247525E75CC8B4699294A8A458659E3832B05762F9C2612E5AB794 + 208BFA6141C5D714C948562D243941B354EE43C95304C842201E07E222DB9653296479 + D9270EB19939CE871D4FD4C89B7530DA8C26F8EB0658C0809011B734E46D786C501896 + 4D1A0D94EDDBC438ED9908B1B62B48D0CE36F941614BC9C040688535CB56#)(s + #1A9955FCAC2D891525C023B2EE839BE9C468BBE09DAAD14D19C6C4A80049AAE95EB9 + A68BD1787FA2C4A1CBB384901308207931AA6383F258A915C431261A7FE3761E0E0BCC + 91D7857CA07AEBA72F57BA9F4167790FF0644C0524DC779A7BC5CB0BE02B6B870EB568 + 4A21B2821ED487302684F412368CBC999BA79C327580B9680ED742B4E8FA60815486BD + A42A9D39A1BA37C8A9DC320DC1C771C2957ED674263BCC99951098CB7546613903B6B5 + 11D542E8D42A150612475267CD8B772BE66E00A51870ACA6C1D0ABBE6B92760196ADF6 + C71A660415C06A304BB99295B1B332188F5A7A7723CE309C9D72A660027945F4A8AD73 + 731BBAD885FC20587F314ED4FC435232197397450F4400ED2AB0F5CA4AAEC06F5F4475 + 25F75AC82996806541C3D61235493B29668F48880033A78ED6717CE2878045F84E4369 + 4124C87060D771B57A153707C4B7A8372D1368DA78CAE3A23F7A5222583A89A3284D89 + 2C73DAEC8A0D8A592A82BF6CBA5C364600805A807E392A706939E259C78F14A5879597 + 6C19772DB28362A0C642D8720520C6E36298E5288BEEF169D6092E84D1A02D4CC8B178 + CD1B53AF41A62281BCC73BF2944C003721DB0930A9AF499767D08CB634A5823C61AD52 + 818A6B71B79C8B9867269F61ABC622D1BE79DCA6533428B404361FCBB0162B8C729880 + C33044E815AB71224A9EBCBF1E882464928F4018B639CA77A014685D1948C9B7493678 + 824FD37CC72835DFE88525F530CAA493828A8A03660A3A1C508CF468AE9255B6A1A924 + 08402697BB16A74379B946A01A476D1B524CC854E0373FCD8A8BD384175A428DAAD5CF + D9D7C5BBE4BCEAA76BB59A7C9DD352E0CC9823CB414F503579C875CC6149C5A84FC54C + 7F6BD2AE26EC42C4C728078926B7458A9BD19989286CCA3B9751B78F9435C533C46C68 + 90C4F6FA70E8C7779AB281FDA80041678F0AECBDF85C6E7E1295F9C04D4DE70AF4CA57 + 2257363C406240E9939F93C19FC73161E11CC727A2AEC67A38D77A45F748BBC1283391 + 3F24B2335A76B3E182200E105731107D72EA3BCC940D6FC0341EE67326B54EDFF10D0D + A19ADB4B6197428C917551C6417663B9697F205AD05B46078735C8DB1BE357A5DE4ABE + 1AD27F4C836403F0855C481700B357B0925A5C46077B659DB1904742C44374E089EB39 + 46EE290D6D76CF89B0AD4D92112C2A354E1C4BAD209D0F416687D5CAD6F964CAF34A65 + C22FB721580CC050C19A2EADC452ADCA3327186933729C8898016E07C952D83BF84952 + 94950A0EF3506C183B1E2B44F4AC5FE02338E2485F15C3BDF30A7C3BE253996B58AEC6 + A9FFF4275F672D42511D2738BAE8136D821A0E7150B173133C5583B81B756322B7C0FA + 162EDF608BB0D66D06840176EC515EC56D9158B518087EB7818F9814A71861636B5658 + 25977146B32480D2594FC83F750076B45B1C37164DBD40595829608420954E63BE55A6 + C055853364593036218A970BC72CA7B5AE0B9A845383BD28CBED8B20503384A8527549 + 60241E4413B3F90A7A2C787222905204AE1F4446E281ABC97354C4E712C000C07EAC52 + 5B69758F379A598370637261D430807139545670A8B0F27DF78040C0832FFEC98FF9AC + B5F49966CD9A8FA74B848E1759F32C0DAD260A2889BBDE218DDC39B17D667A7A916836 + 68931A415D0E20784C81300D9316B78A2280D53BFBE02B4BF2838289260386378AF798 + 3AF35FF52311C98C800DDB2A7F797D26D623E9B44313B9508F43AFE0753A7B4C0CA842 + CB1D322B283CC93C682F377821A7C05CC7A0969EF4A3C7C4871E3917CBA8409B24A001 + BB8417973743CA1C229B9FBD9421631178C4E1BD8EF300BF1C35C89A3089E259099AAF + 53E45DAF9BC445369681BA64BDBCB55CD24E1F78BC5044BDE8873CAFF42FE206521C56 + 5C26706A494B1EF7E8B613711BB501B4D7D6B341515A8393B868572396AB86FC20BEBB + 950956E3454177BEC9677389A3524E630A87E3CC149346BB6215950C7387D7287C96AE + D7B75DB836C97F246EA871640294A0415A307569939726548B88999C68BD0F2996169A + 4ED1F03462B87CE28B1AAC29B540511004BB874D44C890596FB1D4B769E28E6D16A969 + 1329349C1228DCB577701C18215EEC4945EEEC978AF85B4605AD9CF65BADE23CCA4502 + D38697456516897A76F7C3A65C5025D56697EA139C501A9AE5553B9E96386C9611C227 + 2BB4D5B6B574A0F60791FD883682137EE7F96A24DB8846778EF6268A2BFC505D0619B1 + 5B94DE076E11206ED475AE8DE90963BA12D73815C185B68DE04189BB8DAE5A82FE868D + 4959877E1CAE0630B22D41A55495251E26838E68469A5489E5020E05B2476177B4D541 + 3E1EF012974A9B5544C268B0A11FA23513829872F363F8E80D5978861EE81971406EBF + 0A67A921C5992C0611D31DE43BA9A7CB1390C9030509989D4ACF4F9380F206044E7C81 + FDAA75B4463BF96041DDE970BE82828E374F4A34C759E64B67359230CA8C11992D1D49 + 68F22358FF8C2FA12A2A53D83A08AB34D65137B5AA63FC678C71C960A6489789328326 + 6B4BCD738E2F6B45D01C12A9370C6DC040E8E5C79190C88AE88B9FB9BF0E150FDE5555 + DB5A43D4D0ACDA891327F5AF1ACBB94F32A3E005CE3EF92D0AD045AF721F02A98AB947 + 549A815C96117E63BB23CDA130F21C4ACB9612873006AAF37830E2112189A3B8672533 + D709ABF2CED7D04BDB013A637C42DF9930BC0BA4FC07C10E3AA89FFC4342163A802687 + 087623D7914C4D789D4AE51430F977C444CBBB1B927489B90EB3B5253B3286CC6F214A + 00E591B852FB048C0762D6304E914B47F88C9DB76860E1448E04B83C86175457420437 + 715468A02B58B693559AAE61B3A14EA62185A0AC1EC2C2CD210715F5B30F3181ECF259 + EF536641939D0C6A8AD0F168169B5BE200820D32043C3A051D925E4E2298A84C0F0B54 + 1AF3A8170001773B45B548C92696CC5CC7389D40A3B725691A413B52DE75AEDAFC16B1 + 11491DE6AE72F0963ECB7EE040AFD971A1AF1463BC396450A619826479D2577BAA060D + 87D47A693AAE187A0509DA98A1532908C8740752AF6B574113A225BF464391E7B082FA + 738F474B05247525E75CC8B4699294A8A458659E3832B05762F9C2612E5AB794208BFA + 6141C5D714C948562D243941B354EE43C95304C842201E07E222DB9653296479D9270E + B19939CE871D4FD4C89B7530DA8C26F8EB0658C0809011B734E46D786C5018964D1A0D + 94EDDBC438ED9908B1B62B48D0CE36F941614BC9C040688535CB56D5460AE65C8327CD + 15CF3EE73F6369CDED0DF84334ACA61B4A8AA6CB93310A17A819B914C453458D30950B + 62D2FDE5A35F83E4678D8746F423A291D3482B8430#)(seed + #88D6C8E74EA3D6D08731334ABB8162459C66E96984A977F87802A87BC464044BA819 + B914C453458D30950B62D2FDE5A35F83E4678D8746F423A291D3482B8430#))) diff --git a/tests/openpgp/privkeys/5F7A0A9BB0B5519F4E2BB2A91B742AE218886888.key b/tests/openpgp/privkeys/5F7A0A9BB0B5519F4E2BB2A91B742AE218886888.key new file mode 100644 index 000000000..e361cfcd7 --- /dev/null +++ b/tests/openpgp/privkeys/5F7A0A9BB0B5519F4E2BB2A91B742AE218886888.key @@ -0,0 +1,9 @@ +Created: 20260828T193041 +Link: 4C726A3962D6114D5066551A4143FF3A0414394D +Key: (private-key (ecc (curve brainpoolP512r1)(q + #041B6B202AB9DA942BA2C0181C0CE8DC57E3A52199E5583AE7CFB75E2FAB30F3FB78 + 934FC286290C37F712D872D401CC81727C2DDE91F814E7C47B4B24D7186C559B813B52 + ED8089AD124084A05ABC10440381570B62F58964EC31CF5C203B992CC28685409312AD + 8315FAB8FB23C37015A65DE2B7FC5BF7CB97BF7FF09E2015D7#)(d + #A4FDA631052A6B94FA7ABCA3A879C1DD4C77E29C9A610C46A74590A01278128F85D2 + 50A8BA511506FD3D444987BAECD205CCC3FAAC564378275E87681A779F4F#))) diff --git a/tests/openpgp/privkeys/7FF53317DEDAC5BB0299C0A754D106F0621C8257.key b/tests/openpgp/privkeys/7FF53317DEDAC5BB0299C0A754D106F0621C8257.key new file mode 100644 index 000000000..6b046d490 --- /dev/null +++ b/tests/openpgp/privkeys/7FF53317DEDAC5BB0299C0A754D106F0621C8257.key @@ -0,0 +1,4 @@ +Created: 20260903T124406 +Key: (private-key (ecc (curve Ed25519)(flags eddsa)(q + #72A17F2752E2FAF59598AF0039A41C921C3402FCED171FD1040D887379F40C13#)(d + #F7686EED6E7ED4C57D4BCB3C65662E30F19076334C0F1C06EFAD113DFA8D5B52#))) diff --git a/tests/openpgp/privkeys/8D69D5C7F91781175A899B1453CEF28BAD533991.key b/tests/openpgp/privkeys/8D69D5C7F91781175A899B1453CEF28BAD533991.key new file mode 100644 index 000000000..b320608bb --- /dev/null +++ b/tests/openpgp/privkeys/8D69D5C7F91781175A899B1453CEF28BAD533991.key @@ -0,0 +1,8 @@ +Created: 20260828T193041 +Link: 59BE88218A145F87826ABBD49817ED8F26E7DA50 +Key: (private-key (ecc (curve nistp384)(q + #046B5B4964473F027ED46C609B49A57DAEA07B5D9C8A6CD179AC1FDB10733F9CB264 + 64C795F5B4289DFB3BBC383389D3497F1D8D36D529BFD15DD5C01AF68CCBCC3C5FD5ED + 7E580E447E798B6650C7E31980780109C8EABDA60E21EF19106FAD56#)(d + #3C83A872F5A1D6C724EC655116FF093DFADB155A24E77C3CA0ADB7ED38BBBE90CA34 + 7B3F4E0B3E9E5D73FFE3DBA0EFED#))) diff --git a/tests/openpgp/privkeys/AD0EF479060A5B9DC2B0AD14168770867DE53EB0.key b/tests/openpgp/privkeys/AD0EF479060A5B9DC2B0AD14168770867DE53EB0.key new file mode 100644 index 000000000..444321851 --- /dev/null +++ b/tests/openpgp/privkeys/AD0EF479060A5B9DC2B0AD14168770867DE53EB0.key @@ -0,0 +1,111 @@ +Created: 20260828T193041 +Link: 2E99977AF7BF5029064E39A12FA866A1A79F89C4 +Key: (private-key (kyber768 (p #8764C6249BAABC179C0E0A53C1BCCD703BA9A9A1 + 833B4264A451CF55818769788992A51624E2C5E7352AE4759B7BE438FA715DEF2610 + FBD94C4B23A791C5B637243FEB068747283060B6BE478102DEF06A4D3208361BCE98 + 957F4984817A749ADC6B5D13F611E95C6841D720C192A94B31600A8A2F01538F8D41 + A168FAB39A35326C91AE5C681064A62F612B371A779941F130368100624530526507 + 6E697B122CA1063BC171760B33E0AB3270263A3059C5696F79E5AF8C009A41977AD6 + 9769DFA69D0DC55C1845484F676F009242A5389AD8D570CC3BA45CD53A0D14399371 + C9A6773B49F48B7D76C7D0D8B075E9651626C189A8958D461E0DB0791C1B45AA0A0F + F68A4E90232523765781C83806E6760852C0493A5DFC91BAE6445CF3EA9725EC51CE + CA5665C166EA431D53426F49152DE095578F0CCB2D9716D9D1AA14B03DD84311F6B9 + 3FC353295E262AEA858BD54C1DA5203A1554560FE75F22E12883D48095C1B0B2EB5A + 96295BA7815ED6064C4E51B311D23A67B6A577EC34E5E374556494E1F4135CF24F27 + A50457D16D6F565B152A3B0371381735198491242D7B67A0991D7952CFA3FAAB4E8A + 2FE34BC5B0B6573A729224FA243E73977E307CD309BA0DE637D69862FEB5A66C06CA + 9DD769790AA7BBFC693AEA7F78EB257F5A46E625BCE89969834381F64B127E794B71 + D99F632514CA8927B0256309D88A6E6BB0C2A6034038509BC584E720227410976F10 + 9E52D302CAD62B2EC27DAAD1BBC480535678247F4928B667B764E37608626FDB60BA + 5BD82BCDFAB13C8511EBD4B64EA4B1700A5CBA832C709B2A3E6C9AB87B0D67255E67 + 1907A12467A5C7A9E30063E8EC569C33B5EE6700CEDC0998D4978855B053DB0C82A7 + 02A864475C664864D92201367DDBCA8756C83C13A581E286AEC6548E004D97976506 + 9EB765A58873FEF5A7FB9B3B76F54BA068554369638DCB55A45195899685EC4005C9 + 3B2B986C422901C5ED9837F3542EFC2498EA45174854299D2AC7BA862646758FA00B + 9693EB3F54C351DD36518A5937B6F937B6026E126151E4A0C4FD5838592C6419D9A3 + 1E343CA2A421E7A88612FB771D1BC389A9356410ADEB468AB396B946F830B48431EA + 375ACC3C5C0D8839A714A96B7A45FECB4985C3B179E567CCB341CBB71C22E9C532E9 + B2A7F6B55D135BB741B07E298DD2F8529A139273E1BBB8C818E1BC5068F53559D92F + 3B05850B47B9FC434D7842A35C197A7094C6208041F0E9711571761C25219A7A9562 + 6654C4763101FAC42D34912B232F47861E0579119DE17822853641410D3E1505343C + 8D15535F2FA21A970C0E7B932A1D0B3045662B2B09B7B39A91E0EA6704F08B7DD016 + B4F74E77030F75B66D2D697AC0200640D81AE805C028FCB70B936334C73E771273C3 + 811B6B8979EEA7B09189CB17864043D9C2A3C80CB9B57960E28B9045101CD0373F65 + BD14CBCEA5079AC8446F62E96C0B60CBC854CAFDD959E7BBB2A0C2457908C4CBB2B7 + 2748B9F4B72A893437C1305EDB3C3446BB90341A256B2A19F36B15BA8C446984C70C + 8CAA0FC40D6208AE7C767A160051A648757A495255290DB86192BE3518E74115680C + AD994416AEC13F639C1A6D225764E198F5C50107ECC11092C34716475403D8EBDD91 + 4760D5BBC6467F6D#) + (s #9F49C536D519573B4D15C12C77F507AEDBA9FD95C95CC423C8036BFE574E83 + 56312247A61847A4FD807752DC5698EC3FFCF884D42166FF199287F406BEC8063507 + 4E60FA2CC30B72DAF96FFCB4287BB7C86EE319DBEAC816DC2303DB2F32B8BF102792 + 532B0C1AE2BA443C1790A6828F8B2D1E0A5E13883FCBC00438E57FCF78C71C86082C + 127B5F5ACAC6F45628449F378006DE960B71017998891C0599554B56A4FF9669C353 + C134F889B271021EA1741A1071E835C816472149725D9A1284AF9BA566D7A5EFD94E + E5050E928074F6CC97A7A24C0990028E5AC63F28B789997BF646157F214BD20CA865 + D87DDF832727F436F7300E9C535A552232F150C60898B797FBBB0833428DE9B94E26 + 45E87ACDAED1896602A3A2C1B74F90CE9E2950B7AC2B96E162D8EA6BE0A47AD48441 + FDA79DD36CA284638E7E733E00C50ED212CAA65985F67C2BD933BF940C0407E9705B + 821C5B67C84123603F2043C4A41D42C5AE9CB43F4AC5763CD78A29FABB02808A7460 + 2A99B73269F33A6453A11E234431F434366879B0612962DA1E2383BA92058BD63423 + 01EC4D73618C1DC7BF6B522651B88A515547572042D6E05A8CC153C7739B89CA5B24 + 144F9D23B864D390321679A902067EB5699C2A8E3E768B70756DAC605524C26F7660 + B7F6147FF562C41CD388CD9426A23C391854B27232BAC9107A865154A36BC76BF633 + 8C609416603914FA1AC2105AD0EB3C74635B3EC3A7CD1892C3493CEACB707212C777 + 128CB78C5C60482C8BF372FC1C0372CB358911772EBC6F99E752FE718263854EE759 + 5EF9506B5293B494E027814138668740A5A19D13417CA9557041BB85EEB35E91B974 + 7F4185B81ABECA2615F60B44FD45938884CFD801BE819C8CEC274B3AB79EB39798DA + B139504C3082C7AEBC75366B5CC144063DB1512114D555A61A3490809C84E2913194 + 5EDF6A7322484B1EC4B9D71B733F068E98484F9DF8802D9000D1999F104B726DA391 + C147690CA936765A6612964536F019A37A3EAEA732CA3AC621129E16686898302DA3 + 851125F3742976B1ED6701579587C8D376BD9C43AF153B9EDC4E926192EC765B0CF1 + 40272A4A69A10168094B4A7A255380A15C24635709971DEBC532A0611F589CFFA23D + 2B90C34A971B4E088747962FC6BA12AA510CB6B84C81FB9813400B55C09B46F47AE8 + D78FA53AB4CAF42F6365822563243BDBCAB317C0F1A53C6230B02471B613C89084A1 + 50B02183AC96491F224CF1B28061C31471280917F04670A9257815BB83752D23F752 + 5FC616735C1D3385A6528CCA14F287B0564AEEBA14732256C9FB6F93B46B2AE40725 + C29EBBB9854262BFBE0008CB4376722831DAB24E0D007081F6B7DB6013FCB5418628 + 3C94F4B30F6642046C0673E7A88E320F64F18AAA602A53500777194094376E8301BE + E7A203C880A319FB80CDC2CFF362372ED1B276823BDC3171E441CD8B52A2EE65B307 + C626E64477DDE5C94BA503E65007F1D4AC5BC5449499B02398C00000253F2842F172 + 055DCB4F0B66113FB73F6BFB1288541291B3A908E3192DBA013F36B5D925AABE5338 + 1EF45AE2D64330D31E198017C1EA5E75153B7E18A5250A6EF716C62506AF39205B87 + 64C6249BAABC179C0E0A53C1BCCD703BA9A9A1833B4264A451CF55818769788992A5 + 1624E2C5E7352AE4759B7BE438FA715DEF2610FBD94C4B23A791C5B637243FEB0687 + 47283060B6BE478102DEF06A4D3208361BCE98957F4984817A749ADC6B5D13F611E9 + 5C6841D720C192A94B31600A8A2F01538F8D41A168FAB39A35326C91AE5C681064A6 + 2F612B371A779941F1303681006245305265076E697B122CA1063BC171760B33E0AB + 3270263A3059C5696F79E5AF8C009A41977AD69769DFA69D0DC55C1845484F676F00 + 9242A5389AD8D570CC3BA45CD53A0D14399371C9A6773B49F48B7D76C7D0D8B075E9 + 651626C189A8958D461E0DB0791C1B45AA0A0FF68A4E90232523765781C83806E676 + 0852C0493A5DFC91BAE6445CF3EA9725EC51CECA5665C166EA431D53426F49152DE0 + 95578F0CCB2D9716D9D1AA14B03DD84311F6B93FC353295E262AEA858BD54C1DA520 + 3A1554560FE75F22E12883D48095C1B0B2EB5A96295BA7815ED6064C4E51B311D23A + 67B6A577EC34E5E374556494E1F4135CF24F27A50457D16D6F565B152A3B03713817 + 35198491242D7B67A0991D7952CFA3FAAB4E8A2FE34BC5B0B6573A729224FA243E73 + 977E307CD309BA0DE637D69862FEB5A66C06CA9DD769790AA7BBFC693AEA7F78EB25 + 7F5A46E625BCE89969834381F64B127E794B71D99F632514CA8927B0256309D88A6E + 6BB0C2A6034038509BC584E720227410976F109E52D302CAD62B2EC27DAAD1BBC480 + 535678247F4928B667B764E37608626FDB60BA5BD82BCDFAB13C8511EBD4B64EA4B1 + 700A5CBA832C709B2A3E6C9AB87B0D67255E671907A12467A5C7A9E30063E8EC569C + 33B5EE6700CEDC0998D4978855B053DB0C82A702A864475C664864D92201367DDBCA + 8756C83C13A581E286AEC6548E004D979765069EB765A58873FEF5A7FB9B3B76F54B + A068554369638DCB55A45195899685EC4005C93B2B986C422901C5ED9837F3542EFC + 2498EA45174854299D2AC7BA862646758FA00B9693EB3F54C351DD36518A5937B6F9 + 37B6026E126151E4A0C4FD5838592C6419D9A31E343CA2A421E7A88612FB771D1BC3 + 89A9356410ADEB468AB396B946F830B48431EA375ACC3C5C0D8839A714A96B7A45FE + CB4985C3B179E567CCB341CBB71C22E9C532E9B2A7F6B55D135BB741B07E298DD2F8 + 529A139273E1BBB8C818E1BC5068F53559D92F3B05850B47B9FC434D7842A35C197A + 7094C6208041F0E9711571761C25219A7A95626654C4763101FAC42D34912B232F47 + 861E0579119DE17822853641410D3E1505343C8D15535F2FA21A970C0E7B932A1D0B + 3045662B2B09B7B39A91E0EA6704F08B7DD016B4F74E77030F75B66D2D697AC02006 + 40D81AE805C028FCB70B936334C73E771273C3811B6B8979EEA7B09189CB17864043 + D9C2A3C80CB9B57960E28B9045101CD0373F65BD14CBCEA5079AC8446F62E96C0B60 + CBC854CAFDD959E7BBB2A0C2457908C4CBB2B72748B9F4B72A893437C1305EDB3C34 + 46BB90341A256B2A19F36B15BA8C446984C70C8CAA0FC40D6208AE7C767A160051A6 + 48757A495255290DB86192BE3518E74115680CAD994416AEC13F639C1A6D225764E1 + 98F5C50107ECC11092C34716475403D8EBDD914760D5BBC6467F6DDD284CAD5EAEE5 + 2D4EB26C18E9D1F98BA0A20257BDDC972A9C73CB19E771B1A644C181EA085C21F52D + 8A4CE86A25C5A6099ED6A887678758BFD487AC751EAFC7#) + (seed #E969B778AE4B45327051BBF95A16850BF0F31EAED69DD82C5507A0D0A835 + EB2844C181EA085C21F52D8A4CE86A25C5A6099ED6A887678758BFD487AC751EAFC7#))) diff --git a/tests/openpgp/samplekeys/README b/tests/openpgp/samplekeys/README index 88361ee30..ef062a852 100644 --- a/tests/openpgp/samplekeys/README +++ b/tests/openpgp/samplekeys/README @@ -28,6 +28,9 @@ pqc-sample-2.key.asc ky1024_cv448 public key. [*] pqc-sample-3.key.asc ky768_bp256 public key. [*] pqc-sample-4.key.asc ky1024_bp384 public key. [*] pqc-sample-5.key.asc ky1024_bp384 public key. [*] +ietf27-mlk768-v4-pub.asc RFC-9980, A.2.2. +ietf27-mlk768-v6-pub.asc RFC-9980, A.1.2. +ietf27-mlk-nistbrain-pub.asc NIST and Brainpool subkeys Notes: diff --git a/tests/openpgp/samplekeys/ietf27-ietf25-v6-pub.asc b/tests/openpgp/samplekeys/ietf27-ietf25-v6-pub.asc new file mode 100644 index 000000000..39ed3d530 --- /dev/null +++ b/tests/openpgp/samplekeys/ietf27-ietf25-v6-pub.asc @@ -0,0 +1,26 @@ +This is a version 6 keyblock from RRC-9580, A.3. Due to the missing +user-id this sample key can not be imported with gpg w/o changing the +gpg code. + +pub ietf27 2022-11-30 [SC] + CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9 + Keygrip = BA5AC4919D62C0B28CA8FD2A947D79DA54F06B27 +sig! CB186C4F0609A697 2022-11-30 [self-signature] +sub ietf25 2022-11-30 [E] + 12C83F1E706F6308FE151A417743A1F033790E93E9978488D1DB378DA9930885 + Keygrip = 3DE6500F567D5196B2FE33225BFAC442DBE56E73 +sig! CB186C4F0609A697 2022-11-30 [self-signature] + + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf +GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy +KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw +gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn ++eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh +BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 +j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/openpgp/samplekeys/ietf27-ietf25-v6-sec.asc b/tests/openpgp/samplekeys/ietf27-ietf25-v6-sec.asc new file mode 100644 index 000000000..3231539aa --- /dev/null +++ b/tests/openpgp/samplekeys/ietf27-ietf25-v6-sec.asc @@ -0,0 +1,28 @@ + +This is a version 6 keyblock from RRC-9580, A.3. Due to the missing +user-id this sample key can not be imported with gpg w/o changing the +gpg code. + +pub ietf27 2022-11-30 [SC] + CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9 + Keygrip = BA5AC4919D62C0B28CA8FD2A947D79DA54F06B27 +sig! CB186C4F0609A697 2022-11-30 [self-signature] +sub ietf25 2022-11-30 [E] + 12C83F1E706F6308FE151A417743A1F033790E93E9978488D1DB378DA9930885 + Keygrip = 3DE6500F567D5196B2FE33225BFAC442DBE56E73 +sig! CB186C4F0609A697 2022-11-30 [self-signature] + +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB +exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ +BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh +RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe +7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ +LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG +GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE +M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr +k0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK----- diff --git a/tests/openpgp/samplekeys/ietf27-mlk-nistbrain-pub.asc b/tests/openpgp/samplekeys/ietf27-mlk-nistbrain-pub.asc new file mode 100644 index 000000000..b21609f09 --- /dev/null +++ b/tests/openpgp/samplekeys/ietf27-mlk-nistbrain-pub.asc @@ -0,0 +1,172 @@ +sec ietf27 2026-09-03 [SC] [expires: 2029-09-02] + 1620EB12B7A8EE2D38CD1834ABD1CF0E094A44D30C73ECA6A198089705116D37 + Keygrip = 7FF53317DEDAC5BB0299C0A754D106F0621C8257 +uid [ unknown] rfc-9980-gnupg-sample-1 +ssb# mlk1024_np521 2026-08-28 [E] + 3838F412C21DF580C7B8A316BE850C3F26684FC713FEE9801D4D1227294DD094 + Keygrip = 4046964D48529FC7DE554542F4A658591BE27AF2, + 138E37A9418DA4C544BDDCDD11432C01ECEEAED4 +ssb# mlk768_np384 2026-08-28 [E] + 175BCF4EA2AC9C2C35809D071B2549C9DB1898878863D12D374C5A0C794F0AFE + Keygrip = 8D69D5C7F91781175A899B1453CEF28BAD533991, + 59BE88218A145F87826ABBD49817ED8F26E7DA50 +ssb# mlk1024_bp512 2026-08-28 [E] + D56E2B801D217D01601AE80CCB146655004B3EB7F8329DBE59EF8C081E5EB41E + Keygrip = 5F7A0A9BB0B5519F4E2BB2A91B742AE218886888, + 4C726A3962D6114D5066551A4143FF3A0414394D +ssb# mlk768_bp384 2026-08-28 [E] + B416466A0828115F0DC503A3FC7BA4A9A29D89ED12B7C16792237428619C5F51 + Keygrip = 2E99977AF7BF5029064E39A12FA866A1A79F89C4, + AD0EF479060A5B9DC2B0AD14168770867DE53EB0 + +The primary was created by gpg. The subkeys have been taken from +https://github.com/openpgp-pqc/draft-ietf-openpgp-nist-bp-comp/tree/main/test-vectors on 2026-09-03, subkey packets extracted and added to the primary key + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +1uO6b9DodBh6I7y7pXUF +=ve75 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/openpgp/samplekeys/ietf27-mlk768-v4-pub.asc b/tests/openpgp/samplekeys/ietf27-mlk768-v4-pub.asc new file mode 100644 index 000000000..326910cca --- /dev/null +++ b/tests/openpgp/samplekeys/ietf27-mlk768-v4-pub.asc @@ -0,0 +1,38 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/openpgp/samplekeys/ietf27-mlk768-v4-sec.asc b/tests/openpgp/samplekeys/ietf27-mlk768-v4-sec.asc new file mode 100644 index 000000000..28a98b028 --- /dev/null +++ b/tests/openpgp/samplekeys/ietf27-mlk768-v4-sec.asc @@ -0,0 +1,41 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +-----END PGP PRIVATE KEY BLOCK----- diff --git a/tests/openpgp/samplekeys/ietf27-mlk768-v6-pub.asc b/tests/openpgp/samplekeys/ietf27-mlk768-v6-pub.asc new file mode 100644 index 000000000..ee4867693 --- /dev/null +++ b/tests/openpgp/samplekeys/ietf27-mlk768-v6-pub.asc @@ -0,0 +1,40 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/openpgp/samplekeys/ietf27-mlk768-v6-sec.asc b/tests/openpgp/samplekeys/ietf27-mlk768-v6-sec.asc new file mode 100644 index 000000000..3910bae59 --- /dev/null +++ b/tests/openpgp/samplekeys/ietf27-mlk768-v6-sec.asc @@ -0,0 +1,43 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +pujFf59V4t3iZxgQfgvs0+vrcSsE +-----END PGP PRIVATE KEY BLOCK----- diff --git a/tests/openpgp/samplemsgs/enc-9580-a-8-5.asc b/tests/openpgp/samplemsgs/enc-9580-a-8-5.asc new file mode 100644 index 000000000..7867ab777 --- /dev/null +++ b/tests/openpgp/samplemsgs/enc-9580-a-8-5.asc @@ -0,0 +1,10 @@ +Sample encrypted message from RFC-9580, A.8.5 + +-----BEGIN PGP MESSAGE----- + +wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO +WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS +aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l +yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo +bhF30A+IitsxxA== +-----END PGP MESSAGE----- diff --git a/tests/openpgp/samplemsgs/encsig-9980-a-1-3.asc b/tests/openpgp/samplemsgs/encsig-9980-a-1-3.asc new file mode 100644 index 000000000..d4c77966e --- /dev/null +++ b/tests/openpgp/samplemsgs/encsig-9980-a-1-3.asc @@ -0,0 +1,38 @@ +Here is a signed message "Testing\n" encrypted to the key +ietf27-mlk768-v6 using the 9530 SEIPDv2 method. + +-----BEGIN PGP MESSAGE----- + +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 +ngqFrEkdOzaOHiIZarV74y3G3wZyzXobjp+dpA== +-----END PGP MESSAGE----- diff --git a/tests/openpgp/samplemsgs/encsig-9980-a-2-3.asc b/tests/openpgp/samplemsgs/encsig-9980-a-2-3.asc new file mode 100644 index 000000000..7b5f433d6 --- /dev/null +++ b/tests/openpgp/samplemsgs/encsig-9980-a-2-3.asc @@ -0,0 +1,37 @@ +Here is a signed message "Testing\n" encrypted to the key +ietf27-mlk768-v4 using MDC encryption. + +-----BEGIN PGP MESSAGE----- + +wcPUA6T5X5he1hpRI8oKxrVQiCkB27ePKVHeA4pTYMKZA6u1l8syrP2+sEULDgvB +GmH6+0mTw07VEh6J1i1+3ymnnTqLhkv3YqdBtiC81+PL05YPCymPZaWf0ajq+4sM +dnBfLJ3BPrsJw03sVHIBh+L3qolG0CliIzGKxIPz9F5RBSvDdSIwCNg9hnfZjpMu +kcmceYISpWjJR+LeAieyYOTZ+Qhx71jYQ2svfpwW+XAw03uMpZkvqkOJmYr8uUca +i8x2j4G6EUXuu9NswSPPirCqU6OZVdpoHUZusFyRZz89V10fQr9hrnJOGw0VtPGz +SMEulSosvnvnK2BQ2ccJVNn0s/mk+fttQLpBBsKCH0UK8norIXt5ahxdj9sSwBTf +q6cPlHz1o9OnFSuewFkapA4PuLxhf4YY8ZTsC9LUZLiMf8MrMza7gbtnEbBzW3bx +y6QD6I+PneJl/8M5a7ECrlFuR2p3Kyt6MTiY+6sxJ1GOVhpNS24iO/LRxAUe8DRi +tFC46oEQFByp98SIWt3JoJKHcjQzLKTjRWfYhZDiUnBkoM6nYaAZdItcFsBDG3IV +1UstcmfcCugJyWi8V8XHVKdhWe3bWc1WrhieDCVpfSBD2NnRMGG+g90WtHcwhntf +n/mwyR/GLG+gRc16I5hPm84lS34+/txx745yDXdTx/szZAqQw0VW47CwF17A3wdw +cX1UDnUnf7/llFKqg8Zn/GXGIEreo5q/83Ib7dehm50APhtaKnQzoPbPPu21lw+8 +/3gisYxQbmrphEpU2KWWWrG5g8P3JG/D9wlHwDhhXPCdNFB7wthQbaDQ1WnN3WlV +BEtWMOqLTIovjrxHbn5judqLYQQgZPbMguzj5JXrQM7wVu4o0edv967oI6ZRBg4y +BtwlXWF3cgMvvAFfH8fXGXAtJw5Gz4/gxzan1q1JcOm6Akgp55J37LlPeXIKHiyX +W/J8qEbk0XgSSa5VduPfnP7AzrLWtyT5B9lqPszmR7euLXvb+tCuNa/G/ldceJO8 +6MKJVXYuYnk2qpuNWV1NPlCUH583LH2xgJ1YNk6U7ID4opcBMJuVM+MTA7cmLObE +Fdoa2TvJu5rpyrnqedHPNgE98P95kZJ/UtaIqJL+zzGkD5rip70DJPuQ6CkVwX1o +pTx2EKwi3c8H5QZtZUkLYeh8x/1LidZeLBdMLut1Lc7BmD0j0wU0PrwaSLsWAYmW +L16/cY9xPwIqmC8nST8rH94QoBl5eFkm3HrDYjJZrNybk0RKP5oLKG8QoUO2kXyW +9TfKNplK0YCfGTgKcTK6luDSM2cCdwlPdspwCRLPX7L7LZYaK8nEfAFD6Al8ZC45 +meBQgY4SuDlucygbFAHitrXLv4ukBDNRFxe+2dVzig5ryHZj97H5vp89aYH1W3gh +GS/k57744ziY/ACTz8cRxVKgM1oTYjDsNKvmM2J6ij7vRxuMBvM/kO7g+0jD2hPV +eWyIS1VKeT1LG7sRBd+GVQ9jHKpP/7YonXYUrOkpCdG/5YOX6Doo3VlVTRi00QmC +t8716eEJLd7ivorFYFELY5eOEUcjmNLSwEgBr2kPm1Mkn8RS8mCNT188nXgTzZk7 +jZ1rurNkkqQM53xMaLmQImS+N20GPoa2RdHW+R/veP7LugLO7gozMi/zz9+kcd0Y +wPahWnYsZ4uHg/zbgFMIH/iwQ04nv58gOLJfJafGarwotFvBIfl4Nd607lmVJTc0 +OjVhhisWL5WDIC82+DDu0yDLH/huzY4W7/ks3Hn/UpEwzq+A1/bY3MbQomew5hTI +99z/IeulfiT8/0POofN3lvMvTzeGuMMsBiMFp2nbCEHrwWCN9uaE3eAbj3E0OtAM +AIIfxypiV+bYm0IA58t7Ur3kMG1KZmcKG4DF5zrL1u5ArX/T7258Z7shYff7WtNU +Wfo= +-----END PGP MESSAGE----- diff --git a/tests/openpgp/samplemsgs/pqc-mlk1024_bp512.enc.asc b/tests/openpgp/samplemsgs/pqc-mlk1024_bp512.enc.asc new file mode 100644 index 000000000..c6246d51a --- /dev/null +++ b/tests/openpgp/samplemsgs/pqc-mlk1024_bp512.enc.asc @@ -0,0 +1,45 @@ +-----BEGIN PGP MESSAGE----- + +wcYuBiEG1W4rgB0hfQFgGugMyxRmVQBLPrf4Mp2+We+MCB5etB4oBJZKGf5Q4LEh +B5unHmeQNyBT1nlUga+JMQXwBwJxCS4sLypuzPfoYZjS7WmjHG/eMmd/BmUu5ya4 +HaXvo/ZUT6oQQ3jN2mJEWY9zNsDfDVVxuioBsgDEcdPwO0v2zSbtgQiU3ty6qqon +El3DjT14ATMvGdTo60lyvwc7pdGr4c0tt+Abl3fpADFK6Tkymm/RVANi9RF4xjzr +kAEvn2353B/bsxpo4dBcz41HFsVUwhVb2VEpYstX8xWdNmXLd5C1E5oIcb67uoz4 +/oevgMMExNf/995LPqsQqa57Tm8R+Dair4HoQ3VZwsXfxriRboP09GIojTVoKV2o +QjAdW2wf38F1Xln4xcl4kpvFFMUeLfDrvIFYFNRML4wsp6nYPZQWQoNIB3TBBRIQ +VrG5qN2oCddm913OyqCRa6icp4rqDmr9SZg/APg1lCyXp1H7PcmbuX9VpXP5QiqM +RDxR6PkoTbJX8CsGDPfqoTb5YKrCIXURnxUcX+rBKzLn423WpGBHgZVAs1vtLeQK +Qs8NHpfYQk0kG0UiQoPyM4KblWerDFIMrVUTei1guhRYLtdWKIaVxRyIxv1heCer +/d7+K7FrJ1MjxpcyOPYmgQbQ+cmP+sRHjlSTaU0/xMHATfowclAhCkhrBAB/7Rck +iwGdiaWxjCuTALwZtZdNhVLCszkZq8YXX5R47cZObVt6yE2ixVtxndyrgK97SREv +4y+iKwYrzuPfiGLB2rrrIyiY8yExqTV+l3pjUO1NvdVpdSTvQqMtdAkk3XSXz93h +XAoYxbEvacIovePeNDu2jweaVLs35QilTkKWKw+u1NLzEmLOZreeBb3AxsDmXMvj +px58OOBCQ3DelJbClkWjXeL3xO44NwyKxVIauiXnSdXxEqeO69e4nK5XRdSjh0eH +Y16JB9jxDpK9lvFsgyt4mqWc3On4EtB/uUH1gERkVlG28RgrpRCTAjUl6NzdbYxn +/F+pyDEH3benw2ZbDneSvKeWrWyE1pmsUjem9lZK/RK8hRsl/VRm/rZOu60lT2KV +E6bXQIOhjETT4GIovAw7PB+/POYu0aqRf2K0O/oIGgwe1bdC/hormDSbZ2S0VBqz +Ce4rihYGYHtwtJRVM7E4nOeH2CvjtAP6Is45Rh2aa4cmaLg+2H5W/bvtZAjuUIyf +I3m8jFaMuQp6oQcobHt860suwU2YjQkX9i7VcZ6ghL9KyMSevks15LE6cnw72Efk +G5cGVEyNvzoy4dPVjPeponvzjkbCpTGXAhIWlI+0WwG/7LtEWbi/ZkIUYyqcH6+4 +VZtO653IoZOcuenPIJhFc/YiQ2WS9gAJoWU/KaTGc2Kq8q4ujbWDIqaMopkmPMVT +gdCv+zvStuWTS50XidG8WpwrEQcmPM45rsQOyyosKY4jbV2RNZHqpKXwpEkfff5H +8a4Uxl3D9dH9SS+MnxlUUhNbS7szURAlpnGv+1sQuFGOV5vxndQHDJv1HjhW4ruK +fZyR9eHXaTGJM0q3nG3dRlGm0tWhNw4zA0sCtJZTCuXViFxfsaQeI8+anGyNsWI4 +HzRx/1AkqkI4Lg/chLYzHr07tapY8fgIZ6goksZqxsl+wgBbqCA86haUn0DTAz9i +hDzBdaZAma9i6AQ1SklcSEApB3P9gzNAOyWoqjQJAaI2f1bHhT/jMW9bfk1nkUab +3MlnNLJtbJHoUDPO7voKkfb+5NdI7vC2QE1iuT0/SU7g11EFQ/6Oelbe01xex/FQ +Seh6vdP4cq6nUpfIO6yOznqZO/UTXk6SsXYsHm9+ZufaoCqAZUxnfjtN2M32iY64 +NSJu8b44GDh6MXZDRcCCmhte5Za7SXRZM2dR6AI4Je8ujpjrwV0cX4wIur3ximpJ +TUzz/gU8Dzu+oAttESfJyeSI6sAQCY+eeisC9oS1imbyo/MckWIJQZmu7m2A4czk +uHShGcCGAf2r5NKpHoaq+O/Z2OtNSdOfI/THDKn646QuX9qkHtPb2LaiDYYbDlht +Ob/Ah+sg9CkoGetuVQpyQ0da+H13O23hgzfhoU5/8zka5Jr77BJF1B6kW31D7mQv +eJ51r/EGgqrByLzt1tv0GDx6ZdiHTMr+AchgD0/YEyt7sJNoKDQjQQAC8WJOtfFn +3qPu5BenceDNc32Cn7+3q5CNrPR1KfPpENHq4CyWCioIt7iVLMgH2gHdNuUfbcdr +xUIEo8/e2DAdt85chc8V+6Qlrn00Wg7OhWWKNnZesEEg4zE5CDBvwZDIqDhtnEJd +g2SPfiybKS0o81odVmq0IBmLBEqbwJbdZwI/i+OBQcG9YMPdfIZWxim5ppjlGSoy +TtKuAgkCEIClH2oqq0Xd6VC21srOU9OwKEHAqjpBIjHXfuMFTsXF5dJl5EAJybSA +Cwp431LH1pVCKjW/VIn4Yrm6Ffy4EHh5R6tnwljRn/Ri8o5DSJViJpcehCPmZaEK +IOcrBz6mtMwkziy+qVdgSqm5NVY0TX8jI+LV1dhMnmIFIvwzStc6ArF4hypQ5+IN +n2RbMUcRXRc+ySabTXQEjMnEfAABn9vM2kFL0oe2o56d +=qQ3r +-----END PGP MESSAGE----- diff --git a/tests/openpgp/samplemsgs/pqc-mlk1024_np521.enc.asc b/tests/openpgp/samplemsgs/pqc-mlk1024_np521.enc.asc new file mode 100644 index 000000000..bc8abbd4b --- /dev/null +++ b/tests/openpgp/samplemsgs/pqc-mlk1024_np521.enc.asc @@ -0,0 +1,46 @@ +-----BEGIN PGP MESSAGE----- + +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 +iqMKDGHts0j6LA== +=pm0W +-----END PGP MESSAGE----- diff --git a/tests/openpgp/samplemsgs/pqc-mlk768_bp384.enc.asc b/tests/openpgp/samplemsgs/pqc-mlk768_bp384.enc.asc new file mode 100644 index 000000000..558a092b0 --- /dev/null +++ b/tests/openpgp/samplemsgs/pqc-mlk768_bp384.enc.asc @@ -0,0 +1,34 @@ +-----BEGIN PGP MESSAGE----- + +wcQuBiEGtBZGaggoEV8NxQOj/HukqaKdie0St8FnkiN0KGGcX1EnBFDoZ8B5K0HT +qHx3+yd+QHx6Pn5Iq1aG0+Q+1s6p90p/tWfH0+KIUh70bCeBTme5pA3jC23K2dN+ +H3KLtxryl8jzhhExffp0kk2d0SzwcHFP1fLYnp3KQiycc+clTL9PW9YhyTqpoD+9 +BmWvUC7p3Jf4X1ZLbkcPdVigN2H9m5kGxr0YLpDEWNigi5H1PcHZ34FyZ+g4uxEL +5NUcki5r2hcwq9JrZttCGXwPeUo8ejTttUcVRZZ+706KlgjIkFcFzjZu/tULcXYa +huMbD+BJjjw8wiscsp8Gj6Lz61oFE10kK+nWPoJ9pZLLkF03+LH4VUMC6t7RWd7w +mZFVd4Eusq8ry6iy7pgSo9aPRLN/qHhGiubq0ds3zG+TuxDKQwLQUhO6Nbmbs7+q +MRX7UNp82GInMIfej0MYJWHOQ8iXXtYzAmocUMO/hgrFrKRRXMe76xv8GYrz0gOP +zMUxXW1JI2S4wjP/akzp9KnaCwjzW1FYJ9YT3snMmHfJrorygjJoNDux4Vi8sNTV +t1Ocent77LJA4mTVhnTxpsqW3bLrNf2K/lCYk/s8gvT2lxrfrbg7eH0S5Lp2x7gV +lRKaGJohwICoy8Rurfxeq2zIq0Fu70KclE5mqdEVv90A7uv36Q1NQOY/Zd/6jbbE +RyidmNn3A5KDwHzilSiiMb6R+udSQE8DR7C2Fp7anlkoPbXoPmqngia7d+UYBXtw +pPWnKw4gJXrPOxfrMBLkya57T6oHCkmfActGulD5fXQD4L3K3EcQlrJDk0SVYrRS +c66FgJLJKLq4svtiFVBmuflkviwGU1INbSo9DRoD4AlsNHTYEKb57lO3ZgAeNXkn +D9/BCP+j8pdjwnsA8geypeWZ6AR2/i8DgIiUQT9R5QXZQ81mOxvVEFGOmw7bmIFB +XNm8Sq+6b2YBd00bs3pQnbYnAlVvDDSdqtOP8WZvCUewrWsYw7qYORRjMu+cpA2N +6SEOTbHyk3n+qdyBfQz4SGorvR3tv2/X0ljILYVlfYMnlaCa0X32ZC1nS+DSETV4 +7CwjOEL3wlQ75pjG+tvgN34ef232TTe8DQDGjac5dwLn8WHATadXiSBxY9fuWaNx +eaKshyHzulewOzbxFy6bu+6z87BC+53V8wYB0Dq1dBmBdF3V5Vuv+f3Ps9uIgxBw +iN1NGlNXNv3YnAeKY0CquCD/DL+u+18FKo1wa2wtJfaZHh8oaUNtvfBUhHd3pCgA +TcbEUyEq17GsbmqqH4XfWac0TBdl4RWQlPHButrRa89X69KKU2hMbAUzIrAVsIrm +e47vwzLE6LuUlsiXiPfGRTmq6m6zpBsW6xiUVPaL8XJr5dIuo8CJx6lxKENmwQug +8O8C9XqaXViO5iH11oF47rV1X/hJKAQc46671vH45UWdw2mzNkDvsftuBm7I2Dec +z9VvURAFyeCOEsprr3x3rDxRPbjmNhGd0KzmicJLAcqM5r2gfz3YeuerIL04y7E6 +u54YbOMMe801HdGxPEjbNk+WTESI6NHnzNIQKNbyGMNfgFJNZWqOi9qxS/2OavZu +z3aZ+JK+pUcEyy2Mb7fw8kgOjrXecdjPKABaDhulRu0M0uY/Xri/3X13We6HZmD5 +GivRPgOKJwUVWgnGDta8TNDSjQIJAhBfMNUjzLnAuG0QrOLRgBwZV5OfUBoP64If +KOvSX0lyNROyK3/7x1rWY7LEdDksNfdz+UbfOMmnormhXSDaJIFZjsZa1ri3gRUo +LIofz6GvbVbyEC6F7Zxus5XtWJjkfKxp5DRCpQPxqOQZ6HzSSyp78RK0+Qy993mJ +NpZRujhjhR4f2ZiAgUnAXQ== +=SC5C +-----END PGP MESSAGE----- diff --git a/tests/openpgp/samplemsgs/pqc-mlk768_np384.enc.asc b/tests/openpgp/samplemsgs/pqc-mlk768_np384.enc.asc new file mode 100644 index 000000000..27286aa37 --- /dev/null +++ b/tests/openpgp/samplemsgs/pqc-mlk768_np384.enc.asc @@ -0,0 +1,34 @@ +-----BEGIN PGP MESSAGE----- + +wcQuBiEGF1vPTqKsnCw1gJ0HGyVJydsYmIeIY9EtN0xaDHlPCv4lBC/7LuoEUy0k +dlrq0RkaoxXSJLI+oy1CXWrRLP3WHFrlm9/QKL5ai4dKM+xcvM9jbWdSo+PaR5fh +84wlxvijIVXhUcMasEiTwohJDE10S77J7tKmhOohne8oXHQ+NjH/kU/AOZEMbCmv +Vg32rsDhBMeSdiDASoLpr8C9RU+sV5a/eNS9XkZljhOtPKAE6XGDx6Mtu/PTx+O+ +ElSlj7Zri2Uo2eUFE+A5i6oXtwLJDK8YRSgQIZQ27eUmnHO2IgWcKAo+xY4rWQkL +MjdeY3hEwbE+dhU3u4BB28u7OmeCEvR4zGqNdchegoEHV4ZXYHUkv/mx4/xvuqbf +beMEVc3x688bYEOWcrzoO2OOnzZv/rgJ5EsI+6UqRVskRsDjYSXKijxTAHWKReAj +6RBSQefW6vU7lVvkU0LqSYTVBB1JRoNiF370dduQdyI26uWEMXOea6xLabM8CUOh +7mTH8P5gpsxusPIyPjvzzcb/zQHqak89f9FwQtbjwDwtqLUIBcYC5ZDsOE54X7am +jah9Dl5bQZIZQ5D7cVWbicW2e2O8tLKsWmu9kMfitbwGaywdhwESAVi7HQ5d8hFY +Z2awZb/hjedJIOXIzZHXhrXdVHnW2k/8HXAl7LbapcuJf2Jv4LJHxEZx+Xjnf6oa +zUPV7fR9lKenr85MPpJCUfcoLfWO2SI6SqRD8Tp00NOupnv/D7SfbtAh2bdypfxl +F2iF+Eqmre08nq+zbBp1nelLnUjwpEL8NdxlxTpfnSUToFISsbngyunmQ6XGWwP5 +F5eYjbBLiFPV9VvYhwPsCsqwuQJTTNDln0RV6566UC0EYX8BN2eCpMTxylswGLpx +SU9LWZRLYIXQXT7nz414LsQi/keBA3774h3v6JaKqtmYNWAB5XXjugNOEzJvaYQX +eCFJZ7OJHHeqsvTyrbuP3s4O4QfpCh+FTt3jnefBiHwW9OC8Px4HTIGFsVhrnpNZ +MQk8TxOnvm9gzR4INsMRrx7rLrV3exr5nNchCRnR5Cs2ZOaqnxLg5K6+cPSZ4xg4 +JQsxne8ZihgcnmQQoJ3w8lICqMaNv3EhrI8tSPuWpbnY1TC9O011wBqXOKwmmZfV +V2/5eaSHOm2CZNR1k5UGi5H5Od56YTncmU6JGAl51ylLyQSs4Afuyokbsn9+kbTa +9rhXG4BmyVz0iKd7Wt43s44PiCJj19zSAYeCKYDFG5ZDXkU5n/b88FEqi29xjBB+ +rdrJVvone6JQKYRsdcZEWIVYZawfbWurJ805710MHxeEB9Qp0ryA7datt0r4ZQVP +rRpgFnuYYGsNxbJ7B6Frf8/snz1J4aiylMq0oBFO03gB9dm/nfsdQTaooPdLDj2C +dVlhxnDD7x98P2u9/3bKxHly8L3FfWMKlwCoQQNyyv9PZVagdqVip/b6rhBFDs6l +so/ctDmG3zbNoYrMyYUv/OjopyN944DkITWqSci5Z03vTRXvFHQS4DXnwaLg72DI +N3KJ0PyVMhFpGeTYE0Pu34NRsFtG7gKviyMwlNMn4kUHcd4DWFjl6f3B3+cD9JFT +XAZCNitHImBTPtvVrZfE6+afojyDh462KPGrDZ2C92pP3W0Y5BD+mV3bSp3HeB9v +e6QBUp7e+P9Hv6L11hqSuqHSjgIJAhCfM0f6nCYDHNOzIpey7xuC2zaYbsrk38SG +zIxPpnXgKlcSw+Zp6+n2i4y0VCRxq+WJcSLDmKW4ATPI9/Wrp3gQuBOBystbDSaM +OqPp44Yz8cDu1g9c6Qp2wLeQSq9VrSkm2fvdMb1maSXwc+VzUZolEb4XTNswgxhK +9gy5Gr7Lupxs0zetFq/027E= +=8fPi +-----END PGP MESSAGE----- diff --git a/tools/gpgconf.c b/tools/gpgconf.c index 6dc1764b8..48b3ea336 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -1577,6 +1577,46 @@ show_versions_via_dirmngr (estream_t fp) } +static void +show_versioninfo_txt (estream_t fp) +{ + char *fname; + estream_t infp; + + es_fputs ("* Versioninfo\n", fp); + + fname = make_filename (gnupg_bindir (), "../../versioninfo.txt", NULL); + infp = es_fopen (fname, "r"); + if (!infp) + { + xfree (fname); + fname = make_filename (gnupg_bindir (), + "../../Gpg4win/versioninfo.txt", NULL); + infp = es_fopen (fname, "r"); + } + if (!infp) + es_fprintf (fp, "[versioninfo.txt not found]\n"); + else + { + char *line = NULL; + size_t line_len = 0; + + es_fprintf (fp, "# Content of '%s':\n", fname); + while (es_read_line (infp, &line, &line_len, NULL) > 0) + { + /* Prefix each line with a colon and a space to mark it as + * source code. */ + es_fputs (": ", fp); + es_fputs (line, fp); + } + es_fputs ("# End of versioninfo.txt\n", fp); + xfree (line); + } + es_fclose (fp); + xfree (fname); +} + + /* Show all kind of version information. */ static void show_versions (estream_t fp) @@ -1588,6 +1628,11 @@ show_versions (estream_t fp) show_version_gpgrt (fp); es_fputc ('\n', fp); show_versions_via_dirmngr (fp); + if (opt.verbose) + { + es_fputc ('\n', fp); + show_versioninfo_txt (fp); + } } |
