summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2022-02-21 16:00:45 +0100
committerJustus Winter <justus@sequoia-pgp.org>2022-03-25 12:58:42 +0100
commit122a0b4fc0484046d8015444baa171d5431b9a8c (patch)
treedbe636dd76c2f6948889439b21eabf075508c4e1
parente47e91fb5de347b86d2a84c881a41fab482eea60 (diff)
openpgp: Implement S2K::Argon2 using RustCrypto's argon2 crate.justus/alternative-argon2
- See https://openpgp-wg.gitlab.io/rfc4880bis/#name-argon2
-rw-r--r--Cargo.lock68
-rw-r--r--openpgp/Cargo.toml1
-rw-r--r--openpgp/src/crypto/s2k.rs46
-rw-r--r--openpgp/src/parse.rs12
-rw-r--r--openpgp/src/serialize.rs6
-rw-r--r--sq/src/commands/dump.rs11
6 files changed, 144 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 87b713f4..afc2a598 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -91,6 +91,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
[[package]]
+name = "argon2"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25df3c03f1040d0069fcd3907e24e36d59f9b6fa07ba49be0eb25a794f036ba7"
+dependencies = [
+ "base64ct",
+ "blake2",
+ "password-hash",
+]
+
+[[package]]
name = "ascii-canvas"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -158,6 +169,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
+name = "base64ct"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b"
+
+[[package]]
name = "bindgen"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -210,6 +227,15 @@ dependencies = [
]
[[package]]
+name = "blake2"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388"
+dependencies = [
+ "digest 0.10.3",
+]
+
+[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -231,6 +257,15 @@ dependencies = [
]
[[package]]
+name = "block-buffer"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
+dependencies = [
+ "generic-array 0.14.4",
+]
+
+[[package]]
name = "block-modes"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -616,6 +651,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
+name = "crypto-common"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
+dependencies = [
+ "generic-array 0.14.4",
+ "typenum",
+]
+
+[[package]]
name = "crypto-mac"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -757,6 +802,17 @@ dependencies = [
]
[[package]]
+name = "digest"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
+dependencies = [
+ "block-buffer 0.10.2",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
name = "dirs"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1963,6 +2019,17 @@ dependencies = [
]
[[package]]
+name = "password-hash"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8"
+dependencies = [
+ "base64ct",
+ "rand_core 0.6.3",
+ "subtle",
+]
+
+[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2686,6 +2753,7 @@ version = "1.8.0"
dependencies = [
"aes",
"anyhow",
+ "argon2",
"backtrace",
"base64",
"block-modes",
diff --git a/openpgp/Cargo.toml b/openpgp/Cargo.toml
index c6f3568b..8392727e 100644
--- a/openpgp/Cargo.toml
+++ b/openpgp/Cargo.toml
@@ -41,6 +41,7 @@ memsec = { version = ">=0.5", default-features = false }
nettle = { version = "7.0.2", optional = true }
regex = "1"
regex-syntax = "0.6"
+argon2 = { version = "0.3", features = ["std"] }
sha1collisiondetection = { version = "0.2.3", default-features = false, features = ["std"] }
thiserror = "1.0.2"
xxhash-rust = { version = "0.8", features = ["xxh3"] }
diff --git a/openpgp/src/crypto/s2k.rs b/openpgp/src/crypto/s2k.rs
index c31d6fa2..f9b936bb 100644
--- a/openpgp/src/crypto/s2k.rs
+++ b/openpgp/src/crypto/s2k.rs
@@ -6,6 +6,8 @@
//!
//! [Section 3.7 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-3.7
+use std::convert::TryInto;
+
use crate::Error;
use crate::Result;
use crate::HashAlgorithm;
@@ -35,6 +37,18 @@ use quickcheck::{Arbitrary, Gen};
#[non_exhaustive]
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum S2K {
+ /// Argon2 Memory-Hard Password Hashing Function.
+ Argon2 {
+ /// The salt.
+ salt: [u8; 16],
+ /// Number of passes.
+ t: u8,
+ /// Degree of parallelism.
+ p: u8,
+ /// Exponent of memory size.
+ m: u8,
+ },
+
/// Repeatently hashes the password with a public `salt` value.
Iterated {
/// Hash used for key derivation.
@@ -167,6 +181,31 @@ impl S2K {
-> Result<SessionKey> {
#[allow(deprecated)]
match self {
+ &S2K::Argon2 { salt, t, p, m, } => {
+ let mut config = argon2::ParamsBuilder::new();
+ config.t_cost(t.into())?;
+ config.p_cost(p.into())?;
+ config.m_cost(
+ 2u32.checked_pow(m.into())
+ .ok_or_else(|| Error::InvalidArgument(
+ format!("Argon2 memory parameter out of bounds: {}",
+ m)))?)?;
+ config.output_len(
+ key_size.try_into()
+ .map_err(|_| Error::InvalidArgument(
+ format!("key size parameter out of bounds: {}",
+ key_size)))?)?;
+ let params = config.params()?;
+ let argon2 = argon2::Argon2::new(
+ argon2::Algorithm::Argon2id,
+ argon2::Version::V0x13,
+ params);
+ let mut sk: SessionKey = vec![0; key_size].into();
+ password.map(|password| {
+ argon2.hash_password_into(password, &salt, &mut sk)
+ })?;
+ Ok(sk)
+ },
&S2K::Simple { hash } | &S2K::Salted { hash, .. }
| &S2K::Iterated { hash, .. } => password.map(|string| {
let mut hash = hash.context()?;
@@ -183,6 +222,7 @@ impl S2K {
hash.update(&zeros[..]);
match self {
+ &S2K::Argon2 { .. } => unreachable!("handled above"),
&S2K::Simple { .. } => {
hash.update(string);
}
@@ -248,6 +288,7 @@ impl S2K {
Simple { .. }
| Salted { .. }
| Iterated { .. }
+ | Argon2 { .. }
=> true,
S2K::Private { .. }
| S2K::Unknown { .. }
@@ -353,6 +394,11 @@ impl fmt::Display for S2K {
salt[4], salt[5], salt[6], salt[7],
hash_bytes))
}
+ S2K::Argon2 { salt, t, p, m, } => {
+ write!(f,
+ "Argon2id with t: {}, p: {}, m: 2^{}, salt: {}",
+ t, p, m, crate::fmt::hex::encode(salt))
+ },
S2K::Private { tag, parameters } =>
if let Some(p) = parameters.as_ref() {
write!(f, "Private/Experimental S2K {}:{:?}", tag, p)
diff --git a/openpgp/src/parse.rs b/openpgp/src/parse.rs
index a7fbb085..a02ba9a3 100644
--- a/openpgp/src/parse.rs
+++ b/openpgp/src/parse.rs
@@ -1117,6 +1117,18 @@ impl S2K {
hash_bytes: S2K::decode_count(php.parse_u8("s2k_count")?),
}
},
+ 4 => S2K::Argon2 {
+ salt: {
+ let mut b = [0u8; 16];
+ let b_len = b.len();
+ b.copy_from_slice(
+ &php.parse_bytes("argon2_salt", b_len)?);
+ b
+ },
+ t: php.parse_u8("argon2_t")?,
+ p: php.parse_u8("argon2_p")?,
+ m: php.parse_u8("argon2_m")?,
+ },
100..=110 => S2K::Private {
tag: s2k,
parameters: if let Some(l) = s2k_len {
diff --git a/openpgp/src/serialize.rs b/openpgp/src/serialize.rs
index afbf1646..997b6f5c 100644
--- a/openpgp/src/serialize.rs
+++ b/openpgp/src/serialize.rs
@@ -1284,6 +1284,11 @@ impl Marshal for S2K {
w.write_all(&salt[..])?;
w.write_all(&[S2K::encode_count(hash_bytes)?])?;
}
+ S2K::Argon2 { salt, t, p, m, } => {
+ w.write_all(&[4])?;
+ w.write_all(salt)?;
+ w.write_all(&[*t, *p, *m])?;
+ },
S2K::Private { tag, parameters }
| S2K::Unknown { tag, parameters} => {
w.write_all(&[*tag])?;
@@ -1304,6 +1309,7 @@ impl MarshalInto for S2K {
&S2K::Simple{ .. } => 2,
&S2K::Salted{ .. } => 2 + 8,
&S2K::Iterated{ .. } => 2 + 8 + 1,
+ S2K::Argon2 { .. } => 20,
S2K::Private { parameters, .. }
| S2K::Unknown { parameters, .. } =>
1 + parameters.as_ref().map(|p| p.len()).unwrap_or(0),
diff --git a/sq/src/commands/dump.rs b/sq/src/commands/dump.rs
index a9aae2cc..af742cec 100644
--- a/sq/src/commands/dump.rs
+++ b/sq/src/commands/dump.rs
@@ -926,6 +926,17 @@ impl PacketDumper {
writeln!(output, "{} Salt: {}", i, hex::encode(salt))?;
writeln!(output, "{} Hash bytes: {}", i, hash_bytes)?;
},
+ Argon2 { salt, t, p, m, } => {
+ writeln!(output, "Argon2id")?;
+ writeln!(output, "{} Number of passes (t): {}", i, t)?;
+ writeln!(output, "{} Degree of parallelism (p): {}", i, p)?;
+ writeln!(output, "{} Memory size (m): {}", i,
+ 2u32.checked_pow(*m as u32)
+ .map(|b| format!("{} bytes", b))
+ .unwrap_or_else(
+ || format!("Invalid parameter: {}", m)))?;
+ writeln!(output, "{} Salt: {}", i, hex::encode(salt))?;
+ },
Private { tag, parameters } => {
writeln!(output, "Private")?;
writeln!(output, "{} Tag: {}", i, tag)?;