DKIM record management API

This commit is contained in:
mdecimus
2024-04-04 11:51:47 +02:00
parent 93a2f691ea
commit 3a5ca70365
16 changed files with 564 additions and 141 deletions

View File

@@ -248,11 +248,16 @@ impl ConfigManager {
Ok(results)
}
pub async fn set(&self, keys: impl IntoIterator<Item = ConfigKey>) -> store::Result<()> {
pub async fn set<I, T>(&self, keys: I) -> store::Result<()>
where
I: IntoIterator<Item = T>,
T: Into<ConfigKey>,
{
let mut batch = BatchBuilder::new();
let mut local_batch = Vec::new();
for key in keys {
let key = key.into();
if self.cfg_local_patterns.is_local_key(&key.key) {
local_batch.push(key);
} else {

View File

@@ -313,7 +313,7 @@ impl Scripting {
IfBlock::new::<()>(
"sieve.trusted.sign",
[],
"['rsa_' + key_get('default', 'domain'), 'ed_' + key_get('default', 'domain')]",
"['rsa-' + key_get('default', 'domain'), 'ed25519-' + key_get('default', 'domain')]",
)
}),
scripts,
@@ -349,7 +349,7 @@ impl Default for Scripting {
sign: IfBlock::new::<()>(
"sieve.trusted.sign",
[],
"['rsa_' + key_get('default', 'domain'), 'ed_' + key_get('default', 'domain')]",
"['rsa-' + key_get('default', 'domain'), 'ed25519-' + key_get('default', 'domain')]",
),
scripts: AHashMap::new(),
bayes_cache: BayesTokenCache::new(

View File

@@ -91,7 +91,7 @@ impl Default for MailAuthConfig {
"auth.dkim.sign",
[(
"is_local_domain('*', sender_domain)",
"['rsa_' + sender_domain, 'ed_' + sender_domain]",
"['rsa-' + sender_domain, 'ed25519-' + sender_domain]",
)],
"false",
),
@@ -101,7 +101,7 @@ impl Default for MailAuthConfig {
seal: IfBlock::new::<()>(
"auth.arc.seal",
[],
"['rsa_' + key_get('default', 'domain'), 'ed_' + key_get('default', 'domain')]",
"['rsa-' + key_get('default', 'domain'), 'ed25519-' + key_get('default', 'domain')]",
),
},
spf: SpfAuthConfig {
@@ -226,50 +226,8 @@ fn build_signature(config: &mut Config, id: &str) -> Option<(DkimSigner, ArcSeal
(DkimSigner::RsaSha256(signer), ArcSealer::RsaSha256(sealer)).into()
}
Algorithm::Ed25519Sha256 => {
let mut public_key = vec![];
let mut private_key = vec![];
for (key, key_bytes) in [
(("signature", id, "public-key"), &mut public_key),
(("signature", id, "private-key"), &mut private_key),
] {
let mut contents = config.value_require(key)?.as_bytes().iter().copied();
let mut base64 = vec![];
'outer: while let Some(ch) = contents.next() {
if !ch.is_ascii_whitespace() {
if ch == b'-' {
for ch in contents.by_ref() {
if ch == b'\n' {
break;
}
}
} else {
base64.push(ch);
}
for ch in contents.by_ref() {
if ch == b'-' {
break 'outer;
} else if !ch.is_ascii_whitespace() {
base64.push(ch);
}
}
}
}
*key_bytes = base64_decode(&base64)
.ok_or_else(|| {
config.new_build_error(
("signature", id),
format!("Failed to base64 decode key for {}.", key.as_key(),),
)
})
.ok()?;
}
let private_key = parse_pem(config, ("signature", id, "private-key"))?;
let key = Ed25519Key::from_pkcs8_maybe_unchecked_der(&private_key)
.or_else(|_| Ed25519Key::from_seed_and_public_key(&private_key, &public_key))
.map_err(|err| {
config.new_build_error(
("signature", id),
@@ -278,7 +236,6 @@ fn build_signature(config: &mut Config, id: &str) -> Option<(DkimSigner, ArcSeal
})
.ok()?;
let key_clone = Ed25519Key::from_pkcs8_maybe_unchecked_der(&private_key)
.or_else(|_| Ed25519Key::from_seed_and_public_key(&private_key, &public_key))
.map_err(|err| {
config.new_build_error(
("signature", id),
@@ -304,6 +261,44 @@ fn build_signature(config: &mut Config, id: &str) -> Option<(DkimSigner, ArcSeal
}
}
fn parse_pem(config: &mut Config, key: impl AsKey) -> Option<Vec<u8>> {
if let Some(der) = simple_pem_parse(config.value_require(key.clone())?) {
Some(der)
} else {
config.new_build_error(key, "Failed to base64 decode key.");
None
}
}
pub fn simple_pem_parse(contents: &str) -> Option<Vec<u8>> {
let mut contents = contents.as_bytes().iter().copied();
let mut base64 = vec![];
'outer: while let Some(ch) = contents.next() {
if !ch.is_ascii_whitespace() {
if ch == b'-' {
for ch in contents.by_ref() {
if ch == b'\n' {
break;
}
}
} else {
base64.push(ch);
}
for ch in contents.by_ref() {
if ch == b'-' {
break 'outer;
} else if !ch.is_ascii_whitespace() {
base64.push(ch);
}
}
}
}
base64_decode(&base64)
}
fn parse_signature<T: SigningKey, U: SigningKey<Hasher = Sha256>>(
config: &mut Config,
id: &str,

View File

@@ -181,7 +181,7 @@ impl Default for QueueConfig {
sign: IfBlock::new::<()>(
"report.dsn.sign",
[],
"['rsa_' + key_get('default', 'domain'), 'ed_' + key_get('default', 'domain')]",
"['rsa-' + key_get('default', 'domain'), 'ed25519-' + key_get('default', 'domain')]",
),
},
timeout: QueueOutboundTimeout {

View File

@@ -125,7 +125,7 @@ impl Report {
sign: IfBlock::new::<()>(
format!("report.{id}.sign"),
[],
"['rsa_' + key_get('default', 'domain'), 'ed_' + key_get('default', 'domain')]",
"['rsa-' + key_get('default', 'domain'), 'ed25519-' + key_get('default', 'domain')]",
),
send: IfBlock::new::<()>(format!("report.{id}.send"), [], "[1, 1d]"),
};
@@ -174,7 +174,7 @@ impl AggregateReport {
sign: IfBlock::new::<()>(
format!("report.{id}.aggregate.sign"),
[],
"['rsa_' + key_get('default', 'domain'), 'ed_' + key_get('default', 'domain')]",
"['rsa-' + key_get('default', 'domain'), 'ed25519-' + key_get('default', 'domain')]",
),
max_size: IfBlock::new::<()>(format!("report.{id}.aggregate.max-size"), [], "26214400"),
};

View File

@@ -86,7 +86,7 @@ impl CharUtils for char {
pub fn fn_cure_text<'x>(_: &'x Context<'x>, v: Vec<Variable>) -> Variable {
decancer::cure(v[0].to_string().as_ref(), decancer::Options::default())
.map(|s| s.into_str())
.map(String::from)
.unwrap_or_default()
.into()
}

View File

@@ -587,11 +587,11 @@ mod test {
// Test spec
let mut text = String::new();
for i in 0..100 {
text += &format!("Line{i} test test test\n");
text += format!("Line{i} test test test\n").as_str();
}
let mut expected = String::new();
for i in [20, 21, 22, 60, 61, 62] {
expected += &format!("Line{i}testtesttest");
expected += format!("Line{i}testtesttest").as_str();
}
assert_eq!(
String::from_utf8(pyzor_digest(Vec::new(), text.lines(), &psl)).unwrap(),