Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add security override for sha1 #2244

Merged
merged 4 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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},
desvxx marked this conversation as resolved.
Show resolved Hide resolved
{"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.*$')
desvxx marked this conversation as resolved.
Show resolved Hide resolved
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.