From 2170670fbbec94f923cd5c861f7225880e849d86 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 22 Jun 2023 13:07:40 +0200 Subject: openpgp: Implement SHA3. - CNG doesn't currently implement this on commonly deployed installations. As this is not a high priority algorithm, we don't implement it using RustCrypto when the CNG backend is selected, but simply signal no support. --- Cargo.lock | 20 ++++++++++++++++++++ openpgp/Cargo.toml | 2 ++ openpgp/NEWS | 2 ++ openpgp/src/crypto/backend/botan/hash.rs | 4 ++++ openpgp/src/crypto/backend/cng/hash.rs | 11 +++++++++++ openpgp/src/crypto/backend/nettle/hash.rs | 8 +++++++- openpgp/src/crypto/backend/openssl/hash.rs | 2 ++ openpgp/src/crypto/backend/rust/hash.rs | 6 ++++++ openpgp/src/crypto/hash.rs | 14 ++++++++++++++ openpgp/src/policy.rs | 10 ++++++++-- openpgp/src/types/mod.rs | 20 +++++++++++++++++++- 11 files changed, 95 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9cb2069..da83576e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1589,6 +1589,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "lalrpop" version = "0.20.0" @@ -2684,6 +2693,7 @@ dependencies = [ "rust-argon2", "sha1collisiondetection", "sha2", + "sha3", "thiserror", "twofish", "typenum", @@ -2758,6 +2768,16 @@ dependencies = [ "digest", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "shlex" version = "1.3.0" diff --git a/openpgp/Cargo.toml b/openpgp/Cargo.toml index a74c1311..dc5547de 100644 --- a/openpgp/Cargo.toml +++ b/openpgp/Cargo.toml @@ -90,6 +90,7 @@ rand_core = { version = "0.6", optional = true } ripemd = { version = "0.1", features = ["oid"], optional = true } rsa = { version = "0.9.0", optional = true } sha2 = { version = "0.10", features = ["oid"], optional = true } +sha3 = { version = "0.10", features = ["oid"], optional = true } twofish = { version = "0.7", optional = true, features = ["zeroize"] } typenum = { version = "1.12.0", optional = true } x25519-dalek = { version = "2", optional = true, default-features = false, features = ["static_secrets", "zeroize"] } @@ -120,6 +121,7 @@ crypto-rust = [ "dep:cast5", "dep:cfb-mode", "dep:cipher", "dep:des", "dep:digest", "dep:eax", "dep:ecb", "dep:ed25519", "dep:ed25519-dalek", "dep:idea", "dep:md-5", "dep:num-bigint-dig", "dep:ripemd", "dep:rsa", "dep:sha2", + "dep:sha3", "sha1collisiondetection/digest-trait", "sha1collisiondetection/oid", "dep:twofish", "dep:typenum", "dep:x25519-dalek", "dep:p256", "dep:rand", "rand?/getrandom", "dep:rand_core", "rand_core?/getrandom", diff --git a/openpgp/NEWS b/openpgp/NEWS index 06ea3106..cfef24c0 100644 --- a/openpgp/NEWS +++ b/openpgp/NEWS @@ -12,6 +12,8 @@ We have also used the opportunity to clean up our API. Most programs should only require minor adjustments. ** New functionality + - HashAlgorithm::SHA3_256 + - HashAlgorithm::SHA3_512 * Changes in 1.19.0 ** Notable fixes - Key4::import_secret_cv25519 will now clamp some bits of the given diff --git a/openpgp/src/crypto/backend/botan/hash.rs b/openpgp/src/crypto/backend/botan/hash.rs index 0e02b5dd..7f5934e3 100644 --- a/openpgp/src/crypto/backend/botan/hash.rs +++ b/openpgp/src/crypto/backend/botan/hash.rs @@ -48,6 +48,8 @@ impl HashAlgorithm { HashAlgorithm::SHA256 => true, HashAlgorithm::SHA384 => true, HashAlgorithm::SHA512 => true, + HashAlgorithm::SHA3_256 => true, + HashAlgorithm::SHA3_512 => true, HashAlgorithm::RipeMD => true, HashAlgorithm::MD5 => true, HashAlgorithm::Private(_) => false, @@ -82,6 +84,8 @@ impl HashAlgorithm { HashAlgorithm::SHA256 => Ok("SHA-256"), HashAlgorithm::SHA384 => Ok("SHA-384"), HashAlgorithm::SHA512 => Ok("SHA-512"), + HashAlgorithm::SHA3_256 => Ok("SHA-3(256)"), + HashAlgorithm::SHA3_512 => Ok("SHA-3(512)"), HashAlgorithm::MD5 => Ok("MD5"), HashAlgorithm::RipeMD => Ok("RIPEMD-160"), HashAlgorithm::Private(_) | HashAlgorithm::Unknown(_) => diff --git a/openpgp/src/crypto/backend/cng/hash.rs b/openpgp/src/crypto/backend/cng/hash.rs index 301ac0c6..e71789d1 100644 --- a/openpgp/src/crypto/backend/cng/hash.rs +++ b/openpgp/src/crypto/backend/cng/hash.rs @@ -84,6 +84,12 @@ impl TryFrom for cng::HashAlgorithmId { HashAlgorithm::SHA512 => cng::HashAlgorithmId::Sha512, HashAlgorithm::MD5 => cng::HashAlgorithmId::Md5, + // SHA3 support is on the horizon, see + // https://blogs.windows.com/windows-insider/2023/03/23/announcing-windows-11-insider-preview-build-25324/ + HashAlgorithm::SHA3_256 | + HashAlgorithm::SHA3_512 => + return Err(Error::UnsupportedHashAlgorithm(value)), + HashAlgorithm::SHA224 | HashAlgorithm::RipeMD | HashAlgorithm::Private(_) | @@ -119,6 +125,11 @@ impl HashAlgorithm { HashAlgorithm::SHA512 => true, HashAlgorithm::MD5 => true, + // SHA3 support is on the horizon, see + // https://blogs.windows.com/windows-insider/2023/03/23/announcing-windows-11-insider-preview-build-25324/ + HashAlgorithm::SHA3_256 | + HashAlgorithm::SHA3_512 => false, + HashAlgorithm::SHA224 | HashAlgorithm::RipeMD | HashAlgorithm::Private(_) | diff --git a/openpgp/src/crypto/backend/nettle/hash.rs b/openpgp/src/crypto/backend/nettle/hash.rs index 4bf1535e..3e9d12a6 100644 --- a/openpgp/src/crypto/backend/nettle/hash.rs +++ b/openpgp/src/crypto/backend/nettle/hash.rs @@ -29,6 +29,8 @@ impl_digest_for!(nettle::hash::Sha224, SHA224); impl_digest_for!(nettle::hash::Sha256, SHA256); impl_digest_for!(nettle::hash::Sha384, SHA384); impl_digest_for!(nettle::hash::Sha512, SHA512); +impl_digest_for!(nettle::hash::Sha3_256, SHA3_256); +impl_digest_for!(nettle::hash::Sha3_512, SHA3_512); impl_digest_for!(nettle::hash::insecure_do_not_use::Sha1, SHA1); impl_digest_for!(nettle::hash::insecure_do_not_use::Md5, MD5); impl_digest_for!(nettle::hash::insecure_do_not_use::Ripemd160, RipeMD); @@ -42,6 +44,8 @@ impl HashAlgorithm { HashAlgorithm::SHA256 => true, HashAlgorithm::SHA384 => true, HashAlgorithm::SHA512 => true, + HashAlgorithm::SHA3_256 => true, + HashAlgorithm::SHA3_512 => true, HashAlgorithm::RipeMD => true, HashAlgorithm::MD5 => true, HashAlgorithm::Private(_) => false, @@ -59,7 +63,7 @@ impl HashAlgorithm { /// /// [`HashAlgorithm::is_supported`]: HashAlgorithm::is_supported() pub(crate) fn new_hasher(self) -> Result> { - use nettle::hash::{Sha224, Sha256, Sha384, Sha512}; + use nettle::hash::{Sha224, Sha256, Sha384, Sha512, Sha3_256, Sha3_512}; use nettle::hash::insecure_do_not_use::{ Sha1, Md5, @@ -72,6 +76,8 @@ impl HashAlgorithm { HashAlgorithm::SHA256 => Ok(Box::new(Sha256::default())), HashAlgorithm::SHA384 => Ok(Box::new(Sha384::default())), HashAlgorithm::SHA512 => Ok(Box::new(Sha512::default())), + HashAlgorithm::SHA3_256 => Ok(Box::new(Sha3_256::default())), + HashAlgorithm::SHA3_512 => Ok(Box::new(Sha3_512::default())), HashAlgorithm::MD5 => Ok(Box::new(Md5::default())), HashAlgorithm::RipeMD => Ok(Box::new(Ripemd160::default())), HashAlgorithm::Private(_) | HashAlgorithm::Unknown(_) => diff --git a/openpgp/src/crypto/backend/openssl/hash.rs b/openpgp/src/crypto/backend/openssl/hash.rs index b4f6d816..e8db9373 100644 --- a/openpgp/src/crypto/backend/openssl/hash.rs +++ b/openpgp/src/crypto/backend/openssl/hash.rs @@ -73,6 +73,8 @@ fn get_md(algo: HashAlgorithm) -> Option { SHA384 => Nid::SHA384, SHA512 => Nid::SHA512, SHA224 => Nid::SHA224, + SHA3_256 => Nid::SHA3_256, + SHA3_512 => Nid::SHA3_512, HashAlgorithm::Private(_) | HashAlgorithm::Unknown(_) => return None, }; diff --git a/openpgp/src/crypto/backend/rust/hash.rs b/openpgp/src/crypto/backend/rust/hash.rs index 069dfc79..ed3a3704 100644 --- a/openpgp/src/crypto/backend/rust/hash.rs +++ b/openpgp/src/crypto/backend/rust/hash.rs @@ -37,6 +37,8 @@ impl_digest_for!(sha2::Sha224, SHA224); impl_digest_for!(sha2::Sha256, SHA256); impl_digest_for!(sha2::Sha384, SHA384); impl_digest_for!(sha2::Sha512, SHA512); +impl_digest_for!(sha3::Sha3_256, SHA3_256); +impl_digest_for!(sha3::Sha3_512, SHA3_512); impl HashAlgorithm { /// Whether Sequoia supports this algorithm. @@ -47,6 +49,8 @@ impl HashAlgorithm { HashAlgorithm::SHA256 => true, HashAlgorithm::SHA384 => true, HashAlgorithm::SHA512 => true, + HashAlgorithm::SHA3_256 => true, + HashAlgorithm::SHA3_512 => true, HashAlgorithm::RipeMD => true, HashAlgorithm::MD5 => true, HashAlgorithm::Private(_) => false, @@ -73,6 +77,8 @@ impl HashAlgorithm { HashAlgorithm::SHA256 => Ok(Box::new(sha2::Sha256::new())), HashAlgorithm::SHA384 => Ok(Box::new(sha2::Sha384::new())), HashAlgorithm::SHA512 => Ok(Box::new(sha2::Sha512::new())), + HashAlgorithm::SHA3_256 => Ok(Box::new(sha3::Sha3_256::new())), + HashAlgorithm::SHA3_512 => Ok(Box::new(sha3::Sha3_512::new())), HashAlgorithm::RipeMD => Ok(Box::new(ripemd::Ripemd160::new())), HashAlgorithm::MD5 => Ok(Box::new(md5::Md5::new())), HashAlgorithm::Private(_) | HashAlgorithm::Unknown(_) => diff --git a/openpgp/src/crypto/hash.rs b/openpgp/src/crypto/hash.rs index 79a31f8a..7bd9a9e1 100644 --- a/openpgp/src/crypto/hash.rs +++ b/openpgp/src/crypto/hash.rs @@ -96,6 +96,18 @@ const ASN1_OID_SHA512: &[u8] = &[ 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40, ]; +/// ASN.1 OID for SHA3-256 +const ASN1_OID_SHA3_256: &[u8] = &[ + 0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, + 0x04, 0x02, 0x08, 0x05, 0x00, 0x04, 0x20 +]; + +/// ASN.1 OID for SHA3-512. +const ASN1_OID_SHA3_512: &[u8] = &[ + 0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, + 0x04, 0x02, 0x0a, 0x05, 0x00, 0x04, 0x40 +]; + lazy_static::lazy_static! { /// List of hashes that the signer may produce. /// This list is ordered by the preference so that the most preferred @@ -234,6 +246,8 @@ impl HashAlgorithm { HashAlgorithm::SHA256 => Ok(ASN1_OID_SHA256), HashAlgorithm::SHA384 => Ok(ASN1_OID_SHA384), HashAlgorithm::SHA512 => Ok(ASN1_OID_SHA512), + HashAlgorithm::SHA3_256 => Ok(ASN1_OID_SHA3_256), + HashAlgorithm::SHA3_512 => Ok(ASN1_OID_SHA3_512), HashAlgorithm::MD5 => Ok(ASN1_OID_MD5), HashAlgorithm::RipeMD => Ok(ASN1_OID_RIPEMD160), HashAlgorithm::Private(_) | HashAlgorithm::Unknown(_) => diff --git a/openpgp/src/policy.rs b/openpgp/src/policy.rs index 9ea4fcb2..0ed9b919 100644 --- a/openpgp/src/policy.rs +++ b/openpgp/src/policy.rs @@ -626,7 +626,7 @@ impl<'a> From<&'a StandardPolicy<'a>> for Option<&'a dyn Policy> { // Signatures that require a hash with collision Resistance and second // Pre-image Resistance. See the documentation for HashAlgoSecurity // for more details. -a_cutoff_list!(CollisionResistantHashCutoffList, HashAlgorithm, 12, +a_cutoff_list!(CollisionResistantHashCutoffList, HashAlgorithm, 15, [ REJECT, // 0. Not assigned. Some(Timestamp::Y1997M2), // 1. MD5 @@ -640,11 +640,14 @@ a_cutoff_list!(CollisionResistantHashCutoffList, HashAlgorithm, 12, ACCEPT, // 9. SHA384 ACCEPT, // 10. SHA512 ACCEPT, // 11. SHA224 + ACCEPT, // 12. SHA3-256 + REJECT, // 13. Reserved. + ACCEPT, // 14. SHA3-512 ]); // Signatures that *only* require a hash with Second Pre-image // Resistance. See the documentation for HashAlgoSecurity for more // details. -a_cutoff_list!(SecondPreImageResistantHashCutoffList, HashAlgorithm, 12, +a_cutoff_list!(SecondPreImageResistantHashCutoffList, HashAlgorithm, 15, [ REJECT, // 0. Not assigned. Some(Timestamp::Y2004M2), // 1. MD5 @@ -658,6 +661,9 @@ a_cutoff_list!(SecondPreImageResistantHashCutoffList, HashAlgorithm, 12, ACCEPT, // 9. SHA384 ACCEPT, // 10. SHA512 ACCEPT, // 11. SHA224 + ACCEPT, // 12. SHA3-256 + REJECT, // 13. Reserved. + ACCEPT, // 14. SHA3-512 ]); a_cutoff_list!(SubpacketTagCutoffList, SubpacketTag, 38, diff --git a/openpgp/src/types/mod.rs b/openpgp/src/types/mod.rs index 50813cc7..6caea951 100644 --- a/openpgp/src/types/mod.rs +++ b/openpgp/src/types/mod.rs @@ -1339,6 +1339,10 @@ pub enum HashAlgorithm { SHA512, /// 224-bit version of SHA2 SHA224, + /// 256-bit version of SHA3 + SHA3_256, + /// 512-bit version of SHA3 + SHA3_512, /// Private hash algorithm identifier. Private(u8), /// Unknown hash algorithm identifier. @@ -1346,7 +1350,7 @@ pub enum HashAlgorithm { } assert_send_and_sync!(HashAlgorithm); -const HASH_ALGORITHM_VARIANTS: [HashAlgorithm; 7] = [ +const HASH_ALGORITHM_VARIANTS: [HashAlgorithm; 9] = [ HashAlgorithm::MD5, HashAlgorithm::SHA1, HashAlgorithm::RipeMD, @@ -1354,6 +1358,8 @@ const HASH_ALGORITHM_VARIANTS: [HashAlgorithm; 7] = [ HashAlgorithm::SHA384, HashAlgorithm::SHA512, HashAlgorithm::SHA224, + HashAlgorithm::SHA3_256, + HashAlgorithm::SHA3_512, ]; impl Default for HashAlgorithm { @@ -1374,6 +1380,8 @@ impl From for HashAlgorithm { 9 => HashAlgorithm::SHA384, 10 => HashAlgorithm::SHA512, 11 => HashAlgorithm::SHA224, + 12 => HashAlgorithm::SHA3_256, + 14 => HashAlgorithm::SHA3_512, 100..=110 => HashAlgorithm::Private(u), u => HashAlgorithm::Unknown(u), } @@ -1390,6 +1398,8 @@ impl From for u8 { HashAlgorithm::SHA384 => 9, HashAlgorithm::SHA512 => 10, HashAlgorithm::SHA224 => 11, + HashAlgorithm::SHA3_256 => 12, + HashAlgorithm::SHA3_512 => 14, HashAlgorithm::Private(u) => u, HashAlgorithm::Unknown(u) => u, } @@ -1414,6 +1424,10 @@ impl FromStr for HashAlgorithm { Ok(HashAlgorithm::SHA512) } else if s.eq_ignore_ascii_case("SHA224") { Ok(HashAlgorithm::SHA224) + } else if s.eq_ignore_ascii_case("SHA3-256") { + Ok(HashAlgorithm::SHA3_256) + } else if s.eq_ignore_ascii_case("SHA3-512") { + Ok(HashAlgorithm::SHA3_512) } else { Err(()) } @@ -1430,6 +1444,8 @@ impl fmt::Display for HashAlgorithm { HashAlgorithm::SHA384 => f.write_str("SHA384"), HashAlgorithm::SHA512 => f.write_str("SHA512"), HashAlgorithm::SHA224 => f.write_str("SHA224"), + HashAlgorithm::SHA3_256 => f.write_str("SHA3-256"), + HashAlgorithm::SHA3_512 => f.write_str("SHA3-512"), HashAlgorithm::Private(u) => f.write_fmt(format_args!("Private/Experimental hash algorithm {}", u)), HashAlgorithm::Unknown(u) => @@ -1466,6 +1482,8 @@ impl HashAlgorithm { HashAlgorithm::SHA384 => Ok("SHA384"), HashAlgorithm::SHA512 => Ok("SHA512"), HashAlgorithm::SHA224 => Ok("SHA224"), + HashAlgorithm::SHA3_256 => Ok("SHA3-256"), + HashAlgorithm::SHA3_512 => Ok("SHA3-512"), HashAlgorithm::Private(_) => Err(Error::UnsupportedHashAlgorithm(*self).into()), HashAlgorithm::Unknown(_) => -- cgit v1.2.3