Skip to content

Commit

Permalink
Add security override for sha1 (#2244)
Browse files Browse the repository at this point in the history
* Add security override for sha1

* Fix time-machine tests wrong regexp matching

* Add missed parameters to docs
  • Loading branch information
desvxx authored Sep 17, 2024
1 parent abec6c7 commit 112fcf4
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 79 deletions.
13 changes: 13 additions & 0 deletions src/rnp/fficli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,19 @@ cli_rnp_t::init(const rnp_cfg &cfg)
return false;
}

if (cfg_.has(CFG_ALLOW_SHA1)) {
auto now = time(NULL);
uint64_t from = 0;
uint32_t level = 0;
rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, "SHA1", now, NULL, &from, &level);
rnp_add_security_rule(ffi,
RNP_FEATURE_HASH_ALG,
"SHA1",
RNP_SECURITY_OVERRIDE | RNP_SECURITY_VERIFY_KEY,
from,
RNP_SECURITY_DEFAULT);
}

// by default use stdin password provider
if (rnp_ffi_set_pass_provider(ffi, ffi_pass_callback_stdin, this)) {
goto done;
Expand Down
6 changes: 6 additions & 0 deletions src/rnp/rnp.1.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ You may want to use *--hash* option to override default hash algorithm settings.
+
Compression options also apply here. Since the secret key is usually stored encrypted, you will be asked for the password to decrypt it via _stdin_/_tty_ unless *--password* or *--pass-fd* is specified.

*--allow-weak-hash*:::
Allow usage of a weak hash algorithm.

*--allow-sha1-key-sigs*:::
Allow usage of a SHA-1 key signatures.

*--clearsign*::
Digitally sign text data, producing human-readable output with the signature attached. +
+
Expand Down
86 changes: 46 additions & 40 deletions src/rnp/rnp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,52 +56,53 @@ static const char *usage =
"Sign, verify, encrypt, decrypt, inspect OpenPGP data.\n"
"Usage: rnp --command [options] [files]\n"
"Commands:\n"
" -h, --help This help message.\n"
" -V, --version Print RNP version information.\n"
" -e, --encrypt Encrypt data using the public key(s).\n"
" -r, --recipient Specify recipient's key via uid/keyid/fingerprint.\n"
" -h, --help This help message.\n"
" -V, --version Print RNP version information.\n"
" -e, --encrypt Encrypt data using the public key(s).\n"
" -r, --recipient Specify recipient's key via uid/keyid/fingerprint.\n"
#if defined(ENABLE_CRYPTO_REFRESH)
" --v3-pkesk-only Only create v3 PKESK (otherwise v6 will be created if "
" --v3-pkesk-only Only create v3 PKESK (otherwise v6 will be created if "
"appropriate).\n"
#endif
" --cipher name Specify symmetric cipher, used for encryption.\n"
" --aead[=EAX, OCB] Use AEAD for encryption.\n"
" -z 0..9 Set the compression level.\n"
" --[zip,zlib,bzip] Use the corresponding compression algorithm.\n"
" --armor Apply ASCII armor to the encryption/signing output.\n"
" --no-wrap Do not wrap the output in a literal data packet.\n"
" -c, --symmetric Encrypt data using the password(s).\n"
" --passwords num Encrypt to the specified number of passwords.\n"
" -s, --sign Sign data. May be combined with encryption.\n"
" --detach Produce detached signature.\n"
" -u, --userid Specify signing key(s) via uid/keyid/fingerprint.\n"
" --hash Specify hash algorithm, used during signing.\n"
" --allow-weak-hash Allow usage of a weak hash algorithm.\n"
" --clearsign Cleartext-sign data.\n"
" -d, --decrypt Decrypt and output data, verifying signatures.\n"
" -v, --verify Verify signatures, without outputting data.\n"
" --source Specify source for the detached signature.\n"
" --dearmor Strip ASCII armor from the data, outputting binary.\n"
" --enarmor Add ASCII armor to the data.\n"
" --list-packets List OpenPGP packets from the input.\n"
" --json Use JSON output instead of human-readable.\n"
" --grips Dump key fingerprints and grips.\n"
" --mpi Dump MPI values from packets.\n"
" --raw Dump raw packet contents as well.\n"
" --cipher name Specify symmetric cipher, used for encryption.\n"
" --aead[=EAX, OCB] Use AEAD for encryption.\n"
" -z 0..9 Set the compression level.\n"
" --[zip,zlib,bzip] Use the corresponding compression algorithm.\n"
" --armor Apply ASCII armor to the encryption/signing output.\n"
" --no-wrap Do not wrap the output in a literal data packet.\n"
" -c, --symmetric Encrypt data using the password(s).\n"
" --passwords num Encrypt to the specified number of passwords.\n"
" -s, --sign Sign data. May be combined with encryption.\n"
" --detach Produce detached signature.\n"
" -u, --userid Specify signing key(s) via uid/keyid/fingerprint.\n"
" --hash Specify hash algorithm, used during signing.\n"
" --allow-weak-hash Allow usage of a weak hash algorithm.\n"
" --allow-sha1-key-sigs Allow usage of a SHA-1 key signatures.\n"
" --clearsign Cleartext-sign data.\n"
" -d, --decrypt Decrypt and output data, verifying signatures.\n"
" -v, --verify Verify signatures, without outputting data.\n"
" --source Specify source for the detached signature.\n"
" --dearmor Strip ASCII armor from the data, outputting binary.\n"
" --enarmor Add ASCII armor to the data.\n"
" --list-packets List OpenPGP packets from the input.\n"
" --json Use JSON output instead of human-readable.\n"
" --grips Dump key fingerprints and grips.\n"
" --mpi Dump MPI values from packets.\n"
" --raw Dump raw packet contents as well.\n"
"\n"
"Other options:\n"
" --homedir path Override home directory (default is ~/.rnp/).\n"
" -f, --keyfile Load key(s) only from the file specified.\n"
" --output [file, -] Write data to the specified file or stdout.\n"
" --overwrite Overwrite output file without a prompt.\n"
" --password Password used during operation.\n"
" --pass-fd num Read password(s) from the file descriptor.\n"
" --s2k-iterations Set the number of iterations for the S2K process.\n"
" --s2k-msec Calculate S2K iterations value based on a provided time in "
" --homedir path Override home directory (default is ~/.rnp/).\n"
" -f, --keyfile Load key(s) only from the file specified.\n"
" --output [file, -] Write data to the specified file or stdout.\n"
" --overwrite Overwrite output file without a prompt.\n"
" --password Password used during operation.\n"
" --pass-fd num Read password(s) from the file descriptor.\n"
" --s2k-iterations Set the number of iterations for the S2K process.\n"
" --s2k-msec Calculate S2K iterations value based on a provided time in "
"milliseconds.\n"
" --notty Do not output anything to the TTY.\n"
" --current-time Override system's time.\n"
" --set-filename Override file name, stored inside of OpenPGP message.\n"
" --notty Do not output anything to the TTY.\n"
" --current-time Override system's time.\n"
" --set-filename Override file name, stored inside of OpenPGP message.\n"
"\n"
"See man page for a detailed listing and explanation.\n"
"\n";
Expand Down Expand Up @@ -137,6 +138,7 @@ enum optdefs {
OPT_DETACHED,
OPT_HASH_ALG,
OPT_ALLOW_WEAK_HASH,
OPT_ALLOW_SHA1,
OPT_OUTPUT,
OPT_RESULTS,
OPT_COREDUMPS,
Expand Down Expand Up @@ -238,6 +240,7 @@ static struct option options[] = {
{"s2k-iterations", required_argument, NULL, OPT_S2K_ITER},
{"s2k-msec", required_argument, NULL, OPT_S2K_MSEC},
{"allow-weak-hash", no_argument, NULL, OPT_ALLOW_WEAK_HASH},
{"allow-sha1-key-sigs", no_argument, NULL, OPT_ALLOW_SHA1},

{NULL, 0, NULL, 0},
};
Expand Down Expand Up @@ -430,6 +433,9 @@ setoption(rnp_cfg &cfg, int val, const char *arg)
case OPT_ALLOW_WEAK_HASH:
cfg.set_bool(CFG_WEAK_HASH, true);
return true;
case OPT_ALLOW_SHA1:
cfg.set_bool(CFG_ALLOW_SHA1, true);
return true;
case OPT_PASSWDFD:
cfg.set_str(CFG_PASSFD, arg);
return true;
Expand Down
1 change: 1 addition & 0 deletions src/rnp/rnpcfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
#define CFG_CIPHER "cipher" /* symmetric encryption algorithm as string */
#define CFG_HASH "hash" /* hash algorithm used, string like 'SHA1'*/
#define CFG_WEAK_HASH "weak-hash" /* allow weak algorithms */
#define CFG_ALLOW_SHA1 "allow-sha1" /* allow SHA-1 key signatures */
#define CFG_S2K_ITER "s2k-iter" /* number of S2K hash iterations to perform */
#define CFG_S2K_MSEC "s2k-msec" /* number of milliseconds S2K should target */
#define CFG_ENCRYPT_PK "encrypt_pk" /* public key should be used during encryption */
Expand Down
6 changes: 6 additions & 0 deletions src/rnpkeys/rnpkeys.1.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,12 @@ would take _NUMBER_ of milliseconds on the current system. +
For example, setting it to _2000_ would mean that each secret key
decryption operation would take around 2 seconds (on the current machine).

*--allow-weak-hash*:::
Allow usage of a weak hash algorithm.

*--allow-sha1-key-sigs*:::
Allow usage of a SHA-1 key signatures.


=== KEY/SIGNATURE IMPORT

Expand Down
81 changes: 43 additions & 38 deletions src/rnpkeys/rnpkeys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,46 +45,47 @@ const char *usage =
"Manipulate OpenPGP keys and keyrings.\n"
"Usage: rnpkeys --command [options] [files]\n"
"Commands:\n"
" -h, --help This help message.\n"
" -V, --version Print RNP version information.\n"
" -g, --generate-key Generate a new keypair (default is RSA).\n"
" --userid Specify key's userid.\n"
" --expert Select key type, size, and additional parameters.\n"
" --numbits Override default key size (2048).\n"
" --expiration Set key and subkey expiration time.\n"
" --cipher Set cipher used to encrypt a secret key.\n"
" --hash Set hash which is used for key derivation.\n"
" --allow-weak-hash Allow usage of a weak hash algorithm.\n"
" -l, --list-keys List keys in the keyrings.\n"
" --secret List secret keys instead of public ones.\n"
" --with-sigs List signatures as well.\n"
" --import Import keys or signatures.\n"
" --import-keys Import keys.\n"
" --import-sigs Import signatures.\n"
" --permissive Skip erroring keys/sigs instead of failing.\n"
" --export-key Export a key.\n"
" --secret Export a secret key instead of a public.\n"
" --export-rev Export a key's revocation.\n"
" --rev-type Set revocation type.\n"
" --rev-reason Human-readable reason for revocation.\n"
" --revoke-key Revoke a key specified.\n"
" --remove-key Remove a key specified.\n"
" --edit-key Edit key properties.\n"
" --add-subkey Add new subkey.\n"
" --check-cv25519-bits Check whether Cv25519 subkey bits are correct.\n"
" --fix-cv25519-bits Fix Cv25519 subkey bits.\n"
" --set-expire Set key expiration time.\n"
" -h, --help This help message.\n"
" -V, --version Print RNP version information.\n"
" -g, --generate-key Generate a new keypair (default is RSA).\n"
" --userid Specify key's userid.\n"
" --expert Select key type, size, and additional parameters.\n"
" --numbits Override default key size (2048).\n"
" --expiration Set key and subkey expiration time.\n"
" --cipher Set cipher used to encrypt a secret key.\n"
" --hash Set hash which is used for key derivation.\n"
" --allow-weak-hash Allow usage of a weak hash algorithm.\n"
" --allow-sha1-key-sigs Allow usage of a SHA-1 key signatures.\n"
" -l, --list-keys List keys in the keyrings.\n"
" --secret List secret keys instead of public ones.\n"
" --with-sigs List signatures as well.\n"
" --import Import keys or signatures.\n"
" --import-keys Import keys.\n"
" --import-sigs Import signatures.\n"
" --permissive Skip erroring keys/sigs instead of failing.\n"
" --export-key Export a key.\n"
" --secret Export a secret key instead of a public.\n"
" --export-rev Export a key's revocation.\n"
" --rev-type Set revocation type.\n"
" --rev-reason Human-readable reason for revocation.\n"
" --revoke-key Revoke a key specified.\n"
" --remove-key Remove a key specified.\n"
" --edit-key Edit key properties.\n"
" --add-subkey Add new subkey.\n"
" --check-cv25519-bits Check whether Cv25519 subkey bits are correct.\n"
" --fix-cv25519-bits Fix Cv25519 subkey bits.\n"
" --set-expire Set key expiration time.\n"
"\n"
"Other options:\n"
" --homedir Override home directory (default is ~/.rnp/).\n"
" --password Password, which should be used during operation.\n"
" --pass-fd Read password(s) from the file descriptor.\n"
" --force Force operation (like secret key removal).\n"
" --keyfile Load key(s) only from the file specified.\n"
" --output [file, -] Write data to the specified file or stdout.\n"
" --overwrite Overwrite output file without a prompt.\n"
" --notty Do not write anything to the TTY.\n"
" --current-time Override system's time.\n"
" --homedir Override home directory (default is ~/.rnp/).\n"
" --password Password, which should be used during operation.\n"
" --pass-fd Read password(s) from the file descriptor.\n"
" --force Force operation (like secret key removal).\n"
" --keyfile Load key(s) only from the file specified.\n"
" --output [file, -] Write data to the specified file or stdout.\n"
" --overwrite Overwrite output file without a prompt.\n"
" --notty Do not write anything to the TTY.\n"
" --current-time Override system's time.\n"
"\n"
"See man page for a detailed listing and explanation.\n"
"\n";
Expand Down Expand Up @@ -142,6 +143,7 @@ struct option options[] = {
{"set-expire", required_argument, NULL, OPT_SET_EXPIRE},
{"current-time", required_argument, NULL, OPT_CURTIME},
{"allow-weak-hash", no_argument, NULL, OPT_ALLOW_WEAK_HASH},
{"allow-sha1-key-sigs", no_argument, NULL, OPT_ALLOW_SHA1},
{"keyfile", required_argument, NULL, OPT_KEYFILE},
{NULL, 0, NULL, 0},
};
Expand Down Expand Up @@ -517,6 +519,9 @@ setoption(rnp_cfg &cfg, optdefs_t *cmd, int val, const char *arg)
case OPT_ALLOW_WEAK_HASH:
cfg.set_bool(CFG_WEAK_HASH, true);
return true;
case OPT_ALLOW_SHA1:
cfg.set_bool(CFG_ALLOW_SHA1, true);
return true;
case OPT_HASH_ALG:
return cli_rnp_set_hash(cfg, arg);
case OPT_S2K_ITER: {
Expand Down
1 change: 1 addition & 0 deletions src/rnpkeys/rnpkeys.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ typedef enum {
OPT_HOMEDIR,
OPT_NUMBITS,
OPT_ALLOW_WEAK_HASH,
OPT_ALLOW_SHA1,
OPT_HASH_ALG,
OPT_COREDUMPS,
OPT_PASSWDFD,
Expand Down
31 changes: 30 additions & 1 deletion src/tests/cli_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def escape_regex(str):
KEYRING_DIR_1 = 'keyrings/1'
KEYRING_DIR_2 = 'keyrings/2'
KEYRING_DIR_3 = 'keyrings/3'
PUBRING_7 = 'keyrings/7/pubring.gpg'
SECRING_G10 = 'test_stream_key_load/g10'
KEY_ALICE_PUB = 'test_key_validity/alice-pub.asc'
KEY_ALICE_SUB_PUB = 'test_key_validity/alice-sub-pub.pgp'
Expand Down Expand Up @@ -4066,6 +4067,34 @@ def test_allow_weak_hash(self):
clear_workfiles()
shutil.rmtree(RNP2, ignore_errors=True)

def test_allow_sha1_key_sigs(self):
src, sig = reg_workfiles('cleartext', '.txt', '.sig')
random_text(src, 120)

ret, out, _ = run_proc(RNPK, ['--keyfile', data_path(PUBRING_7), '--notty', '--list-keys'])
self.assertEqual(ret, 0)
self.assertRegex(out, r'(?s)^.*\[INVALID\].*$')
ret, out, _ = run_proc(RNPK, ['--keyfile', data_path(PUBRING_7), '--notty', '--list-keys', '--allow-sha1-key-sigs'])
self.assertEqual(ret, 0)
self.assertRegex(out, r'(?s)^.*pub.*2024-06-03.*sub.*2024-06-03.*$')
self.assertNotRegex(out, r'(?s)^.*\[INVALID\].*$')

ret, _, err = run_proc(RNP, ['--keyfile', data_path(PUBRING_7), '--notty', '--password=', '-e', src, '--output', sig])
self.assertNotEqual(ret, 0)
self.assertRegex(err, r'(?s)^.*Failed to add recipient.*')
ret, _, err = run_proc(RNP, ['--keyfile', data_path(PUBRING_7), '--notty', '--password=', '-e', src, '--output', sig, '--allow-sha1-key-sigs'])
self.assertEqual(ret, 0)
remove_files(sig)

ret, _, err = run_proc(RNP, ['--keyfile', data_path(PUBRING_7), '--notty', '--password=', '-e', src, '--output', sig, '--hash', 'SHA1'])
self.assertNotEqual(ret, 0)
self.assertRegex(err, r'(?s)^.*Hash algorithm \'SHA1\' is cryptographically weak!.*Weak hash algorithm detected. Pass --allow-weak-hash option if you really want to use it\..*')
ret, _, err = run_proc(RNP, ['--keyfile', data_path(PUBRING_7), '--notty', '--password=', '-e', src, '--output', sig, '--hash', 'SHA1', '--allow-sha1-key-sigs'])
self.assertEqual(ret, 0)
remove_files(sig)

clear_workfiles()

def test_armored_detection_on_cleartext(self):
ret, out, err = run_proc(RNP, ['--keyfile', data_path(SECRING_1), '--password', PASSWORD, '--clearsign'], 'Hello\n')
self.assertEqual(ret, 0)
Expand Down Expand Up @@ -4279,7 +4308,7 @@ def test_sym_encrypted__rnp_aead_botan_crash(self):
rnp_decrypt_file(data_path('test_messages/message.aead-windows-issue'), dst)
remove_files(dst)
rnp_decrypt_file(data_path('test_messages/message.aead-windows-issue2'), dst)
remove_files(dst)
remove_files(dst)

def test_aead_chunk_edge_cases(self):
if not RNP_AEAD:
Expand Down
Binary file added src/tests/data/keyrings/7/pubring.gpg
Binary file not shown.
Binary file added src/tests/data/keyrings/7/secring.gpg
Binary file not shown.

0 comments on commit 112fcf4

Please sign in to comment.