summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2022-01-20 15:42:30 +0100
committerJustus Winter <justus@sequoia-pgp.org>2024-03-13 10:59:08 +0100
commita3c0e1737d921de757c957bc9d831d85d446c4a1 (patch)
tree30410164ebc67ba42b1ede293ee7bb8e8d45145f
parentd138b9dbe2317a1de69d4ff32546258e9e43d342 (diff)
openpgp: Add key derivation function.
- Nettle, OpenSSL, Botan, and RustCrypto implement this natively, for CNG we use the RustCrypto implementation.
-rw-r--r--Cargo.lock2
-rw-r--r--openpgp/Cargo.toml7
-rw-r--r--openpgp/src/crypto/backend/botan.rs5
-rw-r--r--openpgp/src/crypto/backend/botan/kdf.rs31
-rw-r--r--openpgp/src/crypto/backend/cng.rs1
-rw-r--r--openpgp/src/crypto/backend/cng/kdf.rs20
-rw-r--r--openpgp/src/crypto/backend/fuzzing.rs2
-rw-r--r--openpgp/src/crypto/backend/fuzzing/kdf.rs18
-rw-r--r--openpgp/src/crypto/backend/interface.rs23
-rw-r--r--openpgp/src/crypto/backend/nettle.rs1
-rw-r--r--openpgp/src/crypto/backend/nettle/kdf.rs25
-rw-r--r--openpgp/src/crypto/backend/openssl.rs1
-rw-r--r--openpgp/src/crypto/backend/openssl/kdf.rs31
-rw-r--r--openpgp/src/crypto/backend/rust.rs1
-rw-r--r--openpgp/src/crypto/backend/rust/kdf.rs20
15 files changed, 184 insertions, 4 deletions
diff --git a/Cargo.lock b/Cargo.lock
index da83576e..63fb5e8d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2667,7 +2667,9 @@ dependencies = [
"ed25519",
"ed25519-dalek",
"flate2",
+ "generic-array",
"getrandom",
+ "hkdf",
"idea",
"idna 0.5.0",
"lalrpop",
diff --git a/openpgp/Cargo.toml b/openpgp/Cargo.toml
index dc5547de..f4ede36c 100644
--- a/openpgp/Cargo.toml
+++ b/openpgp/Cargo.toml
@@ -81,6 +81,8 @@ ecdsa = { version = "0.16", optional = true, features = ["hazmat", "arithmetic"]
# std::error::Error.
ed25519 = { version = "2", default-features = false, features = ["std"], optional = true }
ed25519-dalek = { version = "2", features = ["rand_core", "zeroize"], optional = true }
+generic-array = { version = "0.14.4", optional = true }
+hkdf = { version = "0.12", optional = true }
idea = { version = "0.5", optional = true, features = ["zeroize"] }
md-5 = { version = "0.10", features = ["oid"], optional = true }
num-bigint-dig = { version = "0.8", default-features = false, optional = true }
@@ -125,12 +127,13 @@ crypto-rust = [
"sha1collisiondetection/digest-trait", "sha1collisiondetection/oid",
"dep:twofish", "dep:typenum", "dep:x25519-dalek", "dep:p256",
"dep:rand", "rand?/getrandom", "dep:rand_core", "rand_core?/getrandom",
- "dep:ecdsa", "dep:aes-gcm", "dep:dsa"
+ "dep:ecdsa", "dep:aes-gcm", "dep:dsa", "dep:hkdf",
]
crypto-cng = [
"dep:cipher", "dep:eax", "dep:winapi", "dep:win-crypto-ng",
"dep:ed25519", "dep:ed25519-dalek",
- "dep:num-bigint-dig", "dep:aes-gcm", "dep:rand_core"
+ "dep:num-bigint-dig", "dep:aes-gcm", "dep:rand_core",
+ "dep:hkdf", "dep:sha2",
]
crypto-openssl = ["dep:openssl", "dep:openssl-sys"]
crypto-botan = ["dep:botan", "botan?/botan3"]
diff --git a/openpgp/src/crypto/backend/botan.rs b/openpgp/src/crypto/backend/botan.rs
index 3f1ab906..568802de 100644
--- a/openpgp/src/crypto/backend/botan.rs
+++ b/openpgp/src/crypto/backend/botan.rs
@@ -1,11 +1,14 @@
//! Implementation of Sequoia crypto API using the Botan cryptographic library.
-use crate::types::*;
+use crate::{
+ types::*,
+};
pub mod aead;
pub mod asymmetric;
pub mod ecdh;
pub mod hash;
+pub mod kdf;
pub mod symmetric;
pub struct Backend(());
diff --git a/openpgp/src/crypto/backend/botan/kdf.rs b/openpgp/src/crypto/backend/botan/kdf.rs
new file mode 100644
index 00000000..942165e2
--- /dev/null
+++ b/openpgp/src/crypto/backend/botan/kdf.rs
@@ -0,0 +1,31 @@
+use crate::{
+ Result,
+ crypto::{
+ SessionKey,
+ backend::interface::Kdf,
+ },
+};
+
+impl Kdf for super::Backend {
+ fn hkdf_sha256(ikm: &SessionKey, salt: Option<&[u8]>, info: &[u8],
+ okm: &mut SessionKey)
+ -> Result<()>
+ {
+ assert!(okm.len() <= 255 * 32);
+
+ const NO_SALT: [u8; 32] = [0; 32];
+ let salt = salt.unwrap_or(&NO_SALT);
+
+ // XXX: It'd be nice to write that directly to `okm`, but botan-rs
+ // does not have such an interface.
+ let okm_heap: SessionKey =
+ botan::kdf("HKDF(SHA-256)", okm.len(), &*ikm, salt, info)?
+ .into();
+
+ // XXX: Now copy the secret.
+ let l = okm.len().min(okm_heap.len());
+ okm[..l].copy_from_slice(&okm_heap[..l]);
+
+ Ok(())
+ }
+}
diff --git a/openpgp/src/crypto/backend/cng.rs b/openpgp/src/crypto/backend/cng.rs
index 9f43958f..557a34c3 100644
--- a/openpgp/src/crypto/backend/cng.rs
+++ b/openpgp/src/crypto/backend/cng.rs
@@ -8,6 +8,7 @@ pub mod aead;
pub mod asymmetric;
pub mod ecdh;
pub mod hash;
+pub mod kdf;
pub mod symmetric;
pub struct Backend(());
diff --git a/openpgp/src/crypto/backend/cng/kdf.rs b/openpgp/src/crypto/backend/cng/kdf.rs
new file mode 100644
index 00000000..ac98a496
--- /dev/null
+++ b/openpgp/src/crypto/backend/cng/kdf.rs
@@ -0,0 +1,20 @@
+use hkdf::Hkdf;
+use sha2::Sha256;
+
+use crate::{
+ Result,
+ crypto::{
+ SessionKey,
+ backend::interface::Kdf,
+ },
+};
+
+impl Kdf for super::Backend {
+ fn hkdf_sha256(ikm: &SessionKey, salt: Option<&[u8]>, info: &[u8],
+ okm: &mut SessionKey)
+ -> Result<()>
+ {
+ Ok(Hkdf::<Sha256>::new(salt, &ikm).expand(info, okm)
+ .map_err(|e| crate::Error::InvalidOperation(e.to_string()))?)
+ }
+}
diff --git a/openpgp/src/crypto/backend/fuzzing.rs b/openpgp/src/crypto/backend/fuzzing.rs
index e36ee34e..a10e5002 100644
--- a/openpgp/src/crypto/backend/fuzzing.rs
+++ b/openpgp/src/crypto/backend/fuzzing.rs
@@ -12,6 +12,8 @@ pub mod ecdh;
#[allow(unused_variables)]
pub mod hash;
#[allow(unused_variables)]
+pub mod kdf;
+#[allow(unused_variables)]
pub mod symmetric;
pub struct Backend(());
diff --git a/openpgp/src/crypto/backend/fuzzing/kdf.rs b/openpgp/src/crypto/backend/fuzzing/kdf.rs
new file mode 100644
index 00000000..f29f2b25
--- /dev/null
+++ b/openpgp/src/crypto/backend/fuzzing/kdf.rs
@@ -0,0 +1,18 @@
+use crate::{
+ Result,
+ crypto::{
+ SessionKey,
+ backend::interface::Kdf,
+ },
+};
+
+impl Kdf for super::Backend {
+ fn hkdf_sha256(ikm: &SessionKey, salt: Option<&[u8]>, info: &[u8],
+ okm: &mut SessionKey)
+ -> Result<()>
+ {
+ assert!(okm.len() <= 255 * 32);
+ okm.iter_mut().for_each(|p| *p = 4);
+ Ok(())
+ }
+}
diff --git a/openpgp/src/crypto/backend/interface.rs b/openpgp/src/crypto/backend/interface.rs
index 864b2714..05e56a8c 100644
--- a/openpgp/src/crypto/backend/interface.rs
+++ b/openpgp/src/crypto/backend/interface.rs
@@ -4,6 +4,7 @@ use crate::{
Error,
Result,
crypto::{
+ SessionKey,
mem::Protected,
mpi::{MPI, ProtectedMPI},
},
@@ -11,7 +12,7 @@ use crate::{
};
/// Abstracts over the cryptographic backends.
-pub trait Backend: Asymmetric {
+pub trait Backend: Asymmetric + Kdf {
/// Returns a short, human-readable description of the backend.
///
/// This starts with the name of the backend, possibly a version,
@@ -121,3 +122,23 @@ mod tests {
assert_ne!(secret.as_ref(), public);
}
}
+
+/// Key-Derivation-Functions.
+pub trait Kdf {
+ /// HKDF instantiated with SHA256.
+ ///
+ /// Used to derive message keys from session keys, and key
+ /// encapsulating keys from S2K mechanisms. In both cases, using
+ /// a KDF that includes algorithm information in the given `info`
+ /// provides key space separation between cipher algorithms and
+ /// modes.
+ ///
+ /// `salt`, if given, SHOULD be 32 bytes of salt matching the
+ /// digest size of the hash function. If it is not give, 32 zeros
+ /// are used instead.
+ ///
+ /// `okm` must not be larger than 255 * 32 (the size of the hash
+ /// digest).
+ fn hkdf_sha256(ikm: &SessionKey, salt: Option<&[u8]>, info: &[u8],
+ okm: &mut SessionKey) -> Result<()>;
+}
diff --git a/openpgp/src/crypto/backend/nettle.rs b/openpgp/src/crypto/backend/nettle.rs
index c0cb767f..04fef2eb 100644
--- a/openpgp/src/crypto/backend/nettle.rs
+++ b/openpgp/src/crypto/backend/nettle.rs
@@ -8,6 +8,7 @@ pub mod aead;
pub mod asymmetric;
pub mod ecdh;
pub mod hash;
+pub mod kdf;
pub mod symmetric;
pub struct Backend(());
diff --git a/openpgp/src/crypto/backend/nettle/kdf.rs b/openpgp/src/crypto/backend/nettle/kdf.rs
new file mode 100644
index 00000000..a016a3a3
--- /dev/null
+++ b/openpgp/src/crypto/backend/nettle/kdf.rs
@@ -0,0 +1,25 @@
+use nettle::{
+ kdf::hkdf,
+ hash::Sha256,
+};
+
+use crate::{
+ Result,
+ crypto::{
+ SessionKey,
+ backend::interface::Kdf,
+ },
+};
+
+impl Kdf for super::Backend {
+ fn hkdf_sha256(ikm: &SessionKey, salt: Option<&[u8]>, info: &[u8],
+ okm: &mut SessionKey)
+ -> Result<()>
+ {
+ assert!(okm.len() <= 255 * 32);
+ const NO_SALT: [u8; 32] = [0; 32];
+ let salt = salt.unwrap_or(&NO_SALT);
+ hkdf::<Sha256>(&ikm[..], salt, info, okm);
+ Ok(())
+ }
+}
diff --git a/openpgp/src/crypto/backend/openssl.rs b/openpgp/src/crypto/backend/openssl.rs
index aabf94cf..2bea302b 100644
--- a/openpgp/src/crypto/backend/openssl.rs
+++ b/openpgp/src/crypto/backend/openssl.rs
@@ -6,6 +6,7 @@ pub mod aead;
pub mod asymmetric;
pub mod ecdh;
pub mod hash;
+pub mod kdf;
pub mod symmetric;
pub struct Backend(());
diff --git a/openpgp/src/crypto/backend/openssl/kdf.rs b/openpgp/src/crypto/backend/openssl/kdf.rs
new file mode 100644
index 00000000..11c48667
--- /dev/null
+++ b/openpgp/src/crypto/backend/openssl/kdf.rs
@@ -0,0 +1,31 @@
+use openssl::{
+ md::Md,
+ pkey::Id,
+ pkey_ctx::PkeyCtx,
+};
+
+use crate::{
+ Result,
+ crypto::{
+ SessionKey,
+ backend::interface::Kdf,
+ },
+};
+
+impl Kdf for super::Backend {
+ fn hkdf_sha256(ikm: &SessionKey, salt: Option<&[u8]>, info: &[u8],
+ okm: &mut SessionKey)
+ -> Result<()>
+ {
+ let mut pkey = PkeyCtx::new_id(Id::HKDF)?;
+ pkey.derive_init()?;
+ pkey.set_hkdf_md(Md::sha256())?;
+ pkey.set_hkdf_key(&ikm)?;
+ if let Some(salt) = salt {
+ pkey.set_hkdf_salt(salt)?;
+ }
+ pkey.add_hkdf_info(info)?;
+ pkey.derive(Some(okm))?;
+ Ok(())
+ }
+}
diff --git a/openpgp/src/crypto/backend/rust.rs b/openpgp/src/crypto/backend/rust.rs
index e373dba3..27370e82 100644
--- a/openpgp/src/crypto/backend/rust.rs
+++ b/openpgp/src/crypto/backend/rust.rs
@@ -10,6 +10,7 @@ pub mod aead;
pub mod asymmetric;
pub mod ecdh;
pub mod hash;
+pub mod kdf;
pub mod symmetric;
pub struct Backend(());
diff --git a/openpgp/src/crypto/backend/rust/kdf.rs b/openpgp/src/crypto/backend/rust/kdf.rs
new file mode 100644
index 00000000..ac98a496
--- /dev/null
+++ b/openpgp/src/crypto/backend/rust/kdf.rs
@@ -0,0 +1,20 @@
+use hkdf::Hkdf;
+use sha2::Sha256;
+
+use crate::{
+ Result,
+ crypto::{
+ SessionKey,
+ backend::interface::Kdf,
+ },
+};
+
+impl Kdf for super::Backend {
+ fn hkdf_sha256(ikm: &SessionKey, salt: Option<&[u8]>, info: &[u8],
+ okm: &mut SessionKey)
+ -> Result<()>
+ {
+ Ok(Hkdf::<Sha256>::new(salt, &ikm).expand(info, okm)
+ .map_err(|e| crate::Error::InvalidOperation(e.to_string()))?)
+ }
+}