Skip to content

Commit 27d8193

Browse files
committed
OHTTP keys should be rotated
This pr addresses #445. It implements OHTTP-key rotation to payjoin-mailroom Mailroom operators can now decide the time interval for keys to be rotated. Also if a key has expired, a 422 error is returned to clients. Clients can handle they key-rotation via the cach-control header returned by the directory. set rotation grace to 7 days
1 parent 43063b0 commit 27d8193

8 files changed

Lines changed: 746 additions & 62 deletions

File tree

Cargo-minimal.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2879,6 +2879,7 @@ dependencies = [
28792879
"hex-conservative 0.1.2",
28802880
"http",
28812881
"http-body-util",
2882+
"httpdate",
28822883
"hyper",
28832884
"hyper-rustls",
28842885
"hyper-util",

Cargo-recent.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2844,6 +2844,7 @@ dependencies = [
28442844
"hex-conservative 0.1.2",
28452845
"http",
28462846
"http-body-util",
2847+
"httpdate",
28472848
"hyper",
28482849
"hyper-rustls",
28492850
"hyper-util",

payjoin-mailroom/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ futures = "0.3"
4040
hex = { package = "hex-conservative", version = "0.1.1" }
4141
http = "1.3.1"
4242
http-body-util = "0.1.3"
43+
httpdate = "1.0.3"
4344
hyper = { version = "1.6.0", features = ["http1", "server"] }
4445
hyper-rustls = { version = "0.27.7", default-features = false, features = [
4546
"webpki-roots",

payjoin-mailroom/src/config.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ pub struct Config {
1212
pub storage_dir: PathBuf,
1313
#[serde(deserialize_with = "deserialize_duration_secs")]
1414
pub timeout: Duration,
15+
#[serde(deserialize_with = "deserialize_key_epoch_duration_secs")]
16+
pub ohttp_keys_max_age: Option<Duration>,
1517
#[serde(deserialize_with = "deserialize_duration_secs")]
1618
pub mailbox_ttl: Duration,
1719
pub v1: Option<V1Config>,
@@ -87,6 +89,7 @@ impl Default for Config {
8789
listener: "[::]:8080".parse().expect("valid default listener address"),
8890
storage_dir: PathBuf::from("./data"),
8991
timeout: Duration::from_secs(30),
92+
ohttp_keys_max_age: None, //Some(Duration::from_secs(30)),
9093
mailbox_ttl: Duration::from_secs(60 * 60 * 24 * 7), // 1 week
9194
v1: None,
9295
#[cfg(feature = "telemetry")]
@@ -107,17 +110,42 @@ where
107110
Ok(Duration::from_secs(secs))
108111
}
109112

113+
fn deserialize_key_epoch_duration_secs<'de, D>(
114+
deserializer: D,
115+
) -> Result<Option<Duration>, D::Error>
116+
where
117+
D: serde::Deserializer<'de>,
118+
{
119+
let secs: Option<u64> = Option::deserialize(deserializer)?;
120+
let minimum = crate::directory::ROTATION_GRACE.saturating_mul(2);
121+
match secs {
122+
None => Ok(None),
123+
Some(s) => {
124+
let duration = Duration::from_secs(s);
125+
if duration <= minimum {
126+
return Err(<D::Error as serde::de::Error>::custom(format!(
127+
"ohttp_keys_max_age must be greater than {} seconds when set",
128+
minimum.as_secs()
129+
)));
130+
}
131+
Ok(Some(duration))
132+
}
133+
}
134+
}
135+
110136
impl Config {
111137
pub fn new(
112138
listener: ListenerAddress,
113139
storage_dir: PathBuf,
114140
timeout: Duration,
141+
ohttp_keys_max_age: Option<Duration>,
115142
v1: Option<V1Config>,
116143
) -> Self {
117144
Self {
118145
listener,
119146
storage_dir,
120147
timeout,
148+
ohttp_keys_max_age,
121149
mailbox_ttl: Duration::from_secs(60 * 60 * 24 * 7), // 1 week
122150
v1,
123151
#[cfg(feature = "telemetry")]

0 commit comments

Comments
 (0)